Free tool - no signup
Reduced Motion Preview
Paste a transition or an animation. The tool runs it twice, side by
side, as it behaves under no-preference and under
reduce, and prints the media block that turns the second
into a designed state rather than a deleted one. You never touch your
system settings, and nothing leaves your browser.
Only the transition and animation are read; every other declaration in the box is ignored for the preview.
Your system is set to reduce motion. The stage on the left still plays when you press Play, because showing the full-motion version on request is what this tool is for. Nothing here moves on its own.
no-preference full motionreduce the reduced design- Kept: opacity. Taken out: transform.
- Place this block after your own rule. Same specificity, later in the file, so it wins.
How to use it
- Paste a rule with a transition or an animation into the box. If it uses keyframes, paste the
@keyframesblock too. The examples above load three typical cases. - Pick the reduced design: swap, opt-in, or the blanket override for comparison.
- Press Play. Both stages run at once. Ask one question of the right-hand one: can you still tell that something happened?
- Copy the block under the stages. For swap, paste it after your own rule. For opt-in, replace your rule with the two it prints.
The three designs
Swap keeps what carries meaning and removes what moves.
The opacity change stays, the transform is pinned to none,
and the block sits after your rule so the later declaration wins.
Opt-in reaches the same two states from the other end.
The base rule is the calm one, and the travel comes back only inside
@media (prefers-reduced-motion: no-preference).
Blanket is the override many resets ship, with a
0.01ms duration on everything. It is here so you can watch what it does
to the fade. It is the one to avoid.
Why 0.01ms and not none? An animation set to none never
exists, so it never ends, and a script waiting for
animationend waits forever. A near-zero duration keeps the
event firing. That detail is why the blanket block is well made and the
design is still wrong: it removes every state change, including the ones
nobody asked to lose.
What counts as motion here
The tool draws one line. Properties that move or turn an element are
taken out under reduce: transform, translate,
rotate, scale, the offset family,
top, left and their siblings, margins and
background-position. Opacity, color and the like stay,
because a fade is feedback, not travel. It is a rule of thumb that
matches how the setting is described, not a WCAG definition, and it will
be wrong for the occasional effect. Read the notes under the output for
anything the tool could not decide for you.
Animations get the same treatment one level down. When a keyframes block mixes movement and a fade, the tool writes a second block with the movement stripped and points the reduced rule at it. When it only moves things, the reduced rule drops it. A looping animation plays once under reduce, because anything that keeps going on its own is what the setting is meant to stop.
Checked in a browser
The blocks the tool prints were run in Chromium 151 with real
prefers-reduced-motion emulation, on 2026-10-16, as a
separate test from the live stages. For the panel in the first example,
80ms after the state change the opacity read 0.59 under both settings.
The transform was 4px from its end position under
no-preference and absent under reduce. The
blanket block had the opacity at 1 at the first sample, 20ms in. The swap
and opt-in blocks behaved the same as each other under both settings,
for a transition and for the card-pop animation. Only Chromium was run. Firefox and
Safari were not.
Common questions
- Why does the preview switch with an attribute and not with the media query?
- A stylesheet cannot flip its own media features, and two stages that have to show both settings at once cannot share one. Each stage carries a data attribute instead, and the tool rewrites the generated @media blocks into selectors on that attribute. The text you copy keeps the real media query. Both come from the same plan, so what the stage runs is what you paste.
- Should I ship the swap or the opt-in form?
- Opt-in if you are writing the component now, swap if you are retrofitting one that already ships. They produce the same two states. The difference is the failure: forget the block under swap and everyone gets the full travel, forget it under opt-in and everyone gets the calm version. A component that is too still gets noticed in review. One that is too busy gets noticed by the people it hurts.
- Why does the swap block set transform to none instead of only dropping it from the transition?
- Because the offset is still there. If the transition list loses transform but the base state keeps translateY(24px), the panel snaps 24px in one frame and then fades, which is worse than either design. Pinning transform, translate, scale or rotate to none under reduce removes the offset itself. Properties such as top or margin-left have no none to pin, so the tool lists them in the notes.
- Does the tool send my CSS anywhere?
- No. Parsing, the live stages and the generated block all run in your browser tab. There is no request, no account and nothing stored. The preview also never pastes your text into the page: it reads the transition and the animation, rebuilds them from tokens, and drops every other declaration.
- What does it leave out?
- Scroll-driven animation, view transitions and anything driven from JavaScript. animation-timeline and animation-range are copied through untouched, because whether a scroll-linked effect should survive reduce is a per-effect decision. The preview animates the first rule that carries a transition or animation, and shows opacity, transform, translate, scale, rotate, background-color and filter on its demo box.
The reasoning behind the patterns, including scroll-driven effects, view transitions and video, is in Reduce Motion, Not Emotion. To choose the curves you are about to reduce, start with the guide to easing curves, and to see an effect run before you commit to it, use the live CSS preview. Staggered entrances need their own check, which the sibling-index() stagger piece covers, and the stagger calculator sets the delay between items. A scroll-driven effect is worth the same look, and the scroll timeline playground shows which scroller moves it.