Shots in this scene
prefers-reduced-motion in CSS
In one line
The prefers-reduced-motion media query tells your CSS that the visitor asked their device for less movement. The rule is to reduce, not remove: swap travel for a fade and keep every state change.
Think of it like this
A friend asks you to turn the music down. You do not unplug the speaker, because then there is no music and no way to tell the party is on. You lower the volume. Reduced motion works the same way: the page still shows what changed, only with less movement.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- media query
- A condition around CSS rules, such as
@media (prefers-reduced-motion: reduce). The rules apply only while it is true. - no-preference
- The value meaning the visitor has not asked for less motion.
- reduce
- The value meaning the visitor switched on reduced motion in their device settings.
- opt-out / opt-in
- Opt-out: movement is the default and
reduceremoves it. Opt-in: movement exists only insideno-preference.
Step by step
01Find what moves
List every animation that moves or scales something: slides, zooms, spins, bounces, parallax. Fades and colour changes can stay.
02Give each a calm twin
For each one decide the replacement: usually a fade of the same length. Keep the state change itself (the item appears, the panel opens).
03Write the rule
Swap the animation name inside @media (prefers-reduced-motion: reduce), or write the movement only inside no-preference. Put the shared keyframes outside any query.
04Test the code that waits
Search your JavaScript for transitionend and animationend. Run the page with the Reduced motion switch on and check that panels still open, close and get removed.
Try it yourself
- 1
Switch to reduced and read the tracks
In "Reduce, don't remove", press Show message. Normally the card's track is
slide-inand at 0 ms its transform istranslateX(-40px). Switch on Reduced motion: the track isfade-in, the transform isnone, and opacity still runs 0 to 1 over 300 ms (0.67 at 60 ms, in both runs). The spinner's track changes fromspintopulse. - 2
Make panel B safe too
In "The reset that breaks transitionend", change
closeNaive(b)tocloseSafe(b). The second line becomesreset, naive code: removed(the label is fixed text): the duration reads0s, so the panel is removed at once.
Code & diagrams
The line under the spinner prints the computed animation names. Press Show message, then switch on the timeline's Reduced motion: `slide-in` becomes `fade-in` and `spin` becomes `pulse`. The text is the same.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Panels A and B wait for `transitionend`. Panel B has the blanket reset's `transition: none`, so the event never comes and it stays. Panel C checks the transition duration first.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
normal, naive code: removed
reset, naive code: still in the page
reset, safe code: removedBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Reverse the query
In "Reduce, don't remove", change @media (prefers-reduced-motion: reduce) to @media (prefers-reduced-motion: no-preference) and switch on Reduced motion.
Myth vs fact
Myth
Reduced motion means no animation.
Fact
It means less movement. Fades, colour changes and short state changes are welcome, and they carry the message that something changed.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
prefers-reduced-motiononly answers for the device. A visitor may want calm on your site only, or full motion despite the device setting. Topic 7.5 adds a site switch that sits above this query.
Remember this
- 1
The problem this solves: Topic 7.1 listed what makes people ill, and every phone and laptop has a switch where a person says "I need less movement". Your page does not know about that switch unless your CSS asks. A media query is a condition that wraps CSS rules, and
prefers-reduced-motionis the one that reads this setting. - 2
The two values.
no-preferencemeans the visitor has not asked for anything.reducemeans they switched the setting on. MDN notes that@media (prefers-reduced-motion)with no value meansreduce, and that the feature is Baseline widely available. The timeline's "Reduced motion" switch re-runs an example withreduceanswering true, so you can test without touching your device. - 3
Reduce, don't remove. The easy fix is to switch every animation off. That throws away the job the motion did: the toast no longer says "this just appeared", the panel no longer shows where it came from. The better fix keeps the change visible and removes the travel: a slide becomes a short fade, a spin becomes a gentle pulse, a bounce becomes nothing. Colour, opacity and size-free changes are the safe tools (Topic 7.1).
- 4
Two ways to write the rule. The opt-out pattern gives everyone the motion and a
reduceblock takes it away: the toast in Phase 4 swaps its animation names this way. The opt-in pattern writes the movement only insideno-preference: the press scale in Phase 3 works this way. Opt-in is safer for big movement, because anything you forget to list stays calm. Use opt-out for small swaps, where the base rule is already a fade. - 5
The blanket reset trap. A popular snippet puts
* { animation: none !important; transition: none !important; }in thereduceblock. Code that waits fortransitionendoranimationendthen waits forever, because nothing ran, and the panel is never removed. Ask whether a transition will run before waiting (ascloseSafedoes), or use a tiny duration such as0.01ms, which still fires the events (measured here). - 6
Essential animation. Some motion is information, such as progress. Keep it, calmer: the spinner below pulses its opacity instead of spinning, and the words "Loading the Volt…" stay. WCAG counts motion as essential only when nothing else could give the same information (Topic 7.4).
- 7
Where people turn it on (MDN's list): Windows 11 Settings, Accessibility, Visual Effects, Animation effects; macOS Settings, Accessibility, Display, Reduce motion; iOS Settings, Accessibility, Motion; Android 9+ Settings, Accessibility, Remove animations; GNOME Settings, Accessibility, Seeing, Reduced animation. Menu names change between versions. Chrome DevTools' Rendering tab can emulate it.
Explain it without notes
What is the difference between reduce and no-preference?
Why is "reduce, don't remove" better than switching all animation off?
Practice
Write the rule that makes .spinner pulse instead of spin for reduced-motion users.
Write a rule that gives a dialog a slide-and-fade entrance for everyone and only a fade for reduced-motion users, using the opt-out pattern.
Done when you can
You can read both values and say who each matches
You swap movement for a fade instead of deleting the animation
You know why a blanket reset breaks
transitionendcodeYou can say where users switch the setting on