A complete, implementation-ready motion foundation. Every animation resolves to one duration token × one easing token — nothing else. Compose these two tokens for every transition in the product so movement stays consistent and predictable, and no one hand-picks a magic number.
- Animate
transformandopacityonly — neverwidth/height/top/left/margin(layout thrash → dropped frames). For accordions animategrid-template-rows: 0fr → 1fr. - Exits run at ~75% of their entrance duration.
- Never use the default
ease. Pick a curve for the direction (out = entering, in = leaving, in-out = toggles). - No bounce, no elastic. Real objects decelerate smoothly; they don't spring back.
- Always respect
prefers-reduced-motion. - Motion must point at meaning: what changed, what's next. If it doesn't clarify, cut it.
1 · Duration tokens
Six steps, coarse on purpose. Rule of thumb: the more screen area moves, the longer the duration. Press Play to feel each token — every bead travels the same distance.
| Token | Value | Use for |
|---|---|---|
| --motion-instant | 100ms | Button press, toggle, color change |
| --motion-fast | 160ms | Hover, small state change |
| --motion-moderate | 240ms | Menu open, tooltip, tab switch |
| --motion-slow | 360ms | Accordion, drawer, list item |
| --motion-deliberate | 560ms | Modal, dialog, panel |
| --motion-entrance | 720ms | Page load, hero reveal, count-up |
2 · Easing tokens
The curve carries the direction. Entrances decelerate (out), exits accelerate (in), toggles ease both ways (in-out). Press Play to run a bead along each curve.
| Token | Bezier | Use for |
|---|---|---|
| --ease-out-quart | cubic-bezier(0.25, 1, 0.5, 1) | Standard entrance, hover-in |
| --ease-out-quint | cubic-bezier(0.22, 1, 0.36, 1) | Sharper entrance: metrics, hero, emphasis |
| --ease-out-expo | cubic-bezier(0.16, 1, 0.3, 1) | Most decisive: modals, drawers |
| --ease-in-out | cubic-bezier(0.65, 0, 0.35, 1) | State toggles that move both ways |
| --ease-in | cubic-bezier(0.7, 0, 0.84, 0) | Elements leaving the screen entirely |
3 · CSS token block
Drop straight into :root. These exact tokens are live on this page.
4 · Pattern library
Each pattern is pinned to tokens. The demos below are live and implement the spec exactly.
5 · Custom — animation gallery
A grid of self-contained custom animations, one per cell. The homepage hero leads; below are four cursor-reactive canvas backgrounds — Sphere Lines, Particle Sphere, Grid Lines, Wave Lines — ported to vanilla canvas and tokenised to the system (they follow the light/dark toggle). Hover any tile to interact. Config constants sit at the top of each so they can be tuned per brand.
- Globe spin & radar sweep → linear, infinite (constant speed reads mechanical — the same call as the §4.3 marquee).
- Node pulse & card float → ease-in-out, each card a slightly different period so they drift out of sync.
- Cards enter staggered → --motion-deliberate × --ease-out-expo, 90ms apart (the §4.5 pattern).
- Events counter → --motion-entrance × out-quint count-up, then ticks up live (the §4.1 + §4.4 patterns combined).
- Only
transformandopacityanimate; the whole stage honoursprefers-reduced-motion.
6 · Do / Don't
- Animate
transformandopacityonly (GPU-composited, skips layout) - Animate
width/height/top/left - Always reach for a token, never a raw number
- Default to
ease - Make exits faster than entrances (~75%)
- Use bounce or elastic curves
- Respect
prefers-reduced-motion - Run everything at once — stagger or sequence by importance
- Let motion point at meaning: what changed, what's next
- Animate for its own sake