Motion and `prefers-reduced-motion`
Applies to
a11y.reduced-motion.
Covers a11y.reduced-motion. Related: perf.animation-cost.
Why this one is a blocker
Vestibular disorders are common, and large-scale motion is not a matter of taste for the people who have one. Parallax, scroll-jacking, zooming transitions and full-screen wipes can cause nausea and dizziness that lasts hours. The operating system already carries the answer - the visitor has told their machine they want less motion - and all the site has to do is listen.
These are animation-heavy sites, which makes this section load-bearing rather than a formality.
What reduced motion means
Not "the same animation, faster." It means the content arrives in its final state. Fades and colour changes are generally fine. Movement across the screen, scale, rotation and parallax are not.
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduceMotion) {
gsap.set(elements, { clearProps: "all" }); // final state, no timeline
return;
}
@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;
}
}
The CSS blanket rule is a safety net, not the implementation. It cannot reach anything a JavaScript timeline drives, which on these sites is most of it.
Where it gets missed
Every module has to check. A project with thirty-five animated modules and thirty-two checks has three that will move, and those three are usually the biggest ones, because the biggest effects get written first and refactored last.
Check the list explicitly:
- Entrance and scroll reveals
- Pinned and scroll-driven sections
- Parallax on any layer
- Loaders and page transitions
- Auto-playing carousels
- Video that starts on its own
- Marquees and infinite tickers
- Cursor followers
- Anything driven by a physics or WebGL library
The loader and the page transition are the two most commonly forgotten, because they run before the module system has finished starting.
Beyond the media query
prefers-reduced-motion is one requirement. Two more sit alongside it in WCAG 2.1 AA:
- Anything that moves, blinks or scrolls automatically for more than five seconds needs a pause control that a keyboard can reach. Carousels and tickers.
- Motion triggered by device movement must be disableable.
And one that is not a rule but should be: if a scroll-driven reveal controls the largest element
above the fold, it is also destroying the LCP measurement. See docs/perf/measuring.md.
Testing it
macOS: System Settings, Accessibility, Display, Reduce motion. Windows: Settings, Accessibility, Visual effects, Animation effects. Chrome DevTools: Rendering panel, "Emulate CSS prefers-reduced-motion".
Turn it on, reload with a cleared cache, and scroll the whole site. Nothing should move.