Skip to Content
HTML and CSS Advanced: Styles That Hold Up in Production
Advanced
0 likes
Courses
HTML and CSS Advanced: Styles That Hold Up in Production

prefers-reduced-motion and Respecting the User

Honour the reduced-motion setting in CSS and JavaScript, keep useful feedback, and test both states. After this, users who asked for less movement get less movement.

Course content

Sign in to save your progress.

Course Home

Skip ahead: If you already wrap scroll-behavior: smooth in a media query and replace slides with fades under prefers-reduced-motion, skip to "Test it, don't assume", then continue.

Some people turn motion off on purpose. On Windows it is "Show animations in Windows", on macOS and iOS it is "Reduce motion", and on Android it is "Remove animations". The browser passes that choice to your CSS as prefers-reduced-motion. It is a request from the user. Ignoring it is a choice, and for people with vestibular disorders it can mean dizziness or nausea.

Two ways to write it

/* Option A: motion by default, remove it when asked */
.panel { transform: translateY(24px); opacity: 0; transition: opacity 250ms ease, transform 250ms ease; }
.panel.is-visible { transform: none; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .panel { transform: none; }
}

/* Option B: motion only when the user has no preference */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

Option A fits things that already animate. The .panel keeps its fade but loses the slide. Option B suits extras like smooth scrolling, which you can leave out entirely for users who asked for less motion. For new work I would start with B, because the default then does nothing surprising.

Reduce, don't remove

The setting means less motion, and not no feedback. A spinner that shows work is happening, a progress bar, a colour change on a selected tab: those carry information, and you should keep them. What you replace is large movement: sliding panels, parallax, zooming images, bouncing icons, auto-playing background video. Swap them for a fade or an instant change.

What I measured

I emulated both settings in Chrome 154 against a page with the CSS above. With no-preference, scroll-behavior computed to smooth and the panel's transform was a 24px offset, matrix(1, 0, 0, 1, 0, 24). With reduce, scroll-behavior was auto and the transform was none. transition-property was opacity, transform in both cases, so the fade stays and only the 24px slide goes away.

What a good replacement looks like

A drawer that slid in from the left can fade in at its final position. A card that zoomed on hover can change its border colour. A parallax hero can be a still image. In each case the user gets the same information, the state changed, without the sense of travel. If you cannot find a quiet replacement, the animation may have been decoration, and the reduced version is simply nothing.

Reading it from JavaScript

const query = window.matchMedia("(prefers-reduced-motion: reduce)");

function applyMotion() {
  document.documentElement.dataset.motion = query.matches ? "reduced" : "full";
}

applyMotion();
query.addEventListener("change", applyMotion);

Use this when a library animates in JavaScript and CSS cannot reach it. The change listener matters, because users flip the OS setting while the tab is open.

Test it, don't assume

In Chrome DevTools open the Rendering panel and set "Emulate CSS media feature prefers-reduced-motion" to reduce. In Playwright, browser.new_context(reduced_motion="reduce") does the same. Then click through the whole page. Check that nothing slides, scales or loops, and that nothing important vanished.

⚠ Warning: Do not use animation: none !important on * as a blanket fix. It also stops the spinner and the progress bar, and it can break components that wait for animationend, because that event never fires.

Common mistakes

  • Writing the reduced rule but forgetting the JavaScript animations, which ignore CSS entirely.
  • Setting transition-duration: 0s everywhere. A one-millisecond duration is safer for code that waits on transition events.
  • Only testing at the default setting, so the reduced path ships broken.

💡 Tip: Add the reduced-motion emulation to the checklist you run before launch. It takes one minute and catches the sliding hero everybody forgot.

What happens if you skip this

A visitor who asked their device for less motion gets the sliding hero, the parallax and the smooth scroll anyway. They leave. You never see it in analytics, because "closed the tab" looks like any other exit.

Try it

Turn on reduced motion on your own machine and open your site. Write down every movement you can still see, then decide for each one: information or decoration?

Whose request are you honouring when you leave the sliding hero on?

About

Honour the reduced-motion setting in CSS and JavaScript, keep useful feedback, and test both states. After this, users who asked for less movement get less movement.

Duration
5 min
Last update
08/10/2026
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.