You're Animating for Everyone. prefers-reduced-motion Fixes That.
Most animated UIs never check whether the visitor asked for less motion. `prefers-reduced-motion` is the CSS media feature that tells you — and a fail-closed pattern keeps you honest even when a browser doesn't support it.

Somewhere in your traffic right now is a visitor who went into their OS settings and turned on "Reduce motion." Maybe it's vestibular — a parallax hero genuinely makes them reach for the back button, sometimes mid-scroll, stomach first. Maybe it's just a preference. Either way, they told their computer. Your CSS never asked.
That's the part that's easy to miss: this isn't a setting you'd need to build. It already exists, it's already wired into every major OS, and the browser already knows the answer. There's a one-line CSS media feature that reads it. Almost no one uses it, and most of the people who do use it get the "safe" version backwards — in a way that doesn't look wrong in a code review.
Here's what usually happens the first time someone audits a site for motion sensitivity and feels bad about what they find. They add one rule at the top of the stylesheet and call it done:
Ship it, close the ticket, feel good. Then the bug reports start: the loading spinner doesn't spin anymore — it just sits there, frozen, looking broken instead of busy. The focus ring that used to ease in now snaps so hard it looks like a rendering glitch. The toast that confirms "Saved" pops in and out with no transition at all, so on a slow glance it looks like nothing happened.
Turns out !important doesn't know the difference between a parallax hero that exists purely to look cool and a spinner whose entire job is to show the page is still alive. It nuked both.
Here's the distinction the blanket rule missed, and it's the one thing to take from this whole post if you take nothing else: "reduce motion" does not mean "delete all motion."
The accessibility guidance behind this setting (it's closely related to WCAG's Animation from Interactions criterion, though that one specifically covers interaction-triggered motion) is about non-essential motion — parallax, auto-playing background video, swooping page transitions, anything whose only purpose is visual flair. Motion that's carrying information — a spinner proving a request is in flight, a focus ring proving where keyboard focus landed, a progress bar — should usually still move, just without the large transforms (scale, translate, 3D) that trigger a vestibular reaction. A spinner that still spins in place is fine. A spinner that slides across the screen while it spins is not.
So the real pattern isn't "delete everything inside the reduce query." It's "write the big, decorative motion so it only exists when the browser says no one asked for less of it":
Notice which media feature that first block uses: no-preference, not reduce. That's not a stylistic choice — it's the detail that makes the whole thing fail closed.
Quick check before you scroll: which of these two is safer if a browser, for whatever reason, doesn't evaluate the media feature at all?
In version A, the animation is the default — it runs unless the reduce query actively matches. If the media feature can't be evaluated for any reason, nothing turns it off, and the visitor who asked for less motion gets the full swoop anyway. In version B, the animation is the thing that has to be earned — it only runs when the browser can positively confirm no-preference. If evaluation fails for any reason, the safe, motionless default is what's left standing.
prefers-reduced-motion itself has excellent support in evergreen browsers today, so this isn't about Internet Explorer. It's about not betting your accessibility behavior on an active override firing correctly, when you could just as easily make stillness the thing that happens by default and motion the thing you have to opt into.
Here's the open loop from the start of this post: the visitor whose stomach can't handle your parallax hero isn't only fighting your CSS. A lot of the motion that actually triggers a reaction never goes through a transition or animation property at all — it's a requestAnimationFrame loop moving a background layer on scroll, or a hero video with autoplay and no regard for anyone's settings. @media (prefers-reduced-motion: reduce) has zero effect on either, because neither one is CSS.
JavaScript gets the same preference through window.matchMedia() — the same API behind watching other media queries from JS — and, same as any other media query, you can subscribe to it changing live, without a page reload:
That change listener matters more than it looks: if someone flips "Reduce motion" on in their OS settings while your tab is already open — which is exactly what someone mid-vertigo is likely to do — the parallax loop stops without them needing to reload anything.
Runs right in your browser — poke at it and watch the concept react live.
One thing this setting can't do: tell you about a visitor who has the exact same sensitivity but never found, or never bothered with, the OS toggle. prefers-reduced-motion: no-preference is what you get both from someone who genuinely loves motion and from someone who simply doesn't know the setting exists. Respecting the media query is necessary, not sufficient — if your product leans heavily on motion, an in-app "reduce animations" toggle that doesn't require touching OS settings is still worth having alongside it, not instead of it.
The setting was never the hard part — it's one media feature, supported everywhere that matters, and your OS has been asking the browser about it for years. The hard part is remembering that "respect it" means separate decorative motion from functional motion and default to still, not delete transitions wherever you find the word animation. Next time you reach for that one global override, flip it around: wrap the swoop in no-preference instead of silencing it in reduce.
Go check your hero section right now — does the parallax or the autoplaying background video even know this setting exists, or does it just keep moving no matter what the visitor asked for?
Think it clicked? Take the 8-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
- The CSS Property That Fixes Dark Mode's Ugly White Boxes
- One CSS Property Replaces Your Checkbox Hack
- Stop Writing Media Queries for Font Size
🚀 Want more like this? Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
Keep reading
One post a day, in your inbox
Each one with a runnable playground and a quiz. No pitch, no digest, unsubscribe in one click.
0 comments
Sign in to join the discussion, like comments, and save articles for later.