design: add motion-reduced alternatives across animated components - #687
Merged
Conversation
Add `prefers-reduced-motion` support to booking-progress, tooltip, and toast-container components so that animations are disabled for users who have requested reduced motion, improving WCAG 2.1 AA compliance. Closes Chronopay-Org#641 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
|
@Mhidesav Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
Closes #641
Adds
prefers-reduced-motionsupport to the booking progress, tooltip, and toast container components. Users who have enabled reduced motion in their OS settings will now see instantaneous transitions instead of animated ones, while preserving all functional feedback (color changes, state updates, information visibility).Root cause
The booking progress bars, step navigation items, navigation buttons, tooltip surfaces, and the toast "Clear all" animation all used CSS transitions or framer-motion animations without checking
prefers-reduced-motion. This violates WCAG 2.1 AA success criterion 2.3.3 (Animation from Interactions) and can cause discomfort or vestibular distress for sensitive users.The codebase already had established patterns for handling reduced motion (see
autosave-indicator.tsx,WalletConnectModal.tsx,globals.cssskeleton utilities), but the components in scope had not been audited.The fix and why
Approach
Applied Tailwind's
motion-reduce:transition-noneutility (which maps to@media (prefers-reduced-motion: reduce) { transition: none !important }) to every CSS transition in the affected components. For the framer-motion "Clear all" animation inToastContainer, used the existinguseReducedMotion()hook from framer-motion to conditionally disable the transition.This approach was chosen because:
motion-reduce:transition-noneis already used inhelp-popover.tsx,keep-original-price-chip.tsx, and other componentsWhy not alternatives
useReducedMotion()everywhere--motion-duration-*tokens with overridesglobals.cssalready defines--motion-duration-*tokens, but they aren't used by Tailwind utility classes directly. Would require a larger refactor.@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }in globals.cssLazyMotionconfigChanges
src/components/dashboard/booking-progress.tsxBookingProgress): Addedtransition-[width] duration-500 ease-out motion-reduce:transition-noneso bars animate smoothly when width changes, but snap instantly for reduced-motion users.BookingFlowShell): Addedmotion-reduce:transition-nonealongside existingtransition-colorsclass.motion-reduce:transition-nonealongside existingtransitionclass.src/app/components/ui/tooltip.tsxmotion-reduce:transition-nonetotransition-opacity duration-150on both standard and longform variants.motion-reduce:transition-nonetotransition-colors.src/app/components/ui/toast-container.tsxuseReducedMotionfrom framer-motion.motion.div: Settransition={reducedMotion ? false : { duration: 0.15 }}to disable the fade/slide animation when reduced motion is preferred.How it was tested
Unit tests — 5 new tests added:
booking-progress.test.tsx: Progress bar fills containtransition-[width]andmotion-reduce:transition-noneclasses (2 assertions)booking-progress.test.tsx: Step list items containmotion-reduce:transition-nonealongsidetransition-colorsbooking-progress.test.tsx: Navigation buttons containmotion-reduce:transition-nonetooltip.test.tsx: Tooltip surface containsmotion-reduce:transition-nonetooltip.test.tsx: Trigger button containsmotion-reduce:transition-noneTest suite verification:
settings/page.tsxandslot-list.tsxare unrelated)WalletConnectModal.test.tsxfailures confirmed unrelated (fails onmaintoo)Manual verification: The
motion-reduce:transition-noneTailwind class compiles to@media (prefers-reduced-motion: reduce) { transition-property: none; }which is the standard CSS mechanism for respecting this user preference.Follow-up worth filing separately
transition-*classes withoutmotion-reduce:guards (e.g.,filter-sidebar.tsx,search-typeahead.tsx,calendar-view-toggle.tsx). The issue mentions toasts and tooltips but there are ~50+ components with unguarded transitions.prefers-reduced-motiontokens documentation: Add a section todocs/design-tokens.mddocumenting themotion-reduce:transition-noneandmotion-reduce:animate-noneconventions as the standard pattern for new components.prefers-reduced-motion: reducedisables animations in rendered snapshots.globals.cssglobal fallback: Consider adding a low-specificity global rule inglobals.css(like the existing skeleton pattern) that disables all transitions/animations forprefers-reduced-motion: reduceas a safety net, while keeping the scoped overrides for fine-grained control.