
Expression
Motion
How things move, and the specs that keep it consistent.
Motion here is not decoration. It exists to explain where something came from and where it went. Two easings and three durations cover everything on the site — if a new animation needs a fourth, the animation is usually wrong.
Tokens
| Role | Value | Token |
|---|---|---|
| Enter / settle | cubic-bezier(0.22, 1, 0.36, 1) | --ease-enter |
| Hover / press | cubic-bezier(0.32, 0.72, 0, 1) | --ease-hover |
| Fast | 200ms | --duration-fast |
| Base | 400ms | --duration-base |
| Slow / scrub | 1100ms | --duration-slow |
The two easings
Enter settles like a physical object coming to rest. Hover is sharper, because a pointer response that takes 400ms feels broken.
Enter
cubic-bezier(0.22, 1, 0.36, 1)400ms
Hover
cubic-bezier(0.32, 0.72, 0, 1)200ms
Interruptible, always
Reduced motion
Under prefers-reduced-motion: reduce the site does not simply speed animations up — it removes the ones that move things through space and keeps only opacity. Autoplaying video does not start. This is honoured everywhere, including the brand portal you are reading.
Do: Animate opacity and transform only. Both are compositor-friendly.
Do: Start from the current value, not from a hardcoded origin.
Do: Keep hover and press feedback under 200ms.
Do: Let scroll reveals settle at 400ms, staggered by no more than 60ms.
Do not: Do not animate width, height, top or left. They force layout.
Do not: Do not bounce. Overshoot reads as playful; this brand is calm.
Do not: Do not loop an animation that has no reason to repeat.
Do not: Do not block interaction while something is animating.
Geometry
| Radius | Value | Token |
|---|---|---|
| Card | 12px | --radius-card |
| Button | 8px | --radius-button |
| Input | 8px | --radius-input |
| Pill | 9999px | --radius-pill |
| Space | Value | Token |
|---|---|---|
| xxs | 4px | --shrf-space-xxs |
| xs | 8px | --shrf-space-xs |
| sm | 12px | --shrf-space-sm |
| md | 16px | --shrf-space-md |
| lg | 24px | --shrf-space-lg |
| xl | 40px | --shrf-space-xl |
| 2xl | 64px | --shrf-space-2xl |
| 3xl | 96px | --shrf-space-3xl |
Why spacing is prefixed
--shrf-space-* rather than --spacing-* because Tailwind v4 reserves the latter for its own utility scale. Overriding it would silently redefine every max-w-* in the codebase.