// guide
Accessible Motion and Animation: prefers-reduced-motion and WCAG
Animation can delight most users and physically sicken others. This guide covers who motion affects, what WCAG actually requires (pause/stop/hide, no flashing, animation from interactions), and how to honor the prefers-reduced-motion setting in CSS and JavaScript — reducing motion for the people who need it without stripping personality from your interface.
// 01 · who motion affects
Who Motion Affects
Motion on screen is not a neutral aesthetic choice. For people with vestibular disorders — conditions affecting the inner ear and balance — large or unexpected movement can cause real physical symptoms: dizziness, nausea, disorientation, and migraine that can last hours. Parallax scrolling, full-screen zoom transitions, and content that slides or flies across the viewport are common triggers.
Motion also affects people with attention and cognitive differences, for whom animated or auto-updating content is distracting and makes text harder to read, and people prone to photosensitive seizures, for whom flashing content is a genuine hazard. A sizeable share of users deliberately switch on their operating system's "reduce motion" setting — the web's job is to listen for that request and respond to it.
// 02 · what wcag requires
What WCAG Requires
Three success criteria govern motion. Two are Level A — the baseline every site should meet — and one is AAA but cheap enough that honoring it is standard practice.
| Criterion | Level | What it requires |
|---|---|---|
| 2.2.2 Pause, Stop, Hide | A | For motion that starts automatically, lasts more than 5 seconds, and runs beside other content, give users a way to pause, stop, or hide it (e.g. a carousel or auto-scrolling ticker). |
| 2.3.1 Three Flashes or Below Threshold | A | Nothing may flash more than three times in any one-second period. Flashing above this threshold can trigger seizures. |
| 2.3.3 Animation from Interactions | AAA | Let users disable non-essential motion triggered by their own actions (scroll, hover, click). Honoring prefers-reduced-motion satisfies this. |
// 03 · respecting prefers-reduced-motion in css
Respecting prefers-reduced-motion in CSS
The prefers-reduced-motion media feature exposes the user's OS setting to CSS. There are two ways to use it. The most robust is to opt in to motion: keep the interface still by default, and add animation only when the user has expressed no preference against it.
/* Motion is added only when the user has NOT asked to reduce it */
@media (prefers-reduced-motion: no-preference) {
.card {
transition: transform 200ms ease;
}
.card:hover {
transform: translateY(-4px);
}
}
If retrofitting an existing codebase where motion is already scattered everywhere, a global reduce block is a pragmatic safety net. It near-instantly collapses animations and transitions when reduce is set, without touching every rule by hand:
@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;
}
}
animationend and transitionend events firing — so any JavaScript waiting on them (to reveal content, advance a step, clean up) still runs. A hard animation: none can leave those listeners hanging and break the flow.
// 04 · handling motion in javascript
Handling Motion in JavaScript
CSS covers transitions and keyframes, but JavaScript-driven motion — a smooth-scroll call, a physics animation, an auto-playing sequence — has to check the preference itself. Read it with matchMedia and branch:
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
function scrollToSection(target) {
target.scrollIntoView({
behavior: reduceMotion.matches ? 'auto' : 'smooth',
});
}
// React to changes without a page reload
reduceMotion.addEventListener('change', () => {
if (reduceMotion.matches) stopAutoplay();
});
Read the value at the moment you animate rather than caching it once at load, and listen for the change event so the interface responds if the user flips the setting mid-session. The same check gates auto-advancing carousels, animated counters, confetti, and any effect you drive from script.
// 05 · the usual offenders
The Usual Offenders
A few patterns cause most motion-related complaints. Each has an accessible version.
- Auto-advancing carousels. They violate 2.2.2 unless users can pause them, and the movement distracts everyone. Provide a visible pause control, stop on hover and focus, and do not auto-advance at all under reduced motion. See the carousel pattern for an accessible build.
- Parallax scrolling. Backgrounds moving at a different speed from foreground content is a classic vestibular trigger. Disable the parallax entirely under reduced motion and let the layers scroll together.
- Full-screen transitions. Page or route changes that slide or zoom the whole viewport. Replace them with a quick cross-fade, or an instant change, when motion is reduced.
- Autoplaying video and background loops. Motion that starts on its own and runs more than five seconds needs a pause/stop control (2.2.2). Prefer not autoplaying, and respect reduced motion by showing a static poster frame.
prefers-reduced-motion as an additional layer on top of the baseline controls, not a replacement for them.
// 06 · a safe default strategy
A Safe Default Strategy
Pulling it together into a policy you can apply consistently:
- Keep motion small and purposeful by default. Short durations (under ~300ms), gentle easing, movement that communicates something (where a panel came from, that an item was added).
- Under reduced motion, replace movement with a fade or an instant change — do not just delete the transition and leave a jarring snap. A 150ms opacity fade reads as calm, not broken.
- Never rely on motion alone to convey meaning. If an animation signals success or an error, back it with text and an ARIA live region so it survives motion being reduced.
- Provide explicit controls for anything auto-playing, independent of the OS setting, to satisfy 2.2.2.
- Avoid rapid flashing entirely to stay clear of 2.3.1.
// 07 · common mistakes
Common Mistakes
- Ignoring
prefers-reduced-motionaltogether. The most common failure — animations play the same for everyone regardless of the setting. - Deleting motion instead of reducing it. A hard cut to no animation can feel broken; a quick fade preserves polish while removing the trigger.
- Auto-playing carousels with no pause control. A direct 2.2.2 failure, and a distraction for everyone.
- Motion as the only signal. A shake to mean "error" or a slide to mean "saved", with no text equivalent, disappears when motion is reduced.
- Rapid flashing. Any content flashing more than three times per second — a seizure risk that no preference setting excuses.
- Checking the preference once at load. Not listening for the
changeevent, so the UI ignores a mid-session toggle.
Frequently asked questions
What is prefers-reduced-motion?
prefers-reduced-motion is a CSS media feature that reflects an operating-system setting where users can ask software to minimize non-essential animation (macOS "Reduce motion", Windows "Show animations", and equivalents on iOS/Android). When it is set, @media (prefers-reduced-motion: reduce) matches, letting you turn off or tone down transitions and animations for the people who requested it — without affecting everyone else.
Does WCAG require respecting reduced motion?
Several criteria touch motion. 2.2.2 Pause, Stop, Hide (Level A) requires a way to pause or stop any motion that starts automatically, lasts more than five seconds, and runs alongside other content. 2.3.1 Three Flashes (Level A) forbids content that flashes more than three times per second. 2.3.3 Animation from Interactions (Level AAA) asks you to let users disable motion triggered by their own actions — which honoring prefers-reduced-motion satisfies. Even where it is only AAA, respecting the setting is a low-cost, high-impact win.
Should I remove all animation when reduced motion is set?
No — reduce, do not necessarily remove. The problem is large movement: parallax, sliding panels, zooming, spinning, things flying across the screen. These can trigger nausea and dizziness in people with vestibular disorders. Gentle opacity fades and instant state changes are usually fine and can even aid comprehension. A good reduced-motion default replaces big movement with a quick fade or an instant change, rather than making the interface feel broken and static.
What kinds of animation cause accessibility problems?
The riskiest are large-scale movement effects: parallax scrolling, full-screen transitions that slide or zoom, auto-advancing carousels, background video, and anything that spins or bounces continuously. For people with vestibular disorders these can cause real physical symptoms — dizziness, nausea, migraine. Flashing content (more than three flashes per second) is a separate, serious hazard that can trigger seizures. Small UI affordances like a button hover or a subtle fade are generally low-risk.
How do I test reduced motion?
Turn the OS setting on and browse your site. On macOS: System Settings → Accessibility → Display → Reduce motion. On Windows: Settings → Accessibility → Visual effects → Animation effects. Browser dev tools can also emulate it (Chrome/Edge: Rendering panel → "Emulate CSS media feature prefers-reduced-motion"), which is the fastest way to check without changing your system. Then confirm that essential meaning is not lost when motion is reduced.