motion
Moving One Element
The default is no animation. An element earns motion only by answering a question the user is already asking — where did this come from, did my press register, what just changed — and if it answers none of them, ship it static. When motion is earned, the default shape is a CSS transition on transform and opacity, ease-out, under 300ms, with a prefers-reduced-motion path written in the same commit rather than a follow-up. This skill governs one element on its own clock: the moment a second element has to coordinate with the first — stagger, a shared element, a route change — that is transitions, and the moment the clock is a finger rather than a timer, that is gestures.
Read the project's motion system before writing a line. Grep for --ease, cubic-bezier, @theme, transition-timing-function, motion/framer-motion in package.json, useSpring, animate(, or a tokens/theme file. Whatever declares timing already wins: write your change in that mechanism, reuse its named curves and durations, and never introduce a second animation layer to fix one element. If the project ships a curve you disagree with, say so as a finding — do not quietly ship a competing one.
Quick Reference
| When | Open |
|---|---|
| Before writing a modal, drawer, popover, dropdown, tooltip, toast, or accordion from scratch — each has an exact recipe you should not re-derive | patterns.md |
| When the default curve set is not enough, a crossfade looks wrong, you need JS control over a CSS-quality animation, or you must pick the project's house curve family | techniques.md |
Framework 1 — should this animate at all?
Gate on frequency first. This is not a judgment call.