feat(a11y): respect prefers-reduced-motion — wire MotionConfig into BladeProvider and add useReducedMotion hook#3368
Open
starboyvarun wants to merge 1 commit intorazorpay:masterfrom
Conversation
…ations Users with vestibular disorders or motion sensitivity can set "Reduce Motion" in their OS settings, which emits prefers-reduced-motion: reduce. Blade had zero support for this — every animation played at full speed regardless of this preference. Two changes: 1. BladeProvider.web.tsx: wrap children in <MotionConfig reducedMotion="user">. framer-motion reads the OS preference and sets all animation durations to 0 when reduce motion is enabled. This automatically covers every component that uses BaseMotion, Scale, Stagger, AnimateInteractions, or any framer-motion powered animation — with no changes to individual components. 2. useReducedMotion hook: a new utility hook that reads the same media query for CSS-based transitions (styled-components animations, canvas, etc.) and reacts to OS-level changes without a page reload. Exported from ~utils so Blade consumers can sync their own animations with the system preference.
🦋 Changeset detectedLatest commit: f8f222b The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
✅ PR title follows Conventional Commits specification. |
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit f8f222b:
|
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.
Problem
Blade had zero support for
prefers-reduced-motion. Users with vestibular disorders, epilepsy, or general motion sensitivity can set "Reduce Motion" in their OS — macOS, iOS, Windows, and Android all expose this setting. When set, the browser emitsprefers-reduced-motion: reduce, and applications are expected to minimize or eliminate non-essential animation.Every Blade animation — entrance/exit transitions, scale effects, stagger sequences, interactive hover animations — played at full speed regardless of this OS preference. This is a WCAG 2.1 SC 2.3.3 (Animation from Interactions) violation and affects real users.
A search across the entire codebase returned exactly one result for
prefers-reduced-motion— a JSDoc comment inBaseMotion.tsxthat claimed the component "handles reduced motion" but contained no implementation.Solution
Part 1 —
BladeProvider.web.tsxframer-motion ships a
<MotionConfig reducedMotion="user">provider that reads the OS preference and sets all animation durations/delays to0for every framer-motion component inside it. Adding this toBladeProvider— where all other system-wide providers already live — covers every component that usesBaseMotion,BaseMotionEntryExit,Scale,Stagger,AnimateInteractions, or any other framer-motion-powered animation. Zero changes to individual components required.reducedMotion="user"is dynamic — if the user toggles the OS setting while the app is running, framer-motion reacts without a page reload.Part 2 —
useReducedMotionhookNot all Blade animations use framer-motion. CSS
transitionandanimationproperties in styled-components (Toast entrance/exit, Modal backdrop fade, etc.) are not controlled byMotionConfig. Blade consumers building custom components on top of Blade also need a way to synchronize with the OS preference.The new
useReducedMotion()hook readswindow.matchMedia('(prefers-reduced-motion: reduce)')directly, subscribes to OS-level changes, and returns a stableboolean. Exported from~utilsas part of the public API.Files Changed
packages/blade/src/components/BladeProvider/BladeProvider.web.tsx— importMotionConfig, wrap childrenpackages/blade/src/utils/useReducedMotion.ts— new hook with JSDoc and OS change subscriptionpackages/blade/src/utils/index.ts— exportuseReducedMotionfrom public API.changeset/motion-honors-user.md—minorbump (new exported hook)Testing
To verify MotionConfig coverage:
prefers-reduced-motion: reduceWith the setting on, all BaseMotion, Scale, Stagger, and AnimateInteractions animations should complete instantly (no visible transition). With it off, animations play normally.
To verify useReducedMotion:
Toggle the OS setting — the value updates without a page reload.
Scope Note
CSS-based animations in styled-components (Toast entrance, certain Modal transitions) are outside the scope of
MotionConfig. Those can be addressed in a follow-up usinguseReducedMotionwithin the relevant styled-components, following the same pattern established here.