ESC

Type to search the knowledge base.

Reduced Motion Preferences

Honor prefers-reduced-motion for vestibular safety — CSS media queries, JS animation guards, and what to reduce vs remove.

beginner3 min read
  • 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

  1. Ignoring the preference for “delight.”
  2. Smooth scroll only — still respect reduce.
  3. CSS reduced but canvas/WebGL animations run full tilt.
  4. 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.

Further reading

Related guides