# Motion

> Motion is feedback, not decoration: four durations, two easings, and one rule — nothing moves that doesn't mean something.

## The scale

| Token | Value | Use |
| --- | --- | --- |
| `--motion-fast` | 120ms | Hover and press feedback. The change is small, so the response reads as instant. |
| `--motion-base` | 200ms | Reveals, toggles, small movement. The default. |
| `--motion-slow` | 300ms | Large surfaces: sheets, drawers, whole panels. Also the MOT-1 ceiling for interface motion. |
| `--motion-story` | 600ms | Narrative surfaces only — explanatory diagrams and the landing reveal. Never task UI. |

*(interactive element omitted — view it on the page)*

Duration follows the size of the change: the bigger the surface, the longer it
may take. If you mirror the tokens in JS for a motion library, keep both in
sync.

## Easing

| Token | Curve | Use |
| --- | --- | --- |
| `--ease-out` | `cubic-bezier(0.215, 0.61, 0.355, 1)` | Elements entering or exiting: reveals, dropdowns, tooltips. Starts fast, settles into place. |
| `--ease-in-out` | `cubic-bezier(0.645, 0.045, 0.355, 1)` | Elements already on screen that move or morph. |
| plain `ease` | browser default | Hover tint changes. |

No bounce or overshoot, ever (SLP-8).

## When not to animate

- Anything a person uses 100+ times a day gets no animation. Frequency beats
  delight: the fiftieth slide-in is friction, not polish.
- Keyboard navigation is always instant. Animation gives pointer interactions
  spatial feedback; a keyboard user already knows where they are going.
- Paired elements share one duration and one easing. A sheet and its backdrop
  move as one unit, or they read as two things.

## Reduced motion

Every animated surface provides a reduced-motion variant (A11Y-5). In
components that use motion/react, test `useReducedMotion() === true`: the strict
comparison means the null it returns before hydration never skips the
animation. In CSS, use the `motion-reduce:` utilities. Turning motion off may
remove emphasis, never meaning: states, order, and relationships stay legible
statically (MOT-3).

## The standards

- [MOT-1](/standards/catalog/mot-1) — interface motion is 100–300ms with
  standard easing; no decorative motion on critical paths.
- [MOT-2](/standards/catalog/mot-2) ⚑ — motion values come from this token
  set; durations and easings are never hardcoded in component code.
- [MOT-3](/standards/catalog/mot-3) ⚑ — motion may emphasise meaning but never
  carry it alone; the surface communicates the same information with
  animations off.

## Usage

```tsx
{/* CSS: duration and easing from tokens */}
<div className="transition-colors duration-(--motion-fast)" />

{/* motion/react, via a JS mirror of the CSS tokens */}
transition={{ duration: DUR.base, ease: EASE_OUT }}
```

## Best practices

- Do: Take durations and easings from the motion token set (MOT-2).
- Do: Keep interface motion at 300ms or less (MOT-1).
- Do: Give every animated surface a reduced-motion variant (A11Y-5).
- Don't: Hardcode a duration or easing in component code.
- Don't: Use bounce or overshoot easing (SLP-8).

## Related

### Agent skill

- `/dx-harness:dx-design-motion`: Reviews transitions, easing, timing, and reduced-motion behaviour, and proposes ranked fixes.

[All skills](/harness/skills)

### In the catalogue

- [MOT-1](/standards/catalog/mot-1) (L2): Interface motion is 100-300ms with standard easing; no decorative motion on critical paths
- [MOT-2](/standards/catalog/mot-2) (L2): Motion values come from the declared motion token set — durations and easings are never hardcoded in component code
- [MOT-3](/standards/catalog/mot-3) (L2): Motion may emphasise meaning but never carry it alone — an animated surface communicates the same information with animations off
- [A11Y-5](/standards/catalog/a11y-5) (L1): prefers-reduced-motion disables all non-essential animation
- [SLP-8](/standards/catalog/slp-8) (L1): No bounce or elastic easing on interface elements
