AccessScanRun a free scan

Guide

prefers-reduced-motion: Designing for Vestibular Disorders and WCAG Motion Criteria

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-behavior to auto and gate JavaScript timelines behind a matchMedia check with a change listener.
  • 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.

Check your site against AccessScan

See your issues ranked by impact in seconds — free.

Run a free accessibility scan

FAQ

Does prefers-reduced-motion mean I have to remove all animation?

No. The reduce value asks you to avoid large, distracting, non-essential motion such as parallax, big zooms, and looping spins. Small opacity fades, color changes, and movements under about five pixels are generally safe to keep and often carry useful meaning.

Is supporting prefers-reduced-motion enough to pass WCAG 2.2.2?

Not on its own. WCAG 2.2.2 Pause, Stop, Hide (Level A) covers motion that starts automatically and runs over five seconds, and it expects a visible pause, stop, or hide control. Many affected users never enable the OS setting, so auto-playing carousels and background video still need an explicit control.

What is the difference between WCAG 2.3.3 and 2.2.2?

2.3.3 Animation from Interactions (Level AAA) is about motion the user triggers by scrolling, hovering, or clicking, and asks that it be disableable unless essential. 2.2.2 Pause, Stop, Hide (Level A) is about motion that starts automatically and plays alongside other content. Different triggers, different remedies.

How do I handle JavaScript animations like GSAP or scroll libraries?

A CSS media query will not stop JavaScript, so read window.matchMedia('(prefers-reduced-motion: reduce)').matches at runtime and branch your code on it. Add a change event listener so the experience updates if the user toggles the setting during their session.

Is reduced-motion testing something an automated scanner can fully verify?

Partly. Automated tools can flag related issues like disabled pinch-zoom or forced timed refreshes, but judging whether a specific animation is too intense for vestibular users requires manual testing with the OS setting enabled. Use automated scans as a fast first signal, then test motion by hand.

More guides