Motion meaning
Reduced motion isn't about removing animation. It's about deciding what remains when animation disappears.
Ask once, before writing or patching any reduce branch: Is the motion sequence the content, or is it decorating a result that exists either way? Communicative motion keeps the destination and changes how you arrive. Decorative motion can bail or collapse — no substitute required.
Grep for prefers-reduced-motion proves almost
nothing.
Touches ≠ reachable ≠ correct.
The active tab is already known — the pill travel is decoration, so under reduce it collapses (strategy 3): jump to the right spot, no slide. Toggle reduce, or flip your OS setting — the preference is live.
The four strategies
Pick one for what the screen becomes under reduce. Picture the outcome first:
| Strategy | Under reduce looks like |
|---|---|
| 1 · Bail | Effect is gone — no custom layer left behind |
| 2 · Snap | Same final layout as the last frame — no travel |
| 3 · Collapse | Control jumps to the right place — instant, not a slide |
| 4 · Reduce complexity | Same silhouette, cheaper — fewer layers, autoplay off |
Live preference is infrastructure, not a fifth look: subscribe to
change on
matchMedia("(prefers-reduced-motion: reduce)").
One-shot .matches at mount goes stale mid-session.
The four gates
Every animation under authorship or audit passes all four — verified by running both preference states, never by grepping alone:
- Role — Communicative or decorative? (sequence = content?)
- Hazard — Large viewport motion, parallax, spin, autoplay >5s, seizure flash?
- Reduce path — Which strategy (1–4)? Is preference live?
- Meaning — With reduce on, can the user still know the state / outcome?
What's in it
Classification rules (including the hard boundaries between snap vs collapse, and reduce-complexity vs bail), audit report shape, CSS / JS / React patterns, a failure catalog for dead reduce branches, and a Playwright verify harness that checks both preference states. Distilled from real Sora UI bugs — not a WCAG encyclopedia.
Install
Drop it into any coding agent — Claude Code, Cursor, Codex, and more:
npx skills add SoraLabsOSS/skills --skill motion-meaning
/plugin marketplace add SoraLabsOSS/skills
/plugin install motion-meaning@soralabs
Prompts that hold the line
The skill wakes up on reduce / audit asks — wording still decides
whether you get a strategy or a blanket duration: 0:
Before touching prefers-reduced-motion: is this sequence the content, or decorating a result that exists either way? Name the role, pick one reduce strategy (bail / snap / collapse / reduce complexity), wire a live preference listener, and implement the reduce branch inside the same setup that owns the full-motion path. Then verify both preference states.
Inventory every motion source on this page. For each: role, hazard, strategy 1–4, and whether preference is live. Report file:line, failure in one sentence, and the minimal fix. Do not rewrite unrelated UI. Re-verify both preference states after the patch.
Stop before setting duration: 0 everywhere. If the sequence is the content, snap to the end state — do not outer-gate the function that applies it. If motion only decorates a known result, collapse. If it's pure flourish, bail. Meaning must survive under reduce.
Strategies from Sora UI production patterns. Companion to animating-icons. MIT.