Motion & animation audit · Playbook
Motion rules — with provenance
The rules oricorio enforces, each tied to a primary source and the date it was last checked
against that source. RULES in scripts/oricorio.mjs is the machine-readable copy of this table —
change both together.
Why provenance: an audit that can’t say where a threshold came from is folklore with a verdict
attached. Every row here names a first-party source (W3C/WAI, web.dev, Apple) and a
lastVerified date, so a stale rule is visible rather than silently authoritative.
motion-no-reduced-motion — serious
Fires when the page declares one or more animated rules and contains zero
@media (prefers-reduced-motion: reduce) blocks.
Vestibular disorders, migraine and general motion sensitivity make animation actively painful for a real slice of users. The OS-level preference exists precisely so they can ask for less; a page that declares motion and never checks it gives them no way out.
Fix: add a prefers-reduced-motion: reduce block that keeps opacity and colour transitions and
drops transform-based movement. Reduced motion means fewer and gentler, not zero — transitions
that aid comprehension should survive.
- Source: https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html
- Standard: WCAG 2.1 SC 2.3.3 Animation from Interactions (AAA)
- Last verified: 2026-08-05
motion-infinite — serious
Fires when a rule outside a reduced-motion block declares
animation-iteration-count: infinite.
Motion that starts automatically, lasts more than five seconds, and runs alongside other content must be pausable. An infinite animation by definition never stops on its own.
Fix: give the user a pause control, or stop the animation under prefers-reduced-motion. Note
that a spinner tied to a genuinely pending operation is usually defensible — this rule flags the
pattern for a human decision rather than asserting a definite failure.
- Source: https://www.w3.org/WAI/WCAG21/Understanding/pause-stop-hide.html
- Standard: WCAG 2.1 SC 2.2.2 Pause, Stop, Hide (A)
- Last verified: 2026-08-05
motion-layout-property — moderate
Fires when a transition or keyframe animates width, height, top, left, right,
bottom, or any margin/padding — or uses transition: all, which sweeps them up by accident.
These properties force layout and paint on every frame, on the main thread. transform and
opacity are handled by the compositor and skip both, which is the difference between a smooth
animation and a janky one on a mid-range phone.
Fix: re-express the motion as transform (translate/scale) and opacity. Replace
transition: all with an explicit property list.
- Source: https://web.dev/articles/animations-guide
- Standard: performance — not a WCAG failure
- Last verified: 2026-08-05
motion-long-duration — minor
Fires when a non-looping animation or transition exceeds 500ms.
Long transitions make an interface feel sluggish, because the user ends up waiting on the animation rather than on the system. Perceived speed drops even when actual speed is unchanged.
Exempt: infinite/looping motion (spinners, marquees, progress). Constant motion is legitimately long-running, and flagging a 1s linear spinner was a false positive caught while building this skill.
Fix: bring one-shot UI motion under ~300ms. 500ms is the outer bound this check enforces, chosen to flag only the clear offenders rather than to police taste.
- Source: https://developer.apple.com/design/human-interface-guidelines/motion
- Standard: advisory — house guidance
- Last verified: 2026-08-05
motion-indeterminate — incomplete
Fires when one or more stylesheets could not be read.
A cross-origin stylesheet throws SecurityError on .cssRules, so any motion it declares is
invisible to this audit. Reporting PASS in that situation would be a false clean bill of health, so
this finding forces the verdict to REVIEW.
Fix: serve the stylesheet same-origin, or add crossorigin plus Access-Control-Allow-Origin,
to make the audit complete. Otherwise read the result as partial — it is a gap in our coverage, not
a defect in the page.
- Source: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/cssRules
- Standard: coverage gap
- Last verified: 2026-08-05
Deliberately not checked
Rules that were considered and left out, so they don’t get re-proposed:
| Not checked | Why |
|---|---|
Easing direction (ease-in on entrances) |
Real guidance, but it grades taste rather than a defect, and a static read can’t tell an entrance from an exit reliably. |
scale(0) starts |
Same — a style opinion, not a failure. |
| Stagger delays | Needs intent to judge; a long delay may be deliberate. |
| JS-driven motion (WAAPI, inline styles set at runtime) | Not visible in the CSSOM at all. A genuine coverage limit, documented in SKILL.md rather than guessed at. |