<!-- source: openspec/current/design-motion.md -->

---
title: Design Motion
status: stable
owner: pavel
audience: [pavel, ai, engineer]
tldr: Motion spec for the studio design system — duration scale, easing curves, motion modes, the duration-scalar reduced-motion mechanism, and choreography rules.
canonical_at: openspec/brands/studio/design-motion.md
---
# Design Motion — capability spec

> Motion is a first-class category in the studio design system (per R12) — not a subsection of components. This file owns the duration scale, easing curves, motion modes, reduced-motion handling, and choreography rules. Component specs *use* these tokens; they never declare their own durations.
>
> **Sister files:** [`design-system.md`](design-system.md) (visual intent) · [`design-tokens.md`](design-tokens.md) (token registry) · [`design-components.md`](design-components.md) (component intent) · [`ui.md`](ui.md) (implementation references).
>
> Authority: rule R12 from [`_methodology/design-system-convention.md`](../_methodology/design-system-convention.md). The duration/easing token *values* also appear in [`design-tokens.md § Motion tokens`](design-tokens.md) as the R11 registry mirror — this file is the authoritative narrative; the registry carries the lookup mirror. If the two drift, this file wins.

---

## 1. Duration scale

A closed five-token scale. New motion picks a step from the scale; it does not invent a duration.

| Token | Value | Use |
|---|---|---|
| `--duration-instant` | `0ms` | No-op for `prefers-reduced-motion` reductions; explicit "no animation" signal |
| `--duration-fast` | `100ms` | Hover, color shifts, focus rings |
| `--duration-normal` | `200ms` | Default for productive UI: dropdowns, toggles, micro-transitions |
| `--duration-slow` | `300ms` | Dialogs, sheets, multi-property transitions |
| `--duration-slower` | `500ms` | Largest allowed UI duration; signature moments only |

**Anti-pattern:** any UI motion > 500ms. If something needs longer, it is not motion — it is animation (decorative, optional, separately governed).

## 2. Easing curves

Four curves. Every transition picks a named curve; per-component custom curves are forbidden.

| Token | Curve | Use |
|---|---|---|
| `--ease-standard` | `cubic-bezier(0.4, 0, 0.2, 1)` | Element visible from start to end of motion (expanding tile, sorting reorder) |
| `--ease-out` | `cubic-bezier(0, 0, 0.2, 1)` | Entry — modal opens, dropdown appears, toggle on |
| `--ease-in` | `cubic-bezier(0.4, 0, 1, 1)` | Exit — modal closes, toaster leaves, item dismissed |
| `--ease-spring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | Expressive moments only; rare |

**Anti-patterns:** bounce/stretch in productive mode; linear easing in any mode (looks robotic); per-component custom curves (use the scale, do not invent).

## 3. Motion modes

| Mode | Curves | Durations | Use |
|---|---|---|---|
| **productive** (default) | `--ease-standard`, `--ease-out`, `--ease-in` | `--duration-fast`, `--duration-normal` | Micro-interactions; >95% of motion |
| **expressive** | may use `--ease-spring` | `--duration-normal`, `--duration-slow` | Signature moments — page transitions, success states, brand reveals |

**Anti-pattern:** expressive easing on routine interactions (hover, dropdown). Noise, not signal.

The studio's existing **Card reveal sequence** (deploy-from-center brackets → blueprint line → content fade, total ~1200ms) is the canonical `expressive`-mode example. The 60ms stagger between bracketed cards is a brand-rhythm grandfather (see § 5). Hover color-shift transitions are `productive`.

The two-mode philosophy traces to the **IBM Carbon Design System — Motion** (productive / expressive), referenced for shape but not vendored.

## 4. Reduced motion

The studio honours `prefers-reduced-motion` through a single multiplier token, `--duration-scalar`, rather than per-component `@media` overrides.

```css
:root {
  --duration-scalar: 1;
}
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-scalar: 0;
  }
}
```

Every duration in code is wrapped in a `calc()`:

```css
transition-duration: calc(var(--duration-fast) * var(--duration-scalar));
```

- When the scalar is `1` (default), motion runs at its declared duration.
- When the scalar is `0` (`prefers-reduced-motion: reduce`), every duration collapses to instant — all motion disappears in one place, no per-component branch.
- When the scalar is `4` or `8` (dev override), motion runs in slow-motion for choreography debugging.

The `--duration-scalar` mechanism traces to the **Norton Design System** (`--duration-scalar` for both reduced-motion and dev slow-motion). It is registered in [`design-tokens.md § Motion tokens`](design-tokens.md) as a `duration`-typed token.

Implementation note: every keyframe animation in `packages/ui` also carries a `@media (prefers-reduced-motion: reduce)` block — see [`ui.md § Reduced-motion respect`](ui.md).

## 5. Choreography rules

- **Multiple elements animating together:** simultaneously OR staggered with `--stagger-base: 50ms`. The existing `60ms` stagger between bracketed cards is grandfathered as the studio's brand rhythm; new staggers use 50ms.
- **Element appears + disappears in the same gesture:** total visible time ≥ 200ms, otherwise the user does not register it.
- **Page transitions:** must include a continuous element (something present in both states). Otherwise the user loses spatial context.

## 6. Existing durations preserved

A set of legacy motion tokens predates the closed scale. They are **grandfathered** for the bracketed-card reveal sequence and are not removed; new motion uses the § 1 / § 2 scales.

| Token | $type | Value | Use |
|---|---|---|---|
| `--dur-quick` | duration | `120ms` | Legacy: hover transitions on bracketed-card primitives |
| `--dur-deploy` | duration | `320ms` | Legacy: status pill fade-in inside the card-reveal sequence |
| `--dur-slow` | duration | `560ms` | Legacy: bracket deploy + blueprint line scaleX in the card-reveal sequence |
| `--ease-deploy` | cubicBezier | `cubic-bezier(0.2, 0.7, 0.2, 1)` | Legacy: bracket deploy + blueprint line in the card-reveal sequence |
| `--ease-settle` | cubicBezier | `cubic-bezier(0.4, 0, 0.2, 1)` | Legacy alias for `--ease-standard` (kept for legacy reference) |

`--dur-slow` (`560ms`) sits **above the 500ms productive ceiling** from § 1. This is deliberate: the card-reveal sequence is `expressive` mode, and `--dur-slow` is the documented expressive exception — the one place a UI duration exceeds the ceiling, and only because the reveal is a signature brand moment. No new motion may use `--dur-slow`.

## 7. Anti-patterns

- Motion as decoration (no functional purpose).
- Motion that distracts from the primary task.
- UI event motion > 500ms (the `--dur-slow` expressive exception aside, § 6).
- Bounce / overshoot in productive mode.
- Parallel unsynchronized animations in one scene.

## Cross-references

- [`design-system.md`](design-system.md) — visual intent; motion is a brand voice, not just a mechanic.
- [`design-tokens.md`](design-tokens.md) — the R11 registry mirror of every `--duration-*`, `--dur-*`, `--ease-*`, and `--duration-scalar` token.
- [`design-components.md`](design-components.md) — component specs reference motion tokens; they never declare durations.
- [`ui.md`](ui.md) — implementation references; reduced-motion respect at the keyframe level.
- [`_methodology/design-system-convention.md`](../_methodology/design-system-convention.md) — rule R12 (motion is a first-class category); § 6 is the near-complete motion source this file consolidates.
