From 2aec9470cc5935b6d03f0674b4c3041e7485cabf Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 3 Feb 2026 16:22:23 +0000 Subject: [PATCH] Improve animation Signed-off-by: Charles de Dreuille --- .../ui/src/components/Toast/Toast.module.css | 20 +++++------------ packages/ui/src/components/Toast/Toast.tsx | 2 +- .../ui/src/components/Toast/ToastQueue.ts | 22 ++++++++++++++----- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/ui/src/components/Toast/Toast.module.css b/packages/ui/src/components/Toast/Toast.module.css index e6c4020848..7f3bbef42f 100644 --- a/packages/ui/src/components/Toast/Toast.module.css +++ b/packages/ui/src/components/Toast/Toast.module.css @@ -74,7 +74,7 @@ box-shadow: 0 4px 12px -2px rgba(0 0 0 / 0.4); /* Animation */ - transition: transform 6s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 6s ease; + transition: transform 3s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 3s ease; /* View Transitions - name is set dynamically via inline style, class groups them */ view-transition-class: toast; @@ -244,19 +244,12 @@ } } -/* Apply animations to view transition pseudo-elements */ -/* Debug: using wildcard to test if view transitions work at all */ -::view-transition-old(*) { - animation: bui-toast-exit 6s ease-out forwards; +/* Apply exit animation only to the closing toast */ +::view-transition-old(toast-exit) { + animation: bui-toast-exit 3s ease-out forwards; } -::view-transition-new(*) { - animation: none; -} - -/* Don't animate the root snapshot */ -::view-transition-old(root), -::view-transition-new(root) { +::view-transition-new(toast-exit) { animation: none; } @@ -266,8 +259,7 @@ transition: none; } - ::view-transition-old(*), - ::view-transition-new(*) { + ::view-transition-old(toast-exit) { animation: none !important; } } diff --git a/packages/ui/src/components/Toast/Toast.tsx b/packages/ui/src/components/Toast/Toast.tsx index 5732931d98..9b606ade32 100644 --- a/packages/ui/src/components/Toast/Toast.tsx +++ b/packages/ui/src/components/Toast/Toast.tsx @@ -143,10 +143,10 @@ export const Toast = forwardRef( style={ { '--toast-index': index, - viewTransitionName: `toast-${toast.key}`, } as React.CSSProperties } {...dataAttributes} + data-toast-key={toast.key} data-status={finalStatus} data-starting-style={isStarting ? '' : undefined} {...restProps} diff --git a/packages/ui/src/components/Toast/ToastQueue.ts b/packages/ui/src/components/Toast/ToastQueue.ts index 7f3f183ca3..87df3ae8eb 100644 --- a/packages/ui/src/components/Toast/ToastQueue.ts +++ b/packages/ui/src/components/Toast/ToastQueue.ts @@ -45,21 +45,27 @@ import type { ToastContent } from './types'; * * @public */ -// Track if we should use view transition (only for removals) -let useViewTransition = false; +// Track which toast element is being closed for view transition +let closingToastElement: HTMLElement | null = null; export const toastQueue = new RAToastQueue({ maxVisibleToasts: 5, // Wrap state updates in a CSS view transition for smooth animations wrapUpdate(fn) { - if (useViewTransition && 'startViewTransition' in document) { - useViewTransition = false; // Reset flag + if (closingToastElement && 'startViewTransition' in document) { + // Set view-transition-name on the element BEFORE capturing old state + closingToastElement.style.viewTransitionName = 'toast-exit'; + const element = closingToastElement; + closingToastElement = null; + ( document as Document & { startViewTransition: (cb: () => void) => void; } ).startViewTransition(() => { flushSync(fn); + // Clean up (element may be removed, but just in case) + element.style.viewTransitionName = ''; }); } else { fn(); @@ -67,9 +73,13 @@ export const toastQueue = new RAToastQueue({ }, }); -// Override close to enable view transition +// Override close to capture the toast element before transition const originalClose = toastQueue.close.bind(toastQueue); toastQueue.close = (key: string) => { - useViewTransition = true; + // Find the toast element by our custom data attribute + const toastElement = document.querySelector( + `[data-toast-key="${key}"]`, + ) as HTMLElement | null; + closingToastElement = toastElement; originalClose(key); };