Animate Boldly While Respecting Users Who Get Sick From Motion
Guard large and looping motion with prefers-reduced-motion so bold animation never triggers nausea or fails accessibility.
Animation is one of the things that separates a product that feels alive from one that feels static. A card that glides in, a list that slides right under the cursor, a hero that breathes. We build a lot of motion into the interfaces we ship, because the right micro-interactions are what separate a premium product from a prototype. But there is a person on the other side of some of those animations who does not experience them as delight. They experience them as nausea, dizziness, a migraine coming on, or the need to lie down for a while.
Those people have vestibular disorders, and motion effects like parallax scrolling, large page transitions, and spinning or looping elements can literally make them sick. This is not a fringe concern, and it is not a reason to strip the motion out of your product. It is a reason to make the motion conditional. The same interface can be richly animated for people who enjoy it and calm for people who need it, and the mechanism that does this is one CSS media query plus the discipline to use it everywhere.
The operating system already knows
Here is the part that makes this tractable: the user has already told their device. Every major operating system has a "reduce motion" setting, and people who are sensitive to motion turn it on. The browser exposes that preference to your page through the prefers-reduced-motion media query. You do not have to build a detection system or ask the user a question. You just have to read the answer their OS already holds.
@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;
}
}
A blanket reset like that is a reasonable safety net, but the better approach is to author each animation to respect the preference deliberately, so a reduced-motion user still gets a clean, complete interface rather than one where things blink into place. Reach for the full version only when motion is welcome.
Two ways to write it, and the one that fails closed
There are two patterns for guarding an animation, and the difference matters for the edge cases.
The first applies motion only when motion is allowed:
@media (prefers-reduced-motion: no-preference) {
.card { transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1); }
.card:hover { transform: translateY(-4px) scale(1.02); }
}
The second applies motion by default and removes it when reduction is requested. Both are valid, but the first has a quiet advantage: it fails safe. If a browser or environment cannot resolve the preference, no-preference does not match, so the motion does not apply, and a reduced-motion user is never accidentally exposed to an animation the guard failed to catch. When the stakes are someone's physical comfort, defaulting to calm and adding motion only on an explicit no-preference is the more careful default. You can even design the reduced state as the baseline and treat the full animation as the enhancement layered on top.
Guard the JavaScript too, not just the CSS
A media query handles animations written in CSS. It does nothing for motion you drive in JavaScript, and a lot of the richest motion lives there: a smooth-scroll library that hijacks the scroll wheel, a physics-based reveal, a canvas or WebGL animation, an autoplaying carousel on a timer. JavaScript-heavy motion is also the kind of long task that can stall the main thread if it runs unchecked. If you guard the CSS and forget the JS, a reduced-motion user still gets the most intense motion on the page, which is exactly backwards.
In JavaScript, read the same preference with matchMedia and branch on it.
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)");
if (!reduce.matches) {
initSmoothScroll();
startParallax();
}
For a smooth-scroll setup, the right behavior under reduced motion is to not initialize the momentum scrolling at all and let the browser's native, instant scroll take over. For a looping animation, stop the loop. For a parallax effect, hold the elements still. The rule is the same as the CSS: when the user asked for less motion, give them the static version of that feature, not a slightly toned-down one. Reading an OS preference and honoring it deliberately is the same instinct behind real dark mode that is not just an inverted color sheet: the system already knows what the user wants, and your job is to respect it well.
Not every animation needs to be silenced
There is nuance worth keeping. WCAG's guidance distinguishes between large, motion-heavy effects and small, functional ones. A quick hover fade, a button that gently responds to a press, a short color transition, these tiny UI transitions do not trigger vestibular reactions and do not require a control. They are the same small touches that, done right, also have to meet WCAG contrast without wrecking your brand palette. What needs the guard, and sometimes an explicit on-page toggle, is the bigger stuff: carousels, autoplaying backgrounds, parallax, large sweeping page transitions, anything looping or full-screen.
So the practical line is: keep the small, flush, butterfly-fluid micro-interactions that make an interface feel responsive, and guard the large, sweeping, looping, or scroll-driven motion behind prefers-reduced-motion. WCAG even names three acceptable strategies: avoid unnecessary animation entirely, provide a control to turn non-essential animation off, or honor the OS preference via the media query. The media-query approach is the one that scales, because it works for every user automatically without cluttering your UI with a toggle, and you can add an explicit control on top for the heaviest effects.
Make it part of how you build, not a final audit pass
The failure mode here is treating accessibility as a checklist you run at the end. By then the motion is everywhere, ungated, and retrofitting the guards is tedious and easy to miss. The better habit is to write the guard alongside the animation, in the same commit, so every transition is born respecting the preference. When the reusable motion primitive itself, the class or hook that animates your cards and rows, has the prefers-reduced-motion check baked in, every element that uses it inherits the right behavior for free. You stop relying on anyone remembering, and the whole product becomes compliant by construction.
This is the same instinct that drives the rest of our work: the right thing should happen by default, not by discipline, the way one command should bootstrap your whole dev stack. We hold a high bar for motion, every interactive element on every page should feel alive, and we hold an equally high bar for who that motion serves. The two are not in tension. A reduced-motion guard is what lets you animate boldly for the people who love it without making anyone sick, and that is a strictly better interface than one that had to choose. Building both into the same components is part of how we design and build for the web, where premium motion and accessibility are the same job done well.
The standard
Bold animation and inclusive design are not a trade-off. Author the rich version, gate the large and looping effects behind prefers-reduced-motion, guard the JavaScript-driven motion the same way you guard the CSS, and bake the check into your reusable primitives so nothing slips through. The result is a product that feels alive to most people and calm to the ones who need it, without a single user having to choose between enjoying your site and feeling well.






