Reduced Motion Preferences
Honor prefers-reduced-motion for vestibular safety — CSS media queries, JS animation guards, and what to reduce vs remove.
- accessibility
- reduced-motion
Large-scale motion, parallax, and autoplaying animations can cause dizziness or nausea (vestibular disorders). prefers-reduced-motion lets users ask the OS — and your site — to dial it back. WCAG 2.3.3 Animation from Interactions (AAA) and related guidance encourage respecting that preference.
Docs: MDN prefers-reduced-motion, WCAG 2.3.3.
CSS media query
.hero {
animation: slide-fade 800ms ease both;
}
@media (prefers-reduced-motion: reduce) {
.hero {
animation: none;
transition: none;
}
}
Nuclear option for design systems:
@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;
}
}
Use carefully — some transitions still help cognitive context. Prefer reducing large movement over deleting all feedback.
What to reduce
| Reduce / remove | Keep (usually) |
|---|---|
| Parallax scroll | Opacity fades short |
| Autoplay carousels | Instant cut to next |
| Spinners that bounce wildly | Static progress text |
| Page transition slides | Crossfade or none |
| Infinite decorative loops | Still images |
JavaScript animations
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function scrollToId(id: string) {
document.getElementById(id)?.scrollIntoView({
behavior: reduce ? 'auto' : 'smooth',
});
}
Listen for changes if users toggle OS settings while the app is open:
const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
mq.addEventListener('change', (e) => {
document.documentElement.dataset.reducedMotion = String(e.matches);
});
Framer Motion / GSAP / Lottie
Gate animation props on the media query. Provide a static final state. Don’t assume libraries default correctly — check.
Vestibular vs seizure
Reduced motion helps vestibular issues. Flashing content is a separate WCAG concern (2.3.1 Three Flashes). Don’t flash large areas rapidly regardless of reduced-motion preference.
Footguns
- Ignoring the preference for “delight.”
- Smooth scroll only — still respect reduce.
- CSS reduced but canvas/WebGL animations run full tilt.
- No non-animated way to complete a task (motion-only affordance).
Interview out-loud answer
“I listen to prefers-reduced-motion in CSS and JS, cut large movement and infinite loops, and keep essential feedback. Smooth scroll and page transitions become instant when reduce is set. Flashing is handled separately.”
Carousels and autoplay
If a carousel auto-advances, provide pause/stop controls (WCAG 2.2.2) and honor reduced motion by disabling autoplay. Prefer user-initiated advances when prefers-reduced-motion: reduce is set.
Extra practice
Write a minimal demo in a scratch file or the playground: one happy path, one failure path, and one boundary input. If you cannot exhibit a bug that the pattern prevents, you do not own the concept yet — re-read the primary docs linked below and tighten the example until the failure is obvious.
Notes from real codebases
Teams that succeed here keep the rules mechanical: lint where possible, CI for the rest, and a short human checklist for what automation cannot see. Document exceptions with an owner name and a removal date so “temporary” escapes do not become permanent architecture.
Implementation notes
Wire this into real code on the next feature, not only a demo. Prefer the smallest change that encodes the rule — a shared helper, a lint rule, or a checklist item in the PR template. Revisit after a week of production traffic: if users or tests still hit the failure mode, the documentation (and the abstraction) are not sharp enough yet.
Related on this site
- WCAG principles POUR
- Focus management
- Keyboard accessibility checklist
- Core Web Vitals
- Visual regression testing
Further reading
Related guides
- Accessible Combobox PatternBuild or evaluate comboboxes with APG keyboard behavior, aria-expanded/activedescendant, and filterable listbox pairing.
- Accessible Forms ErrorsLabel inputs, associate errors with fields, announce failures, and avoid placeholder-only forms that break accessibility.
- Accessible Menus PatternAPG menu and menubar keyboard model — arrow navigation, escape to close, aria-expanded, and when a disclosure is enough.
- Accessible Modals PatternsModal dialogs that work: focus trap, Escape, return focus, aria-modal, and why native dialog or APG patterns beat div soup.
- Accessible Names ComputationHow browsers compute accessible names from content, labels, aria-label, and labelledby — and how that powers Testing Library queries.