Reduced motion is not no motion
The reduced-motion setting is a request to cut the motion that causes harm, not a request for a static page. Here is which animations to keep, shorten, and remove.
Every major operating system has a reduce-motion setting. People turn it on because animation makes them dizzy or nauseated, because it triggers migraines, because it makes it hard to track where things went, or because it is simply exhausting. The browser passes the setting through as a media query, and a page can respond.
The common response is one line:
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; transition: none !important; }
}
It is better than ignoring the setting, and it is what this site does today. But it throws away motion that was helping, and it treats a request to reduce as a request to remove. There is a more careful answer.
What the setting is asking for
Reduce motion is about the kinds of motion that cause the problems above: large things moving across the screen, parallax, zooming, spinning, and anything that keeps going. A 150 millisecond color change on hover is not motion in that sense. A dialog fading in is not motion. The setting is asking you to cut the vestibular triggers, not the affordances.
Sort your animations into three bins
Remove. Anything that moves across a large area, scales toward or away from the viewer, rotates, or loops forever. Parallax backgrounds. Hero elements that slide in from off screen. Spinners that spin. Auto-playing carousels. Under reduced motion these go to zero.
Shorten or swap. Transitions that communicate a change: a panel sliding open, a card lifting on hover, an item moving to its new place in a list. Replace the slide with a fade, cut the duration, or jump straight to the end state. The user still learns that something changed; they just do not watch it travel.
Keep. Short color and opacity transitions, focus ring appearance, a progress bar filling. These are under about 200 milliseconds, do not move the element, and help people see what happened.
@media (prefers-reduced-motion: reduce) {
.hero-art, .parallax, .spinner { animation: none; }
.panel { transition: opacity 0.15s ease; transform: none; }
* { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; }
}
The last line is a safety net: any animation you forgot to sort runs once, instantly, instead of never running. Elements that depend on an animation ending still reach their end state.
Do it in JavaScript too
Scripted motion does not read your CSS. Check the same query before starting a scroll animation, a counter, or a canvas loop.
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
if (!reduce.matches) startParallax();
reduce.addEventListener('change', () => { if (reduce.matches) stopParallax(); });
Listen for the change, because people toggle the setting while a page is open.
Offer a switch, do not require one
A visible motion toggle on the site is a kindness for people who have not found the system setting. It is not a substitute for honoring the media query, because most people who need it set it once at the OS level and expect every page to listen.
The test
Turn on reduce motion in your system settings. Load the page. Anything still sliding, spinning, or zooming is a bug. Anything that no longer tells you what happened, a panel that now appears with no cue at all, is a different bug. The goal is a page that is calm and still legible, not a page that is frozen.
The agent ruleset tracks this as AW-MOTION-001, and corpus page 13-motion plants an infinite animation with no override.