From 51b87b4ee4adc4846c314b0d9710a9542b8eb880 Mon Sep 17 00:00:00 2001 From: Ravi Suhag Date: Sat, 11 Jul 2026 22:58:48 +0530 Subject: [PATCH] fix: bring component motion in line with animation standards Consolidates hand-typed easing curves and durations into shared --rs-ease-*/--rs-duration-* tokens, and fixes a batch of motion issues across the library: removes the command palette's open/close animation (keyboard-driven, 100+ uses/day), rebuilds popover/menu/select/combobox entrances as interruptible origin-anchored transitions, fixes a toast transition that fought active swipe gestures, replaces `transition: all` with the interactive token in four components, syncs switch/checkbox/ radio color transitions with their movement, fixes an inverted reduced-motion override in the sidebar, moves the tabs indicator off layout properties onto `translate`, and adds missing entrance motion to checkbox/radio/empty-state. --- .../components/button/button.module.css | 2 +- .../components/checkbox/checkbox.module.css | 21 ++++++++++++++ .../raystack/components/checkbox/checkbox.tsx | 10 ++++++- .../components/combobox/combobox.module.css | 20 +++++++++++++ .../components/command/command.module.css | 17 ----------- .../components/dialog/dialog.module.css | 13 ++++++--- .../components/drawer/drawer.module.css | 4 +-- .../empty-state/empty-state.module.css | 25 +++++++++++++++++ .../components/field/field.module.css | 2 +- .../raystack/components/menu/menu.module.css | 21 ++++++++++++++ .../components/meter/meter.module.css | 4 +-- .../components/popover/popover.module.css | 23 ++++++++------- .../preview-card/preview-card.module.css | 4 +-- .../components/progress/progress.module.css | 4 +-- .../components/radio/radio.module.css | 21 ++++++++++++++ packages/raystack/components/radio/radio.tsx | 2 +- .../components/select/select.module.css | 23 +++++++++++++-- .../components/sidebar/sidebar.module.css | 10 +++++-- .../components/skeleton/skeleton.module.css | 2 +- .../components/switch/switch.module.css | 1 + .../raystack/components/tabs/tabs.module.css | 12 ++++---- .../components/text-area/text-area.module.css | 2 +- .../components/toast/toast.module.css | 13 ++++++--- .../components/tooltip/tooltip.module.css | 3 +- .../components/tour-beta/tour.module.css | 28 +++++++++++-------- .../raystack/components/tour/tour.module.css | 20 ++++++++----- packages/raystack/styles/effects.css | 11 ++++++++ packages/raystack/styles/typography.css | 10 ------- 28 files changed, 239 insertions(+), 89 deletions(-) diff --git a/packages/raystack/components/button/button.module.css b/packages/raystack/components/button/button.module.css index 1364db399..65ff5385e 100644 --- a/packages/raystack/components/button/button.module.css +++ b/packages/raystack/components/button/button.module.css @@ -210,7 +210,7 @@ @media (prefers-reduced-motion: no-preference) { .button { - transition: all 0.2s ease; + transition: var(--rs-transition-interactive); } } diff --git a/packages/raystack/components/checkbox/checkbox.module.css b/packages/raystack/components/checkbox/checkbox.module.css index 1ccc55a4f..f93a4a4cd 100644 --- a/packages/raystack/components/checkbox/checkbox.module.css +++ b/packages/raystack/components/checkbox/checkbox.module.css @@ -20,6 +20,7 @@ border: 1px solid var(--rs-color-border-base-secondary); cursor: pointer; flex-shrink: 0; + transition: background-color 150ms ease, border-color 150ms ease; } /* Size variants */ @@ -107,6 +108,26 @@ width: 100%; height: 100%; color: var(--rs-color-foreground-accent-emphasis); + transition: opacity var(--rs-duration-fast) var(--rs-ease-out); +} + +.indicator[data-starting-style], +.indicator[data-ending-style], +.indicator[data-unchecked] { + opacity: 0; +} + +@media (prefers-reduced-motion: no-preference) { + .indicator { + transition: + opacity var(--rs-duration-fast) var(--rs-ease-out), + transform var(--rs-duration-fast) var(--rs-ease-out); + } + + .indicator[data-starting-style], + .indicator[data-ending-style] { + transform: scale(0.8); + } } .icon { diff --git a/packages/raystack/components/checkbox/checkbox.tsx b/packages/raystack/components/checkbox/checkbox.tsx index d3b9340fe..7063b7d53 100644 --- a/packages/raystack/components/checkbox/checkbox.tsx +++ b/packages/raystack/components/checkbox/checkbox.tsx @@ -103,11 +103,19 @@ function CheckboxItem({ > ( - {state.indeterminate ? : } + {(state.checked || + state.indeterminate || + state.transitionStatus === 'ending') && + (state.indeterminate ? ( + + ) : ( + + ))} )) } diff --git a/packages/raystack/components/combobox/combobox.module.css b/packages/raystack/components/combobox/combobox.module.css index 07693070c..69d7374ed 100644 --- a/packages/raystack/components/combobox/combobox.module.css +++ b/packages/raystack/components/combobox/combobox.module.css @@ -15,6 +15,26 @@ max-height: 320px; min-width: var(--anchor-width); overflow: auto; + transform-origin: var(--transform-origin); + transition: opacity var(--rs-duration-fast) var(--rs-ease-out); +} + +.content[data-starting-style], +.content[data-ending-style] { + opacity: 0; +} + +@media (prefers-reduced-motion: no-preference) { + .content { + transition: + opacity var(--rs-duration-fast) var(--rs-ease-out), + transform var(--rs-duration-fast) var(--rs-ease-out); + } + + .content[data-starting-style], + .content[data-ending-style] { + transform: scale(0.97); + } } .list { diff --git a/packages/raystack/components/command/command.module.css b/packages/raystack/components/command/command.module.css index 2a61d6cc4..e1e3285b1 100644 --- a/packages/raystack/components/command/command.module.css +++ b/packages/raystack/components/command/command.module.css @@ -162,20 +162,12 @@ inset: 0; z-index: var(--rs-z-index-portal); background-color: var(--rs-color-overlay-black-a5); - transition: - opacity 200ms ease-out, - backdrop-filter 200ms ease-out; } .backdropBlur { backdrop-filter: var(--rs-blur-lg); } -.backdrop[data-starting-style], -.backdrop[data-ending-style] { - opacity: 0; -} - .viewport { position: fixed; inset: 0; @@ -202,13 +194,4 @@ overflow: hidden; outline: none; isolation: isolate; - transition: - opacity 200ms ease-out, - transform 200ms ease-out; -} - -.dialogPopup[data-starting-style], -.dialogPopup[data-ending-style] { - opacity: 0; - transform: scale(0.98); } diff --git a/packages/raystack/components/dialog/dialog.module.css b/packages/raystack/components/dialog/dialog.module.css index eb1562737..085c2eba8 100644 --- a/packages/raystack/components/dialog/dialog.module.css +++ b/packages/raystack/components/dialog/dialog.module.css @@ -2,7 +2,7 @@ background-color: var(--rs-color-overlay-black-a5); position: fixed; inset: 0; - transition: opacity 150ms cubic-bezier(0.45, 1.005, 0, 1.005); + transition: opacity var(--rs-duration-normal) var(--rs-ease-out); z-index: var(--rs-z-index-portal); } @@ -24,7 +24,7 @@ position: fixed; top: 0; left: 50%; - transition: opacity 150ms; + transition: opacity var(--rs-duration-normal) var(--rs-ease-out); /* translateY is the smaller of 160px (desired top offset) and calc(50vh - 50%) (the y-coord that vertically centers the dialog, where the inner 50% resolves to the element's own height). On tall @@ -50,6 +50,11 @@ background-color: var(--rs-color-overlay-black-a1); } +.dialogContent[data-starting-style], +.dialogContent[data-ending-style] { + opacity: 0; +} + .dialogContent.showNestedAnimation[data-starting-style], .dialogContent.showNestedAnimation[data-ending-style] { opacity: 0; @@ -78,8 +83,8 @@ @media (prefers-reduced-motion: no-preference) { .dialogContent { transition: - opacity 150ms, - transform 150ms ease; + opacity var(--rs-duration-normal) var(--rs-ease-out), + transform var(--rs-duration-normal) var(--rs-ease-out); } } diff --git a/packages/raystack/components/drawer/drawer.module.css b/packages/raystack/components/drawer/drawer.module.css index ccf1ed7a0..ba436ea52 100644 --- a/packages/raystack/components/drawer/drawer.module.css +++ b/packages/raystack/components/drawer/drawer.module.css @@ -5,7 +5,7 @@ z-index: var(--rs-z-index-portal); background-color: var(--rs-color-overlay-black-a5); opacity: calc(1 - var(--drawer-swipe-progress, 0)); - transition: opacity 450ms cubic-bezier(0.32, 0.72, 0, 1); + transition: opacity var(--rs-duration-drawer) var(--rs-ease-drawer); } .backdrop[data-starting-style], @@ -202,6 +202,6 @@ .drawerPopup-left, .drawerPopup-top, .drawerPopup-bottom { - transition: transform 450ms cubic-bezier(0.32, 0.72, 0, 1); + transition: transform var(--rs-duration-drawer) var(--rs-ease-drawer); } } diff --git a/packages/raystack/components/empty-state/empty-state.module.css b/packages/raystack/components/empty-state/empty-state.module.css index 6f2c89130..6a898b566 100644 --- a/packages/raystack/components/empty-state/empty-state.module.css +++ b/packages/raystack/components/empty-state/empty-state.module.css @@ -97,3 +97,28 @@ height: 100%; padding: var(--rs-space-9) var(--rs-space-5); } + +@keyframes empty-state-enter { + from { + opacity: 0; + } +} + +@keyframes empty-state-enter-rise { + from { + opacity: 0; + translate: 0 var(--rs-space-2); + } +} + +.emptyState, +.emptyStateContent { + animation: empty-state-enter var(--rs-duration-moderate) var(--rs-ease-out); +} + +@media (prefers-reduced-motion: no-preference) { + .emptyState, + .emptyStateContent { + animation-name: empty-state-enter-rise; + } +} diff --git a/packages/raystack/components/field/field.module.css b/packages/raystack/components/field/field.module.css index 567c18446..39fee8da2 100644 --- a/packages/raystack/components/field/field.module.css +++ b/packages/raystack/components/field/field.module.css @@ -40,7 +40,7 @@ letter-spacing: var(--rs-letter-spacing-small); outline: none; box-sizing: border-box; - transition: all 0.2s ease; + transition: var(--rs-transition-interactive); height: var(--rs-space-9); } diff --git a/packages/raystack/components/menu/menu.module.css b/packages/raystack/components/menu/menu.module.css index 26c2f191b..4b0b3efd4 100644 --- a/packages/raystack/components/menu/menu.module.css +++ b/packages/raystack/components/menu/menu.module.css @@ -17,7 +17,28 @@ border: 0.5px solid var(--rs-color-border-base-primary); color: var(--rs-color-foreground-base-primary); min-width: var(--anchor-width); + transform-origin: var(--transform-origin); + transition: opacity var(--rs-duration-fast) var(--rs-ease-out); } + +.content[data-starting-style], +.content[data-ending-style] { + opacity: 0; +} + +@media (prefers-reduced-motion: no-preference) { + .content { + transition: + opacity var(--rs-duration-fast) var(--rs-ease-out), + transform var(--rs-duration-fast) var(--rs-ease-out); + } + + .content[data-starting-style], + .content[data-ending-style] { + transform: scale(0.97); + } +} + .content:focus, .content:focus-visible { outline: none; diff --git a/packages/raystack/components/meter/meter.module.css b/packages/raystack/components/meter/meter.module.css index 15d9be392..6e509c1c6 100644 --- a/packages/raystack/components/meter/meter.module.css +++ b/packages/raystack/components/meter/meter.module.css @@ -86,11 +86,11 @@ @media (prefers-reduced-motion: no-preference) { .indicator { - transition: width 500ms; + transition: width var(--rs-duration-moderate) linear; } .circularIndicatorCircle { - transition: stroke-dashoffset 500ms; + transition: stroke-dashoffset var(--rs-duration-moderate) linear; } } diff --git a/packages/raystack/components/popover/popover.module.css b/packages/raystack/components/popover/popover.module.css index 85880bd4a..89d75e9f8 100644 --- a/packages/raystack/components/popover/popover.module.css +++ b/packages/raystack/components/popover/popover.module.css @@ -16,21 +16,24 @@ box-shadow: var(--rs-shadow-soft); border: 1px solid var(--rs-color-border-base-primary); color: var(--rs-color-foreground-base-primary); + transform-origin: var(--transform-origin); + transition: opacity var(--rs-duration-normal) var(--rs-ease-out); } -@keyframes slideUpAndFade { - from { - opacity: 0; - transform: translateY(var(--rs-space-1)); - } - to { - opacity: 1; - transform: translateY(0); - } +.popover[data-starting-style], +.popover[data-ending-style] { + opacity: 0; } @media (prefers-reduced-motion: no-preference) { .popover { - animation: slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1); + transition: + opacity var(--rs-duration-normal) var(--rs-ease-out), + transform var(--rs-duration-normal) var(--rs-ease-out); + } + + .popover[data-starting-style], + .popover[data-ending-style] { + transform: scale(0.97); } } diff --git a/packages/raystack/components/preview-card/preview-card.module.css b/packages/raystack/components/preview-card/preview-card.module.css index 9dd1760d7..4c74b3755 100644 --- a/packages/raystack/components/preview-card/preview-card.module.css +++ b/packages/raystack/components/preview-card/preview-card.module.css @@ -1,6 +1,6 @@ .positioner { - --easing: cubic-bezier(0.22, 1, 0.36, 1); - --animation-duration: 0.35s; + --easing: var(--rs-ease-out); + --animation-duration: var(--rs-duration-moderate); z-index: var(--rs-z-index-portal); height: var(--positioner-height); diff --git a/packages/raystack/components/progress/progress.module.css b/packages/raystack/components/progress/progress.module.css index 622a2f03a..868daefb4 100644 --- a/packages/raystack/components/progress/progress.module.css +++ b/packages/raystack/components/progress/progress.module.css @@ -86,11 +86,11 @@ @media (prefers-reduced-motion: no-preference) { .indicator { - transition: width 500ms; + transition: width var(--rs-duration-moderate) linear; } .circularIndicatorCircle { - transition: stroke-dashoffset 500ms; + transition: stroke-dashoffset var(--rs-duration-moderate) linear; } } diff --git a/packages/raystack/components/radio/radio.module.css b/packages/raystack/components/radio/radio.module.css index c509526d3..53562f1c5 100644 --- a/packages/raystack/components/radio/radio.module.css +++ b/packages/raystack/components/radio/radio.module.css @@ -20,6 +20,7 @@ justify-content: center; cursor: pointer; box-sizing: border-box; + transition: background-color 150ms ease, border-color 150ms ease; } /* Group-level size — low specificity via :where() so item-level size wins */ @@ -74,6 +75,26 @@ width: 100%; height: 100%; position: relative; + transition: opacity var(--rs-duration-fast) var(--rs-ease-out); +} + +.indicator[data-starting-style], +.indicator[data-ending-style], +.indicator[data-unchecked] { + opacity: 0; +} + +@media (prefers-reduced-motion: no-preference) { + .indicator { + transition: + opacity var(--rs-duration-fast) var(--rs-ease-out), + transform var(--rs-duration-fast) var(--rs-ease-out); + } + + .indicator[data-starting-style], + .indicator[data-ending-style] { + transform: scale(0.8); + } } .indicator::after { diff --git a/packages/raystack/components/radio/radio.tsx b/packages/raystack/components/radio/radio.tsx index 87b4e0454..386c2a05d 100644 --- a/packages/raystack/components/radio/radio.tsx +++ b/packages/raystack/components/radio/radio.tsx @@ -69,7 +69,7 @@ function RadioItem({ className, size, ...props }: RadioItemProps) { {...props} className={radioVariants({ size, className })} > - + ); } diff --git a/packages/raystack/components/select/select.module.css b/packages/raystack/components/select/select.module.css index 5f68685e1..8fb9297cd 100644 --- a/packages/raystack/components/select/select.module.css +++ b/packages/raystack/components/select/select.module.css @@ -14,12 +14,31 @@ box-shadow: var(--rs-shadow-soft); border: 0.5px solid var(--rs-color-border-base-primary); min-width: var(--anchor-width); - transition: var(--rs-transition-interactive); + transform-origin: var(--transform-origin); + transition: opacity var(--rs-duration-fast) var(--rs-ease-out); max-height: 320px; position: relative; overflow: auto; } +.content[data-starting-style], +.content[data-ending-style] { + opacity: 0; +} + +@media (prefers-reduced-motion: no-preference) { + .content { + transition: + opacity var(--rs-duration-fast) var(--rs-ease-out), + transform var(--rs-duration-fast) var(--rs-ease-out); + } + + .content[data-starting-style], + .content[data-ending-style] { + transform: scale(0.97); + } +} + .menuitem { display: flex; align-items: center; @@ -75,7 +94,7 @@ @media (prefers-reduced-motion: no-preference) { .trigger { - transition: all 0.2s ease; + transition: var(--rs-transition-interactive); } } diff --git a/packages/raystack/components/sidebar/sidebar.module.css b/packages/raystack/components/sidebar/sidebar.module.css index b82b03cec..676cc412c 100644 --- a/packages/raystack/components/sidebar/sidebar.module.css +++ b/packages/raystack/components/sidebar/sidebar.module.css @@ -340,7 +340,6 @@ .nav-group-panel { height: var(--accordion-panel-height); overflow: hidden; - transition: height 0.2s ease; } .nav-group-panel[data-starting-style], @@ -384,8 +383,13 @@ .root, .nav-item, .nav-text, - .resizeHandle, - .nav-group-panel { + .resizeHandle { transition: width 0.2s ease; } + + .nav-group-panel { + transition: + width 0.2s ease, + height 0.2s ease; + } } diff --git a/packages/raystack/components/skeleton/skeleton.module.css b/packages/raystack/components/skeleton/skeleton.module.css index 9467e91b0..2b4fd3458 100644 --- a/packages/raystack/components/skeleton/skeleton.module.css +++ b/packages/raystack/components/skeleton/skeleton.module.css @@ -37,6 +37,6 @@ @media (prefers-reduced-motion: no-preference) { .animate::after { - animation: shimmer var(--skeleton-duration) infinite; + animation: shimmer var(--skeleton-duration) linear infinite; } } diff --git a/packages/raystack/components/switch/switch.module.css b/packages/raystack/components/switch/switch.module.css index edde474bf..5a7e3853d 100644 --- a/packages/raystack/components/switch/switch.module.css +++ b/packages/raystack/components/switch/switch.module.css @@ -9,6 +9,7 @@ cursor: pointer; background: var(--rs-color-background-neutral-secondary); border-radius: var(--rs-radius-full); + transition: background-color 200ms ease; } .switch.large { diff --git a/packages/raystack/components/tabs/tabs.module.css b/packages/raystack/components/tabs/tabs.module.css index b8d447a1f..ec0b76d9a 100644 --- a/packages/raystack/components/tabs/tabs.module.css +++ b/packages/raystack/components/tabs/tabs.module.css @@ -94,8 +94,9 @@ background-color: var(--rs-color-background-base-primary); border-radius: var(--rs-radius-2); box-shadow: var(--rs-shadow-feather); - top: var(--active-tab-top, 0); - left: var(--active-tab-left, 0); + top: 0; + left: 0; + translate: var(--active-tab-left, 0) var(--active-tab-top, 0); width: var(--active-tab-width, 0); height: var(--active-tab-height, 0); z-index: 0; @@ -104,10 +105,9 @@ @media (prefers-reduced-motion: no-preference) { .indicator { transition: - top 0.25s cubic-bezier(0.4, 0, 0.2, 1), - left 0.25s cubic-bezier(0.4, 0, 0.2, 1), - width 0.25s cubic-bezier(0.4, 0, 0.2, 1), - height 0.25s cubic-bezier(0.4, 0, 0.2, 1); + translate var(--rs-duration-moderate) var(--rs-ease-in-out), + width var(--rs-duration-moderate) var(--rs-ease-in-out), + height var(--rs-duration-moderate) var(--rs-ease-in-out); } } diff --git a/packages/raystack/components/text-area/text-area.module.css b/packages/raystack/components/text-area/text-area.module.css index 20c9523d7..f7bde8a0c 100644 --- a/packages/raystack/components/text-area/text-area.module.css +++ b/packages/raystack/components/text-area/text-area.module.css @@ -16,7 +16,7 @@ @media (prefers-reduced-motion: no-preference) { .textarea { - transition: all 0.2s ease; + transition: var(--rs-transition-interactive); } } diff --git a/packages/raystack/components/toast/toast.module.css b/packages/raystack/components/toast/toast.module.css index c41c10948..7183796d7 100644 --- a/packages/raystack/components/toast/toast.module.css +++ b/packages/raystack/components/toast/toast.module.css @@ -66,7 +66,7 @@ cursor: default; z-index: calc(1000 - var(--toast-index)); - transition: opacity 500ms; + transition: opacity var(--rs-duration-slow) var(--rs-ease-out); } /* ===== Vertical position: bottom ===== */ @@ -294,8 +294,13 @@ @media (prefers-reduced-motion: no-preference) { .root { transition: - transform 500ms cubic-bezier(0.22, 1, 0.36, 1), - opacity 500ms, - height 150ms; + transform var(--rs-duration-slow) var(--rs-ease-out), + opacity var(--rs-duration-slow) var(--rs-ease-out), + height var(--rs-duration-fast) var(--rs-ease-out); } } + +/* Track the finger 1:1 while dragging; transitions resume on release. */ +.root[data-swiping] { + transition: none; +} diff --git a/packages/raystack/components/tooltip/tooltip.module.css b/packages/raystack/components/tooltip/tooltip.module.css index 545d1a8ee..d7cac3666 100644 --- a/packages/raystack/components/tooltip/tooltip.module.css +++ b/packages/raystack/components/tooltip/tooltip.module.css @@ -123,7 +123,8 @@ @media (prefers-reduced-motion: no-preference) { .content { - animation-duration: 400ms; + animation-duration: var(--rs-duration-fast); + animation-timing-function: var(--rs-ease-out); } .content[data-open][data-side="top"]:not([data-instant]) { diff --git a/packages/raystack/components/tour-beta/tour.module.css b/packages/raystack/components/tour-beta/tour.module.css index aae323601..7d16946b2 100644 --- a/packages/raystack/components/tour-beta/tour.module.css +++ b/packages/raystack/components/tour-beta/tour.module.css @@ -10,7 +10,7 @@ @media (prefers-reduced-motion: no-preference) { .overlay { - animation: tour-fade-in 250ms ease; + animation: tour-fade-in var(--rs-duration-moderate) var(--rs-ease-out); } } @@ -31,11 +31,11 @@ @media (prefers-reduced-motion: no-preference) { .spotlight { transition: - left 300ms cubic-bezier(0.22, 1, 0.36, 1), - top 300ms cubic-bezier(0.22, 1, 0.36, 1), - width 300ms cubic-bezier(0.22, 1, 0.36, 1), - height 300ms cubic-bezier(0.22, 1, 0.36, 1), - border-radius 300ms cubic-bezier(0.22, 1, 0.36, 1); + left 300ms var(--rs-ease-out), + top 300ms var(--rs-ease-out), + width 300ms var(--rs-ease-out), + height 300ms var(--rs-ease-out), + border-radius 300ms var(--rs-ease-out); } } @@ -52,7 +52,7 @@ otherwise the first open would animate in from the viewport origin. */ @media (prefers-reduced-motion: no-preference) { .positioner:has(.popup:not([data-starting-style])) { - transition: transform 350ms cubic-bezier(0.22, 1, 0.36, 1); + transition: transform 350ms var(--rs-ease-out); } } @@ -72,9 +72,7 @@ line-height: var(--rs-line-height-small); letter-spacing: var(--rs-letter-spacing-small); transform-origin: var(--transform-origin); - transition: - opacity 200ms ease, - scale 200ms ease; + transition: opacity var(--rs-duration-normal) var(--rs-ease-out); } .popup[data-starting-style], @@ -83,6 +81,14 @@ scale: 0.96; } +@media (prefers-reduced-motion: no-preference) { + .popup { + transition: + opacity var(--rs-duration-normal) var(--rs-ease-out), + scale var(--rs-duration-normal) var(--rs-ease-out); + } +} + .stepContent { display: flex; flex-direction: column; @@ -91,7 +97,7 @@ @media (prefers-reduced-motion: no-preference) { .stepContent { - animation: tour-step-in 250ms cubic-bezier(0.22, 1, 0.36, 1); + animation: tour-step-in var(--rs-duration-moderate) var(--rs-ease-out); } } diff --git a/packages/raystack/components/tour/tour.module.css b/packages/raystack/components/tour/tour.module.css index ffef1540e..6823972ff 100644 --- a/packages/raystack/components/tour/tour.module.css +++ b/packages/raystack/components/tour/tour.module.css @@ -23,7 +23,7 @@ @media (prefers-reduced-motion: no-preference) { .overlay { - transition: opacity 200ms ease; + transition: opacity var(--rs-duration-normal) var(--rs-ease-out); } } @@ -54,7 +54,7 @@ @media (prefers-reduced-motion: no-preference) { .spotlightCover { - transition: opacity 160ms ease; + transition: opacity var(--rs-duration-fast) var(--rs-ease-out); } } @@ -72,7 +72,7 @@ origin. `fade` mode repositions the card while it is hidden, so no glide. */ @media (prefers-reduced-motion: no-preference) { .positioner[data-transition="move"]:has(.popup:not([data-starting-style])) { - transition: transform 460ms cubic-bezier(0.33, 0.7, 0, 1); + transition: transform 350ms var(--rs-ease-out); } } @@ -92,9 +92,7 @@ line-height: var(--rs-line-height-small); letter-spacing: var(--rs-letter-spacing-small); transform-origin: var(--transform-origin); - transition: - opacity 200ms ease, - scale 200ms ease; + transition: opacity var(--rs-duration-normal) var(--rs-ease-out); } .popup[data-starting-style], @@ -103,6 +101,14 @@ scale: 0.96; } +@media (prefers-reduced-motion: no-preference) { + .popup { + transition: + opacity var(--rs-duration-normal) var(--rs-ease-out), + scale var(--rs-duration-normal) var(--rs-ease-out); + } +} + /* Fade mode: the card is hidden until its target settles. Hiding is instant (transition: none) so the reposition and content swap between steps are not seen; revealing uses the .popup transition above for a soft fade-in in place. */ @@ -120,7 +126,7 @@ @media (prefers-reduced-motion: no-preference) { .stepContent { - animation: tour-step-in 250ms cubic-bezier(0.22, 1, 0.36, 1); + animation: tour-step-in var(--rs-duration-moderate) var(--rs-ease-out); } } diff --git a/packages/raystack/styles/effects.css b/packages/raystack/styles/effects.css index 2ebecd2ab..648f233dc 100644 --- a/packages/raystack/styles/effects.css +++ b/packages/raystack/styles/effects.css @@ -10,6 +10,17 @@ /* Transitions */ --rs-transition-interactive: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease; + /* Motion */ + /* Example usage: transition: opacity var(--rs-duration-normal) var(--rs-ease-out); */ + --rs-ease-out: cubic-bezier(0.22, 1, 0.36, 1); /* entrances, exits, value changes */ + --rs-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* elements moving on screen */ + --rs-ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer slide */ + --rs-duration-fast: 150ms; /* tooltips, menus, small popups */ + --rs-duration-normal: 200ms; /* popovers, dialogs, switches */ + --rs-duration-moderate: 250ms; /* tabs indicator, preview card, tour steps */ + --rs-duration-slow: 400ms; /* toasts */ + --rs-duration-drawer: 450ms; /* drawer */ + /* Blurs */ /* Example usage: backdrop-filter:: var(--rs-blur-sm); */ --rs-blur-sm: blur(0.5px); diff --git a/packages/raystack/styles/typography.css b/packages/raystack/styles/typography.css index bc329377d..3d9820895 100644 --- a/packages/raystack/styles/typography.css +++ b/packages/raystack/styles/typography.css @@ -130,13 +130,3 @@ textarea { caret-color: var(--rs-color-border-accent-emphasis); } -/* Keyframe Animation */ -@keyframes dissolve-hide { - 0%, - 50% { - opacity: 1; - } - 100% { - opacity: 0; - } -}