Home / Snippets / Layout /

Duration and motion tokens

A duration scale plus easing curves as tokens, with a prefers-reduced-motion override that zeros all durations — accessible animation in one rule.

--dur-instant
50ms
--dur-fast
120ms
--dur-base
200ms
--dur-slow
350ms
--dur-slower
500ms

Hover a row to see the transition at that duration

Widely Supported
layouttokensanimationno-js

Quick implementation

:root {
  /* Duration scale */
  --dur-instant:  50ms;   /* state indicators, selection */
  --dur-fast:    120ms;   /* hover states, tooltips */
  --dur-base:    200ms;   /* most transitions */
  --dur-slow:    350ms;   /* panels, modals entering */
  --dur-slower:  500ms;   /* page transitions, hero elements */

  /* Easing curves */
  --ease-linear:   linear;
  --ease-out:      cubic-bezier(0.0, 0.0, 0.2, 1);   /* decelerate */
  --ease-in:       cubic-bezier(0.4, 0.0, 1.0, 1);   /* accelerate */
  --ease-in-out:   cubic-bezier(0.4, 0.0, 0.2, 1);   /* standard */
  --ease-spring:   cubic-bezier(0.34, 1.56, 0.64, 1);/* slight overshoot */
}

/* Accessibility: zero all durations for reduced-motion users.
   Using the tokens means this one block covers every animation. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-instant:  0ms;
    --dur-fast:     0ms;
    --dur-base:     0ms;
    --dur-slow:     0ms;
    --dur-slower:   0ms;
  }
}

/* Usage */
.btn {
  transition: background var(--dur-fast) var(--ease-out),
              box-shadow  var(--dur-base) var(--ease-out);
}

.modal {
  transition: opacity    var(--dur-base) var(--ease-in-out),
              transform  var(--dur-slow) var(--ease-out);
}

Prompt this to your LLM

Includes role, constraints, framework variants, and edge cases.

You are a senior frontend engineer building a motion token system
for a design system.

Goal: Create CSS custom property tokens for animation durations
and easing curves, with built-in reduced-motion support.

Duration scale:
- --dur-instant (50ms): state feedback (checkbox check, badge update)
- --dur-fast (120ms): hover states, tooltips
- --dur-base (200ms): most UI transitions
- --dur-slow (350ms): panels, drawers, modals entering
- --dur-slower (500ms): page transitions, large element animations

Easing tokens:
- --ease-out (decelerate): elements entering from off-screen
- --ease-in (accelerate): elements leaving to off-screen
- --ease-in-out (standard): toggling visible/hidden
- --ease-spring (slight overshoot): interactive/playful elements

Accessibility:
- Show the prefers-reduced-motion override that zeros all duration
  tokens — explain why this is better than writing individual
  @media (prefers-reduced-motion: reduce) blocks per component

Framework variant: Show how to expose these tokens as a JavaScript
object for use with Framer Motion or React Spring animations.

Return only CSS with inline comments.

Why token-based reduced-motion is better

The naive reduced-motion approach wraps each animation in its own media query: @media (prefers-reduced-motion: reduce) { .btn { transition: none } }. This doesn't scale — every animated component needs its own override. With duration tokens, @media (prefers-reduced-motion: reduce) { :root { --dur-base: 0ms } } zeros every animation in the system at once. Components don't need to know about reduced-motion; they just use var(--dur-base) and it becomes zero automatically for users who prefer it.

Choosing the right duration

UI response should feel instant below 100ms — users perceive it as direct manipulation. Between 100–300ms feels like smooth UI response. Above 300ms starts to feel slow unless the element is large (a full-screen modal entering) or the animation is the primary feedback (a loading state). The rule: transitions should be shorter than the user's attention span for that action. Hover states at 350ms feel sluggish — 120ms is correct. Full-panel slides at 120ms feel jarring — 350ms gives the eye time to track the motion.