-
Notifications
You must be signed in to change notification settings - Fork 44
fade animations for modals #2623
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,17 +8,15 @@ | |
| width: 100%; | ||
| z-index: var(--z-index-modal); | ||
|
|
||
| /* Start hidden/offscreen and invisible */ | ||
| transform: translateY(100%); | ||
| transform: scale(0.98); | ||
| opacity: 0; | ||
| pointer-events: none; | ||
| overflow: hidden; | ||
|
|
||
|
Comment on lines
+11
to
15
|
||
| transition: | ||
| transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), | ||
| opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1), | ||
| height 0.2s cubic-bezier(0.4, 0, 0.2, 1); | ||
| will-change: transform, opacity, height; | ||
| transform 0.15s cubic-bezier(0.16, 1, 0.3, 1), | ||
| opacity 0.15s cubic-bezier(0.16, 1, 0.3, 1); | ||
| will-change: transform, opacity; | ||
|
|
||
| .View__inset { | ||
| border: 0; | ||
|
|
@@ -27,13 +25,13 @@ | |
| } | ||
|
|
||
| .SlideupModal.open { | ||
| transform: translateY(0); | ||
| transform: scale(1); | ||
| opacity: 1; | ||
| pointer-events: auto; | ||
| } | ||
|
|
||
| .SlideupModal.closed { | ||
| transform: translateY(100%); | ||
| transform: scale(0.98); | ||
| opacity: 0; | ||
| pointer-events: none; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||
| import React, { useEffect, useRef } from "react"; | ||||||||||||||||||
| import React from "react"; | ||||||||||||||||||
|
|
||||||||||||||||||
| import "./styles.scss"; | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -14,26 +14,13 @@ export const AccountHeaderModal = ({ | |||||||||||||||||
| isDropdownOpen, | ||||||||||||||||||
| icon, | ||||||||||||||||||
| className, | ||||||||||||||||||
| }: AccountHeaderModalProps) => { | ||||||||||||||||||
| const dropdownRef = useRef<HTMLDivElement>(null); | ||||||||||||||||||
|
|
||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||
| if (dropdownRef.current != null) { | ||||||||||||||||||
| dropdownRef.current.style.maxHeight = isDropdownOpen | ||||||||||||||||||
| ? `calc(100vh - 1rem)` | ||||||||||||||||||
| : "0"; | ||||||||||||||||||
| } | ||||||||||||||||||
| }, [isDropdownOpen]); | ||||||||||||||||||
|
|
||||||||||||||||||
| return ( | ||||||||||||||||||
| <> | ||||||||||||||||||
| {icon} | ||||||||||||||||||
| <div | ||||||||||||||||||
| ref={dropdownRef} | ||||||||||||||||||
| className={`AccountHeaderModal ${className || ""}`} | ||||||||||||||||||
| > | ||||||||||||||||||
| <div className="AccountHeaderModal__content">{children}</div> | ||||||||||||||||||
| </div> | ||||||||||||||||||
| </> | ||||||||||||||||||
| ); | ||||||||||||||||||
| }; | ||||||||||||||||||
| }: AccountHeaderModalProps) => ( | ||||||||||||||||||
| <> | ||||||||||||||||||
| {icon} | ||||||||||||||||||
| <div | ||||||||||||||||||
| className={`AccountHeaderModal ${isDropdownOpen ? "AccountHeaderModal--open" : ""} ${className || ""}`} | ||||||||||||||||||
| > | ||||||||||||||||||
| <div className="AccountHeaderModal__content">{children}</div> | ||||||||||||||||||
|
||||||||||||||||||
| <div className="AccountHeaderModal__content">{children}</div> | |
| <div | |
| className="AccountHeaderModal__content" | |
| aria-hidden={!isDropdownOpen} | |
| inert={!isDropdownOpen ? "" : undefined} | |
| > | |
| {children} | |
| </div> |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,21 +1,31 @@ | ||||||||||
| @use "../../../styles/utils.scss" as *; | ||||||||||
|
|
||||||||||
| .AccountHeaderModal { | ||||||||||
| overflow: hidden; | ||||||||||
| position: absolute; | ||||||||||
| left: 0; | ||||||||||
| top: 0; | ||||||||||
| z-index: calc(var(--z-index-tooltip) + 1); | ||||||||||
| transition: max-height var(--dropdown-animation); | ||||||||||
|
|
||||||||||
| // default max-height on page load | ||||||||||
| max-height: 0; | ||||||||||
| /* Fade animation */ | ||||||||||
| opacity: 0; | ||||||||||
| transform: scale(0.95); | ||||||||||
| pointer-events: none; | ||||||||||
| transition: | ||||||||||
| opacity 0.15s cubic-bezier(0.16, 1, 0.3, 1), | ||||||||||
| transform 0.15s cubic-bezier(0.16, 1, 0.3, 1); | ||||||||||
|
Comment on lines
+14
to
+15
|
||||||||||
| opacity 0.15s cubic-bezier(0.16, 1, 0.3, 1), | |
| transform 0.15s cubic-bezier(0.16, 1, 0.3, 1); | |
| opacity var(--dropdown-animation), | |
| transform var(--dropdown-animation); |
Copilot
AI
Feb 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With only opacity: 0/pointer-events: none, the dropdown remains focusable to keyboard and present to assistive tech when closed (e.g., the hidden items can still be tabbed to). Consider also toggling visibility: hidden (and/or applying inert/aria-hidden from the component) when closed so the content is removed from the tab order and accessibility tree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The close timeout is hard-coded and now shorter than the
LoadingBackgroundfade-out (transition: all var(--dropdown-animation)+transition-delay: 0.1s), so when the parent unmounts the modal after 150ms the backdrop transition is cut off mid-animation. Consider waiting fortransitionend(modal and/or backdrop) before callingsetIsModalOpen(false), or at least deriving a single shared duration (incl. delay) so the JS unmount timing can’t drift from CSS.