Snippets / Accessibility /

Reduced Motion

@media (prefers-reduced-motion: reduce) lets you disable or replace motion-heavy animations for users who have enabled the OS-level "reduce motion" setting.

Spinner
Stops in reduced-motion
Pulse
Fades only (no scale)
Progress bar
Widely Supported
a11yno-js

Quick implementation

/* Default: full animation */
.spinner {
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Option 1: disable entirely */
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none;
  }
}

/* Option 2: replace with a gentler alternative
   (opacity fade instead of movement) */
.pulse {
  animation: pulse 1.5s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.4); }
}

@media (prefers-reduced-motion: reduce) {
  .pulse {
    animation: fade 2s ease-in-out infinite;
  }
}

@keyframes fade {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* Global reset approach — catches everything */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are an accessibility-focused CSS developer. Implement prefers-reduced-motion support for a design system.

Requirements:
1. Show the "disable" pattern: @media (prefers-reduced-motion: reduce) { animation: none } for a loading spinner.
2. Show the "replace" pattern: swap a scale-based pulse animation with an opacity-only fade for users who still need a loading indicator but without vestibular-triggering movement.
3. Show the global reset pattern: set animation-duration: 0.01ms, animation-iteration-count: 1, transition-duration: 0.01ms on * inside the media query — with a note that this is a sledgehammer approach that catches animations you may have forgotten.
4. Show how to use the prefers-reduced-motion: no-preference media query to opt IN to animations, rather than wrapping defaults and then overriding them — this is often cleaner when most of the site is motion-heavy.
5. Include a JavaScript check: window.matchMedia('(prefers-reduced-motion: reduce)').matches — useful for JS-driven animations (canvas, WebGL, GSAP).

Constraints:
- The setting maps to: macOS → Accessibility → Reduce Motion; Windows → Ease of Access → Turn off unnecessary animations; iOS → Accessibility → Motion → Reduce Motion.
- Do not remove all transitions — only motion-heavy or transform-based ones. Color and opacity transitions are generally fine.
- Test in Chrome DevTools: Rendering panel → Emulate CSS media feature prefers-reduced-motion.

Output CSS demonstrating all three patterns plus the JS check snippet.

Why this matters in 2026

Vestibular disorders, migraines, and attention disorders affect a significant portion of users. Large-scale or looping animations — parallax scrolling, spinning loaders, sliding carousels — can trigger nausea, headaches, or distraction for these users. The OS-level "reduce motion" preference is a direct signal from the user that they need less visual movement, and @media (prefers-reduced-motion) is the CSS hook to respect that signal. Ignoring it is a WCAG 2.3.3 (AAA) violation for animations that run for more than five seconds.

The logic

There are two approaches: disable, or replace. Disabling is appropriate for purely decorative animations — looping background animations, spinning icons, parallax effects. Replacing is better for animations that communicate state — a spinner tells users something is loading, so removing it entirely may confuse; replacing the spin with an opacity fade preserves the meaning without the vestibular trigger. The global reset (animation-duration: 0.01ms !important on *) is a safety net for codebases with many animations, but per-component overrides give you more control over which animations to replace versus disable.

Accessibility & performance

WCAG 2.3.3 (Success Criterion: Animation from Interactions) is AAA level but is increasingly expected in enterprise and government contexts. WCAG 2.2 at AA level requires that users can pause, stop, or hide any auto-playing animation that lasts more than five seconds. Disabling animations for reduced-motion users also has a minor performance benefit — fewer GPU-composited layers and no rAF loops — which is a pleasant side effect on low-power devices.