Reduced motion
减少动态效果 · reduced-motionHonor the system preference by removing spatial movement while keeping a short fade as feedback.
Turning off every animation can obscure a state change, while slowing everything down still leaves large spatial movement. A useful split is to make translation and scale immediate, then let opacity change briefly. The demo simulates this preference without changing the OS setting.
Formula
if reducedMotion: positional properties → instant opacity → short fade else: position + scale + opacity → full transition
Parameters
| This page | Mature libraries | |
|---|---|---|
| position | instant in simulated reduced mode | Motion: positional keys use type: false |
| opacity | 180 ms | Motion: retains its configured transition |
| preference | prefers-reduced-motion | — |
Doing it well
- Separate spatial change from opacity. Motion makes positional properties instant under reduced motion while opacity and colour can still animate.motion/packages/motion-dom/src/animation/interfaces/visual-element-target.ts:135–145; motion/packages/motion-dom/src/render/utils/keys-position.ts
- Read the system preference and respond if it changes while the page is open.www-motion/rewrite/lib/motion-kit.js:316
- The still state must communicate whether the panel is open or closed; motion should not carry the only signal.motion/packages/motion-dom/src/animation/interfaces/visual-element-target.ts:135–145
In our cases
15 studies explicitly handle reduced motion. The quality records flag 65 studies lacking it. The dimension and morph rewrites read the system preference, suggesting this policy belongs in the shared runtime as well as individual effects.
65 / 69 cases · no or partial reduced-motion handlingFrom Codex per-case quality flags (keyword-clustered, approximate)
Used in 15 cases
Reference implementation · basics.js
/** Respect the OS setting, and react when it changes mid-session. */
export function reducedMotion(onChange) {
const mq = matchMedia('(prefers-reduced-motion: reduce)');
if (onChange) mq.addEventListener('change', e => onChange(e.matches));
return mq.matches;
}