Motion
Duration and easing tokens — reach for the semantic aliases, and know that the numeric suffix is a step index rather than a millisecond value.
Motion tokens define how long a transition runs and how it accelerates. Three semantic duration aliases cover almost every case; the numeric primitives underneath them exist for the rare long-running animation.
The numeric suffix is a step index, not a millisecond value. --duration-400 is 500ms, not 400ms. The suffix tracks milliseconds up to --duration-300 and then stops. Reach for --duration-sm / --duration-md / --duration-lg instead — they name the intent and cannot be misread.
Semantic durations
The default choice. These carry the overwhelming majority of motion in BDS, and every component CSS file should reach for one of them first.
| Token | Resolves to | Use case |
|---|---|---|
--bds-duration-sm | 100ms | Micro-interactions — hover, focus |
--bds-duration-md | 200ms | Standard transitions — buttons, cards |
--bds-duration-lg | 300ms | Emphasis — modals, sheets, reveals |
--bds-duration-xl | 500ms | Cross-fades, long reveals — one rung past --bds-duration-lg |
Defined in tokens/gap-fills.css as aliases of the numeric primitives below. The step words follow the t-shirt ramp per ADR-033 § 3; the legacy fast / normal / slow are sm / md / lg grandfathered pending rename.
Periodic durations
A separate class from the transition tier above. These are not tween lengths — they are the period of a continuous loop or the dwell before a timed advance, so they live in seconds and carry a role name rather than a step (ADR-033 § Named exceptions, the same basis as --shadow-overlay). Reach for the semantic durations for any tween; only continuous or auto-advancing motion uses these.
| Token | Value | Use case |
|---|---|---|
--bds-duration-marquee | 55s | Full marquee loop period — logo tickers, continuous scroll |
--bds-duration-autoplay | 10s | Dwell before auto-advance — carousels, media tabs |
Defined in tokens/gap-fills.css. Measured from inspiration teardowns; re-run the Playwright capture before treating a value as canon later — vendor sites drift.
Numeric durations
The underlying primitive scale, generated from Figma. Use one of these only when no semantic alias fits — a long-running progress animation, for example.
| Token | Value | Suffix matches value |
|---|---|---|
--bds-duration-100 | 100ms | yes |
--bds-duration-200 | 200ms | yes |
--bds-duration-300 | 300ms | yes |
--bds-duration-400 | 500ms | no |
--bds-duration-500 | 800ms | no |
--bds-duration-600 | 1000ms | no |
The break at --duration-400 is the trap: it reads as 400ms and resolves to 500ms, so an animation lands 25% slower than intended and nothing errors. If you are reaching past --duration-300, read the value rather than the name.
Easing
Four curves, each with a distinct job. Pair --ease-out with most UI transitions — deceleration reads as responsive because the motion starts fast.
| Token | Value | Use case |
|---|---|---|
--bds-ease-out | cubic-bezier(0.16, 1, 0.3, 1) | Decelerate — most UI transitions |
--bds-ease-in | cubic-bezier(0.7, 0, 0.84, 0) | Accelerate — exits, collapses |
--bds-ease-in-out | cubic-bezier(0.65, 0, 0.35, 1) | Symmetric — looping, continuous |
--bds-ease-spring | cubic-bezier(0.34, 1.56, 0.64, 1) | Overshoot bounce — pop-in, badge appear |
--bds-ease-spring-soft | cubic-bezier(0, 1.08, 0.77, 1.01) | Gentle overshoot settle — media/panel entrance (softer than --bds-ease-spring) |
--bds-ease-linear | linear | Constant velocity — marquees, continuous loops |
Defined alongside the semantic durations in tokens/gap-fills.css.
Reach for --ease-out / --ease-in for ordinary decelerate / accelerate needs — there is deliberately no separate --ease-decel / --ease-accel. --ease-spring-soft is the one exception the house curves did not already cover: a settle that overshoots just past its target without the visible bounce of --ease-spring.
Stagger
Fixed offsets for sequencing a group of elements into view — apply an increasing stagger as animation-delay across siblings.
| Token | Value |
|---|---|
--stagger-1 | 0ms |
--stagger-2 | 50ms |
--stagger-3 | 100ms |
--stagger-4 | 150ms |
--stagger-5 | 200ms |
--stagger-6 | 250ms |
Stagger tokens ship in dist/bridge.css, not dist/tokens.css. They sit outside the canonical token registry, so a consumer that imports only the token bundle will not have them.
Where these live
Motion tokens are authored in three different places, which matters when you need to change one.
| Token group | Source | Editable |
|---|---|---|
--bds-duration-100 … --bds-duration-600 | Figma → design-tokens/tokens-studio.json → tokens/figma-tokens.css | Figma only — the CSS is auto-generated |
--bds-duration-sm / -normal / -slow, all --ease-* | tokens/gap-fills.css | Hand-authored |
--stagger-1 … --stagger-6 | tokens/bridge.css | Hand-authored |
Ready-made utility classes built on these tokens — .bds-enter-*, .bds-anim-* — live in tokens/motion-classes.css over the keyframes in tokens/animations.css. Prefer an existing class before writing a new animation.
Related
- Motion — tiers, effects, and when to escalate beyond CSS
- Interaction states — the states these transitions animate between