Reduced Motion
Date: 2026-08-17
A system-level preference for less animation, set by people for whom motion causes genuine physical symptoms. It’s one CSS media query, it’s widely supported, and ignoring it means shipping something that makes a subset of your customers feel ill.
prefers-reduced-motion is an operating-system setting exposed to CSS and JavaScript, indicating the user has requested minimal non-essential motion.
Why people set it
Vestibular disorders — inner-ear conditions affecting balance — make certain motion genuinely nauseating. The symptoms are dizziness, nausea, headache and disorientation, and they’re triggered by the same effects designers reach for:
Most triggering:
- Parallax scrolling
- Large-scale movement across the viewport
- Zoom and scale transitions
- Spinning or rotating elements
- Auto-playing video and carousels
- Scroll-jacked animation
Less triggering:
- Small fades
- Colour changes
- Short, small movements
Others set it for motion sensitivity, migraine, attention reasons, or simple preference — and none of those require justification.
The implementation
@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;
}
}Near-zero rather than zero — some scripts depend on transition or animation events firing, and setting the duration to 0 can prevent them, breaking behaviour rather than just removing motion.
In JavaScript:
const reduce = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches
if (!reduce) startCarouselAutoplay()JavaScript-driven animation is not covered by the CSS query — canvas, WebGL, scroll-triggered libraries and autoplay all need the check explicitly.
Reduce, don’t remove
The distinction that matters:
WRONG remove all motion
→ state changes become
instantaneous with no
indication anything happened
→ the causality cue is lost for
people who may need it most
RIGHT replace movement with an
instant but still-visible
change
→ a modal appears rather than
sliding, and it still clearly
appeared
→ a fade of 0.01s is
imperceptible and still fires
the events
Motion communicates. The goal is removing the distress, not the information — Motion and Transitions.
The build-it-in approach
Rather than a global override bolted on at the end:
:root {
--duration-base: 250ms;
}
@media (prefers-reduced-motion: reduce) {
:root {
--duration-base: 0.01ms;
}
}
.modal {
transition: opacity var(--duration-base);
}One token, changed once. Every component using it respects the preference automatically, and nothing has to be remembered per component — Design Tokens, Easing and Duration.
What else it should affect
Beyond CSS transitions, and these are routinely missed:
AUTO-PLAYING CAROUSELS stop autoplay
AUTO-PLAYING VIDEO don't
PARALLAX disable entirely
ANIMATED GIFs offer a pause,
or a static poster
SMOOTH SCROLL scroll-behavior:
auto
LOADING SHIMMER static skeleton
— Loading and Perceived Performance
CONFETTI / CELEBRATION skip it
See: Loading and Perceived Performance
Auto-playing carousels are the worst offender in retail — large-scale movement, unrequested, repeating, and typically ignored past the first slide anyway.
The related requirement
Separate from the preference, WCAG requires that motion lasting more than five seconds, or auto-updating content, must have a mechanism to pause, stop or hide it — which applies whether or not the preference is set — WCAG.
Testing it
macOS System Settings → Accessibility
→ Display → Reduce motion
Windows Settings → Accessibility →
Visual effects → Animation
effects
iOS/Android Accessibility → Motion
DevTools emulate the media feature
directly
Toggle it and walk a purchase. The bugs it surfaces are usually the JavaScript-driven animations nobody wired to the check.