Respecting prefers-reduced-motion

prefers-reduced-motion is a CSS media feature that reports whether a visitor has asked their device for less motion. It has two values, no-preference and reduce. Wrap large movement such as parallax, zooms and slides in a query, and when the value is reduce, swap them for a short fade or no animation.

Where people turn the setting on
SystemSetting
Windows 11Settings > Accessibility > Visual effects > Animation effects (off)
macOSSystem Settings > Accessibility > Display (or Motion) > Reduce motion
iOSSettings > Accessibility > Motion > Reduce Motion
Android 9 and laterSettings > Accessibility > Remove animations
Firefox (testing)about:config, set ui.prefersReducedMotion to 1

Updated

People turn on reduced motion because sliding, zooming and parallax can trigger dizziness, nausea or headaches, especially for those with vestibular disorders. The badge below reads your own device setting live, and the demo shows the same animation in full and reduced form, so you can compare before choosing a pattern.

Your motion setting

The badge reads your device setting with matchMedia, and the preview switch shows the reduced animation without changing your system.

CSS patterns

Motion as an enhancement

The card slides in only when no preference is set. Everyone else sees a gentle fade.

<style>
  @keyframes fade { from { opacity: 0 } to { opacity: 1 } }
  @keyframes slide-up { from { opacity: 0; transform: translateY(40px) } to { opacity: 1; transform: none } }
  .card { padding: 20px; border-radius: 12px; background: #eff6ff; animation: fade 400ms ease both }
  @media (prefers-reduced-motion: no-preference) {
    .card { animation: slide-up 600ms cubic-bezier(.2,.8,.2,1) both }
  }
</style>
<div class="card">This card arrives with motion only if your device allows it.</div>

A safety net for existing sites

Useful when you cannot rewrite every animation. Durations become almost zero rather than none, so animationend and transitionend events still fire.

@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;
  }
}

JavaScript and React

Read the setting and follow changes

matchMedia returns a live list. Its change event fires when the visitor flips the setting.

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

function scrollToTop() {
  window.scrollTo({ top: 0, behavior: query.matches ? "auto" : "smooth" });
}

query.addEventListener("change", (event) => {
  document.documentElement.dataset.motion = event.matches ? "reduce" : "full";
});

React hook: usePrefersReducedMotion

Starts at true on the server so nothing animates before the browser has answered.

import { useEffect, useState } from "react";

const QUERY = "(prefers-reduced-motion: reduce)";

export function usePrefersReducedMotion() {
  const [reduce, setReduce] = useState(true);
  useEffect(() => {
    const mql = window.matchMedia(QUERY);
    setReduce(mql.matches);
    const onChange = (e) => setReduce(e.matches);
    mql.addEventListener("change", onChange);
    return () => mql.removeEventListener("change", onChange);
  }, []);
  return reduce;
}

What to swap each effect for

Reduced alternatives
Full motionReduced version
Parallax backgroundStatic background
Slide or zoom page changeCross-fade, or an instant change
Element flies in on scrollShort fade in place
Spinning loaderPulsing opacity or a progress bar
Smooth scroll to anchorInstant jump
Autoplaying background videoPoster image with a play button

Which motion causes problems

The trouble is movement that fills a large part of the view or moves in a direction the visitor did not cause: parallax backgrounds, full-page zooms, sliding page changes, spinning elements and content that jumps as you scroll.

Small, quick, user-started feedback, such as a button colour change or a checkbox tick, is rarely an issue and can usually stay.

No-motion-first CSS

Many sites add a reduce query at the end that sets every duration to almost zero. It works, but it is easy to miss one animation, and some scripts listen for animationend events that then fire instantly.

Writing the motion inside a no-preference query flips the default. Anyone whose browser does not report a preference still gets motion, and anything you forget falls back to the calm version.

Scripts and libraries

CSS queries do not reach animations started from JavaScript, canvas or smooth scrolling calls. Check the media query before calling element.animate() or scrollIntoView with behavior: smooth.

Read the setting once at start and subscribe to its change event, so a person who switches it mid-visit is respected without a reload.

How it works

  1. Check the badge: it shows what your device reports right now and updates if you change the setting.
  2. Use the preview switch to see the reduced version of the demo without changing your system setting.
  3. Copy the CSS, JavaScript or React pattern that matches how your site animates.

Tips

Frequently asked questions

What values does prefers-reduced-motion have?

Two: no-preference, when the visitor has not set anything, and reduce, when they have asked for less motion. Writing @media (prefers-reduced-motion) on its own is the same as asking for reduce.

How do I check prefers-reduced-motion in JavaScript?

Call window.matchMedia("(prefers-reduced-motion: reduce)") and read its matches property. Add a change event listener to the returned object to react when the setting changes while the page is open.

Should I turn off every animation when reduce is set?

No. The request is for less motion, not a frozen page. Keep colour changes, short fades and progress indicators, and remove or shrink movement such as sliding, scaling, spinning and parallax.

Is respecting reduced motion required by WCAG?

WCAG 2.3.3 Animation from Interactions, level AAA, asks that motion triggered by interaction can be turned off unless it is essential. Respecting the system setting is one of the listed ways to meet it. Separately, 2.2.2 Pause, Stop, Hide at level A covers motion that starts on its own.

How do I handle reduced motion in Tailwind?

Use the motion-safe: variant to apply an animation only when no preference is set, or motion-reduce: to override one when reduce is set, for example motion-safe:animate-bounce.

Related: CSS transitions playground, Scroll animations, Framer Motion examples, CSS media queries, Website accessibility checklist, CSS animation generator.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap