A full-bleed parallax hero or a card that zooms 40% on scroll can look stunning in a portfolio and still make a real person feel physically sick. For people with vestibular disorders, large or unexpected motion can trigger dizziness, nausea, headaches, and disorientation that lingers long after they leave your page. The fix is not to strip all motion out of the web. It is to listen to a signal the browser already hands you: prefers-reduced-motion.
This guide is for designers and developers who want motion that delights most users without harming the ones it can hurt. We will cover what the prefers-reduced-motion media query actually reports, which animations matter most to reduce, how to implement a respectful fallback in CSS and JavaScript, and how all of this maps to WCAG 2.3.3 Animation from Interactions and 2.2.2 Pause, Stop, Hide.
Why motion is an accessibility problem, not just a taste problem
Vestibular disorders affect the inner ear and the brain's balance system. When the visual field moves in a way the body did not initiate — a background sliding under fixed text, an element rushing in from off-screen, a looping zoom — the mismatch between what the eyes report and what the inner ear feels can provoke the same response as motion sickness. Conditions like vestibular migraine, Meniere's disease, and persistent postural-perceptual dizziness affect a meaningful slice of any audience, and they are largely invisible.
Motion sensitivity is not limited to the vestibular system. People with ADHD or cognitive disabilities can be pulled off-task by anything that moves in their peripheral vision, and a constantly animating carousel or auto-playing video competes for attention they cannot spare. Treating motion as an accessibility concern — alongside color contrast and keyboard operation — means designing the calm version as a first-class experience, not an apology.
What prefers-reduced-motion actually tells you
prefers-reduced-motion is a CSS media feature backed by an operating-system setting. On macOS it is System Settings > Accessibility > Display > Reduce motion. On Windows it is Settings > Accessibility > Visual effects > Animation effects. iOS and Android expose equivalent toggles. When the user enables that setting, the browser reports prefers-reduced-motion: reduce; otherwise it reports no-preference.
Two things matter here. First, this is a genuine user request, not a guess — someone deliberately turned it on, so honor it without second-guessing. Second, reduce does not mean "no animation ever." It means avoid large, distracting, non-essential motion. A subtle 150ms opacity fade or a focus outline that eases in is usually fine. A 600px slide, an infinite spin, or a parallax layer is not.
Which animations to reduce, and which to keep
The deciding factors are size, distance, and whether the motion is essential to understanding. Use this as a triage list when a reduced-motion preference is set:
- Reduce or remove: parallax scrolling, large scale/zoom transforms, full-screen transitions that fly content across the viewport, spinning or bouncing loops, auto-playing background video, and 3D rotation. These are the highest-risk patterns for vestibular users.
- Usually keep, but soften: color changes, opacity fades, and small movements under roughly 5 pixels. These rarely trigger symptoms and often carry meaning, like a button confirming a press.
- Replace, do not just delete: if an animation communicates state — a notification appearing, a step advancing — swap the movement for an instant change or a cross-fade so the information still lands. Motion that is essential to the activity is explicitly exempt under WCAG, but most decorative motion is not essential.
Implementing it in CSS and JavaScript
The most maintainable approach is to animate by default and opt out inside a single media query, rather than scattering reduced-motion logic everywhere. A common, robust pattern neutralizes runaway animations globally while leaving you free to re-enable specific safe ones:
@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; } }
For JavaScript-driven animation — a GSAP timeline, a scroll library, an auto-advancing slider — the media query alone will not stop your code, so check the preference at runtime: const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;. Branch on that value, and add a change listener so the experience updates if the user toggles the setting mid-session. In React, Next.js, or Vue, wrap this in a small hook or composable so every component reads one source of truth instead of re-querying matchMedia in a dozen places.
One caveat developers miss: scroll-behavior: smooth is motion too. Smooth-scrolling a user hundreds of pixels to an anchor can be disorienting, so reset it to auto under reduced motion as shown above.
How this maps to WCAG 2.3.3 and 2.2.2
Two success criteria govern motion, and they cover different triggers. 2.3.3 Animation from Interactions (Level AAA) says that motion animation triggered by interaction can be disabled unless the animation is essential. This is the criterion prefers-reduced-motion was practically designed for: when motion fires because the user scrolled, hovered, or clicked, give them a way to turn it off. Because 2.3.3 is AAA, it sits above the AA baseline, but honoring it is low-cost and high-impact.
2.2.2 Pause, Stop, Hide (Level A) targets a different beast: motion that starts automatically, runs more than five seconds, and plays alongside other content — think auto-advancing carousels, animated tickers, or looping background video. For these, the user must be able to pause, stop, or hide the movement. A reduced-motion media query helps, but 2.2.2 also expects a visible control, because not everyone affected has the OS setting enabled. Build a real pause button into any carousel or auto-playing component.
Both criteria are part of WCAG 2.2, the standard referenced by the European Accessibility Act via EN 301 549. The EAA's enforceable baseline is WCAG 2.2 Level A and AA, so 2.2.2 (Level A) is squarely in scope for products covered in the EU, while 2.3.3 (AAA) is a best-practice target rather than a legal floor. You can see how both sit in the wider standard on our WCAG 2.2 reference.
A quick checklist before you ship
- Toggle the OS reduce-motion setting and reload — confirm parallax, large transforms, and auto-play stop or simplify.
- Replace removed state-change animations with instant or cross-fade equivalents so no information is lost.
- Add a visible pause control to anything that auto-animates for more than five seconds.
- Reset
scroll-behaviortoautoand gate JavaScript timelines behind amatchMediacheck with achangelistener. - Run an automated scan to catch related issues like disabled zoom and forced timed refreshes — you can scan any page free with AccessScan and pair it with manual motion testing.