react-doctor/require-reduced-motion
Project ships a motion library but never gates animation on the user's reduced-motion preference — add `useReducedMotion()` / `<MotionConfig reducedMotion="user">` or a `@media (prefers-reduced-motion: reduce)` query
- Category: Accessibility
- Severity: error
- Source:
react-doctor-core - Framework: global
- Enabled when: always (project depends on a known motion library)
- Documentation: https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions
Validation prompt
Use this to decide whether a fired diagnostic is real or a false positive.
Project-level heuristic that fires ONCE (on package.json) when a dependency or devDependency is a known motion library (framer-motion / motion, react-spring, gsap, @react-spring/*, react-native-reanimated, etc.) AND a repo-wide text scan of *.ts/*.tsx/*.js/*.jsx/*.css/*.scss finds none of prefers-reduced-motion, useReducedMotion, MotionConfig, or reducedMotion. CONFIRM when the app actually animates (page/route transitions, parallax, auto-playing or looping motion, spring physics) and nothing in the codebase respects the OS "reduce motion" setting — this is a WCAG 2.3.3 failure. SUPPRESS when reduced-motion handling exists but the grep can't see it: it lives in a file type outside the scanned globs (MDX, a .styl/.less sheet, a templated or build-generated stylesheet, an inline <style> in HTML), the motion dependency is present only for non-animating utilities (easing math, a useInView observer, layout measurement) so no real animation ships, or animation is delegated to a component library/design system that already honors the preference globally. Do not flag a library listed but unused.
Fix prompt
Use this once validation confirms the diagnostic is real.
Honor the user's OS "reduce motion" setting for every non-essential animation rather than deleting motion outright (WCAG 2.3.3). For Framer Motion / motion: read const shouldReduceMotion = useReducedMotion() and drop or shorten transforms/opacity/transition when it's true, or wrap the app once in <MotionConfig reducedMotion="user"> so transform/layout animations are auto-reduced. For react-spring use the useReducedMotion hook to swap to immediate/zero-duration springs; for Reanimated check AccessibilityInfo.isReduceMotionEnabled() / useReducedMotion. For CSS-driven or other animation, add a global guard: @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }, then pare back parallax and auto-playing motion. Keep essential, information-carrying motion but make it calm. See https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions