⚠️ Design system CSS not found. Check the path in cms/docs.config.js → designSystemPath, then re-run npm run docgen.

Motion

The motion tokens that govern every transition, animation, and micro-interaction

Design System / Motion
Download .md file
Open .md in new tab

Motion tokens define timing and rhythm, not intent. They are reused for page transitions, component animations, and micro-interactions depending on context.

The system is built in two layers, the same model as color and spacing: primitives (raw easings and durations, named on the same t-shirt scale as --space-* and --font-*) → semantic tokens (named by intent, e.g. --motion-page-open). Always use semantic tokens in components — never hardcode cubic-bezier() or millisecond values inline.

The philosophy: durations follow the same t-shirt scale as spacing and type. Easings use the standard CSS keyword names (in, out, in-out) with refined cubic-bezier values. Zero new vocabulary to memorise — if you already know how --space-m works, you already know how --duration-m works.


Easing Primitives

Three primitives, named after the standard CSS easing keywords. The cubic-bezier values are our refined replacements for the browser defaults.

Token Value Use for
cubic-bezier(0.4, 0, 1, 1) Exits. Fast start, slow end. The element accelerates away.
cubic-bezier(0.16, 1, 0.3, 1) Entrances. Slow start, slow end with a long graceful tail. The element settles into place.
cubic-bezier(0.65, 0, 0.35, 1) Swaps and continuous motion. Symmetric. Reads as a single sweep.

ease-in

ease-out

ease-in-out

transition-timing-function: var(--ease-out);

Duration Primitives

Six durations on the same t-shirt scale as --space-*, --font-*, and --radius-*. Extendable in either direction (2xs, 2xl, etc.) when a new use case demands it.

Token Value Use for
100ms Hovers, presses, tooltips appearing
200ms Small UI feedback (toasts, focus rings, button state)
400ms Element-level transitions (dropdowns, expanding rows, fade swaps)
600ms Surface-level transitions (drawers, modals, page swaps)
800ms Page-level openings (full-area entrances)
1200ms Hero motion, deliberate reveals, intro animations

2xs · 100ms

xs · 200ms

s · 400ms

m · 600ms

l · 800ms

xl · 1200ms

transition-duration: var(--duration-m);

Semantic Motion Tokens

Named by intent. These compose primitives. Components and product code only ever read the semantic layer — never the primitives directly.

In v1 only page-level semantic tokens exist, because that's the first consumer (studio's Barba page transitions). Element-, surface-, and feedback-level tokens will be added when a real component needs them.

Semantic token Composes Used for
+ A page rising into view (slide-up). 800ms with a long graceful tail.
+ A page falling away (slide-down). 600ms — slightly faster than open, because the user has already decided to leave.
+ Sibling-page navigation (the conveyor). Single continuous sweep.
+ Crossfade fallback when no direction is known.

Each semantic token is stored as two CSS variables — one for duration, one for easing — so consumers can plug them into either CSS shorthand (transition) or JS animation APIs (element.animate(keyframes, { duration, easing })) without parsing.

/* The four page tokens, as declared in design-system.css */
--motion-page-open-duration:  var(--duration-l);
--motion-page-open-easing:    var(--ease-out);

--motion-page-close-duration: var(--duration-m);
--motion-page-close-easing:   var(--ease-in-out);

--motion-page-swap-duration:  var(--duration-m);
--motion-page-swap-easing:    var(--ease-in-out);

--motion-page-fade-duration:  var(--duration-s);
--motion-page-fade-easing:    var(--ease-in-out);

Naming Convention

When a new consumer needs a new semantic motion token, follow this pattern:

--motion-{scope}-{event}-{property}
Slot Allowed values
scope page · surface · element · feedback
event open · close · swap · enter · exit · fade · hover · press
property duration · easing

Examples (not yet defined — to be added when first needed):

  • --motion-surface-open-duration — drawers, modals, the contact overlay
  • --motion-element-hover-duration — buttons, links, hover states
  • --motion-feedback-toast-duration — toast notifications appearing

The semantic name should describe what kind of motion event this is, not which component triggers it. A token called --motion-button-hover would be wrong — many things hover, not just buttons. --motion-element-hover is right.


Usage in CSS

Reference the duration and easing variables separately, the same way transition-duration and transition-timing-function already split in standard CSS:

.button {
  transition-property: background, color, border-color;
  transition-duration: var(--duration-2xs);
  transition-timing-function: var(--ease-out);
}

.dialog {
  transition-property: opacity, transform;
  transition-duration: var(--motion-page-fade-duration);
  transition-timing-function: var(--motion-page-fade-easing);
}

For component code, prefer the semantic token (--motion-*) when one exists. Fall back to the primitives (--duration-*, --ease-*) for genuinely new motion events that don't have a semantic name yet — but consider whether a new semantic token should be added first.


Usage in JavaScript

Read the tokens once at module load via getComputedStyle, then use them with the Web Animations API:

function readToken(name) {
  return getComputedStyle(document.documentElement)
    .getPropertyValue(name).trim();
}

const MOTION = {
  pageOpen: {
    duration: parseInt(readToken("--motion-page-open-duration"), 10),
    easing:   readToken("--motion-page-open-easing"),
  },
};

element.animate(
  [{ opacity: 0 }, { opacity: 1 }],
  { duration: MOTION.pageOpen.duration, easing: MOTION.pageOpen.easing }
);

This is the pattern used by studio/assets/js/studio-barba.js for page transitions.


Reduced Motion

The system does not define a separate set of tokens for prefers-reduced-motion users. Instead, individual consumers should check the user preference at animation time and either skip the animation or use --duration-2xs (effectively instant):

const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const duration = reducedMotion ? 0 : MOTION.pageOpen.duration;

This keeps the token surface small. If a future component needs more nuanced reduced-motion behaviour, add a --motion-{event}-reduced-duration variant alongside it.

On this page
  • Easing Primitives
  • Duration Primitives
  • Semantic Motion Tokens
  • Naming Convention
  • Usage in CSS
  • Usage in JavaScript
  • Reduced Motion
Previous

Icon

Next

Layout

Was this page helpful?

We use this feedback to improve our documentation.

Thanks for your feedback

Send feedback

© 2026 By Default