Foundations

Motion

7 duration tokens and 3 easing curves. Motion here explains a change: something opened, something loaded. It’s functional by default, and the components drop their animation under prefers-reduced-motion.

Demo

Press Play to run a bar over each duration. With reduced motion on, the bars jump to the end instead.

  • --duration-interaction200ms · Hover, press and focus changes on controls.
  • --duration-reveal-delay300ms · Stagger delay for elements that follow a reveal into view
  • --duration-skeleton400ms · Length of one Skeleton pulse.
  • --duration-transition400ms · Overlays and messages entering or leaving: dialogs, drawers, toasts, dismissing an alert.
  • --duration-reveal400ms · Content fading and sliding into view as it scrolls in.
  • --duration-entrance600ms · Larger entrance animations on page content, slower than duration-transition.
  • --duration-spin700ms · One full turn of the Spinner.
  • --easing-default
    cubic-bezier(0.4, 0, 0.2, 1)
  • --easing-in
    cubic-bezier(0.4, 0, 1, 1)
  • --easing-out
    cubic-bezier(0, 0, 0.2, 1)

Tokens

TokenValueUsed by
--duration-skeleton400msSkeleton
--duration-spin700msSpinner
--duration-interaction200msAlert, Card, Dialog, Drawer and 18 more
--duration-transition400msAlert, Dialog, Drawer, Toast
--duration-entrance600ms—
--duration-reveal400ms—
--duration-reveal-delay300ms—
TokenValue
--easing-defaultcubic-bezier(0.4, 0, 0.2, 1)
--easing-incubic-bezier(0.4, 0, 1, 1)
--easing-outcubic-bezier(0, 0, 0.2, 1)

Usage

css
.panel {
  transition: opacity var(--duration-transition) var(--easing-default);
}

.panel[data-state='open'] {
  animation: panel-in var(--duration-entrance) var(--easing-out);
}

@media (prefers-reduced-motion: reduce) {
  .panel[data-state='open'] {
    animation: none;
  }
}

Do and don’t

Do

  • Use the duration that matches the job: interaction for hover, entrance for a dialog opening.
  • Ease out for things arriving and ease in for things leaving.
  • Give every animation a reduced-motion rule.

Don’t

  • Write transition: 200ms. The linter rejects hardcoded motion in component CSS.
  • Animate for decoration. If nothing changed, nothing should move.
  • Make people wait on an animation before they can act.