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:

# any agent
npx skills add SoraLabsOSS/skills --skill motion-meaning
# or as a Claude Code plugin
/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:

The default prompt 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.
The audit prompt 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.
The gatekeeping prompt 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.