prefers-reduced-motion
prefers-reduced-motion is a media feature that reports whether the user has asked their operating system to minimize animation and motion effects. It's set in system accessibility settings (for example macOS's "Reduce motion", Windows's "Show animations in Windows", or the equivalent on mobile OSes), and CSS can read that OS-level preference directly with a media query — no JavaScript, no cookie banner, no separate toggle to build.
Who this is for
Vestibular disorders — large-scale motion, parallax, and spinning effects can trigger real physical symptoms (dizziness, nausea) for some users.
Motion sensitivity / migraine triggers — flashing or fast-moving content can provoke migraines or seizures in susceptible users.
Simple preference — plenty of users who have no medical reason simply find constant motion distracting and turn it off.
The media query
/* Default: users who haven't opted for reduced motion get the full effect */
.hero-banner {
animation: slide-and-spin 2s ease-in-out infinite;
}
/* Users who've asked for reduced motion get little to none */
@media (prefers-reduced-motion: reduce) {
.hero-banner {
animation: none;
}
}
/* You can also target the opposite explicitly */
@media (prefers-reduced-motion: no-preference) {
.hero-banner {
animation: slide-and-spin 2s ease-in-out infinite;
}
}A worked example — a flashy card entrance
Say a card animates in with a dramatic flip-and-bounce by default. Wrap the flashy version so it's skipped for reduced-motion users, and give them a much calmer (or instant) fallback instead of just nothing:
.card {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: no-preference) {
.card {
animation: flip-bounce-in 0.8s cubic-bezier(0.2, 0.8, 0.2, 1);
}
@keyframes flip-bounce-in {
0% { opacity: 0; transform: rotateY(90deg) scale(0.8); }
60% { opacity: 1; transform: rotateY(-10deg) scale(1.05); }
100% { opacity: 1; transform: rotateY(0) scale(1); }
}
}
@media (prefers-reduced-motion: reduce) {
.card {
/* Still communicate the state change, just without motion */
transition: opacity 0.15s ease;
}
}The card still appears for every user. Users without the preference get the full flip-and-bounce; users who've asked for reduced motion get a quick, gentle fade (or nothing at all) instead of a disorienting rotation.