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

---
title: Design Tokens
status: stable
owner: pavel
audience: [pavel, ai, engineer]
tldr: Token registry for the studio design system — each row carries a W3C DTCG type tag, semantic role, Tailwind utility, and mode parity.
canonical_at: openspec/brands/studio/design-tokens.md
---
# Design Tokens — capability spec

> Token registry for the studio design system. Each row carries a W3C DTCG `$type` tag. Source-of-truth for *values* is `packages/ui/src/styles/globals.css § @theme`; this file declares the *contract* (role, mapping, mode parity).
>
> **Sister files:** [`design-system.md`](design-system.md) (visual intent) · [`design-motion.md`](design-motion.md) (motion) · [`design-components.md`](design-components.md) (component intent) · [`ui.md`](ui.md) (implementation references).
>
> Authority: rules R1, R4, R8, R9, R11 from [`_methodology/design-system-convention.md`](../_methodology/design-system-convention.md).

---

## How to read this file

Every token row has six fields per the registry schema:

```
| Token name | $type | Semantic role | Tailwind utility | Mode parity | Source |
```

- `$type` — one of the DTCG closed list. The studio uses the **13 atomic types**: `color`, `dimension`, `fontFamily`, `fontWeight`, `fontSize`, `lineHeight`, `letterSpacing`, `duration`, `cubicBezier`, `shadow`, `opacity`, `gradient`, plus the three composites (`transition`, `typography`, `border`) which the studio **defers** — DTCG allows nesting, the studio has no current consumer that needs composites, so R11 only requires the atomic types be used correctly. Composites are tracked as `Q-DS-2`.
- `Mode parity` — `light + dark` when the token has different values per mode; `shared` when the same value applies in every mode; `shared (alias)` when the row is an alias; `dark only` for additive dark-mode aliases.
- `Source` — `globals.css § <block>`. Never inline OKLCH or px values here. (Exception: applied-delta sections — § Severity tokens, § Code surface tokens — preserve OKLCH cells from the source change as historical record.)

To find the value of a token, follow the Source pointer to `packages/ui/src/styles/globals.css`.

---

## Color tokens

Two variants — light is for public surfaces (landing, muse, snapshots); dark is for the internal studio workspace. The variant is chosen by surface intent (see [`design-system.md § Surface mapping`](design-system.md)), applied via `.dark` or `[data-theme="dark"]` on the root element.

### Brand-canonical palette

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--color-canvas` | color | Page background — warm ivory in light, near-black in dark | `bg-canvas` | light + dark | globals.css § Palette |
| `--color-ink` | color | Primary text colour | `text-ink` | light + dark | globals.css § Palette |
| `--color-ink-light` | color | Body text — secondary emphasis | `text-ink-light` | light + dark | globals.css § Palette |
| `--color-ink-muted` | color | Meta / captions | `text-ink-muted` | light + dark | globals.css § Palette |
| `--color-subtle` | color | Surface tint above canvas | `bg-subtle` | light + dark | globals.css § Palette |
| `--color-code-bg` | color | Inline-code pill background | `bg-code-bg` | light + dark | globals.css § Palette |
| `--color-grid` | color | Dividers, 0.5px–1px rules | `border-grid` | light + dark | globals.css § Palette |
| `--color-blueprint` | color | Cool muted blue — links, blueprint rules | `text-blueprint` / `bg-blueprint` | light + dark | globals.css § Palette |
| `--color-blueprint-light` | color | Blueprint hover state | `text-blueprint-light` | light + dark | globals.css § Palette |
| `--color-accent` | color | Terracotta — tactical accents, status dots | `text-accent` / `bg-accent` | light + dark | globals.css § Palette |
| `--color-accent-light` | color | Accent hover state | `text-accent-light` | light + dark | globals.css § Palette |

### Semantic aliases

Forward-looking depth axis (`--bg-N`) used by variant-portable primitives. Polarity differs per mode: light's lowest layer is page ivory; dark's lowest layer is the rail / deepest chrome.

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--fg-1` | color | Foreground emphasis 1 — maps to `--color-ink` | `text-fg-1` | shared (alias) | globals.css § Semantic aliases |
| `--fg-2` | color | Foreground emphasis 2 — maps to `--color-ink-light` | `text-fg-2` | shared (alias) | globals.css § Semantic aliases |
| `--fg-3` | color | Foreground emphasis 3 — maps to `--color-ink-muted` | `text-fg-3` | shared (alias) | globals.css § Semantic aliases |
| `--bg-1` | color | Lowest z-stack layer — page (light) / rail / deepest chrome (dark) | `bg-bg-1` | light + dark | globals.css § Semantic aliases |
| `--bg-2` | color | Middle layer — section / panel (light) / canvas (dark) | `bg-bg-2` | light + dark | globals.css § Semantic aliases |
| `--bg-3` | color | Raised layer — cards / modals. **Light value forward-looking; no current consumer (Q-DS-4).** | `bg-bg-3` | light + dark | globals.css § Semantic aliases |
| `--line-1` | color | 0.5–1px dividers — alias for `--color-grid` | `border-line-1` | shared (alias) | globals.css § Semantic aliases |
| `--line-2` | color | 2px rules — alias for `--color-blueprint` | `border-line-2` | shared (alias) | globals.css § Semantic aliases |
| `--tactical` | color | Tactical accents (brackets, status labels) — alias for `--color-accent` | `text-tactical` | shared (alias) | globals.css § Semantic aliases |

### Hard rules at the colour level

- No gradients — exception: structural single-tone background-pattern utilities on text-free decorative zones, per `D-DS-8` (`openspec/decisions.md`). **No images behind text — and no patterns behind text either; the `D-DS-8` exception never applies under text content.** Full pattern system: [`design-components.md § Background patterns`](design-components.md).
- No shadows. Elevation is expressed through surface contrast (canvas vs subtle) and corner brackets.
- Background is flat warm ivory (light) or flat near-black (dark) behind content. Always. (Decorative text-free zones may carry a `D-DS-8` pattern fill.)

The brand-level statement of these rules lives in [`design-system.md § Hard constraints`](design-system.md).

---

## Status tokens

Declared in `globals.css § Status taxonomy tokens`. Cross-ref: [`design-components.md § Chip family`](design-components.md) for the full `StatusKind` enum and pill anatomy.

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--status-live` | color | LIVE — active and deployed (terracotta) | `text-status-live` | shared (alias to `--primary`) | globals.css § Status |
| `--status-live-fg` | color | LIVE foreground for filled pills | `text-status-live-fg` | shared (alias) | globals.css § Status |
| `--status-progressing` | color | DEPLOYING / IN FLIGHT (cool blue) | `text-status-progressing` | shared (alias to `--accent`) | globals.css § Status |
| `--status-progressing-fg` | color | Progressing foreground | `text-status-progressing-fg` | shared (alias) | globals.css § Status |
| `--status-idle` | color | DRAFT / PENDING / WAITING | `text-status-idle` | shared (alias to `--muted-foreground`) | globals.css § Status |
| `--status-idle-fg` | color | Idle foreground | `text-status-idle-fg` | shared (alias) | globals.css § Status |
| `--status-archived` | color | ARCHIVED / SUPERSEDED | `text-status-archived` | shared (alias to `--muted-foreground`) | globals.css § Status |
| `--status-error` | color | ERROR / FAILED / REVOKED | `text-status-error` | shared (alias to `--destructive`) | globals.css § Status |
| `--status-error-fg` | color | Error foreground | `text-status-error-fg` | shared (alias) | globals.css § Status |
| `--status-embargo` | color | EMBARGO — restricted release | `text-status-embargo` | shared (alias to `--color-brand-accent`) | globals.css § Status |

Cross-ref: `archive/2026-05-03-enrich-status-taxonomy/` for the original derivation.

---

## Severity tokens (applied 2026-05-07 from change `add-forge-ui-task-detail`)

Four mode-aware tokens encode finding severity for the Forge UI. Defined as a **token group**, not a status pill role — they colorize severity dots inline without requiring a status-pill wrapper.

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--severity-critical` | color | `<FindingRow>` severity dot — `critical` | `text-severity-critical` / `bg-severity-critical` | light + dark | globals.css § Severity |
| `--severity-major` | color | `<FindingRow>` severity dot — `major` | `text-severity-major` | light + dark | globals.css § Severity |
| `--severity-minor` | color | `<FindingRow>` severity dot — `minor` | `text-severity-minor` | light + dark | globals.css § Severity |
| `--severity-info` | color | `<FindingRow>` severity dot — `info` | `text-severity-info` | light + dark | globals.css § Severity |

Reference OKLCH (preserved from source change as historical record): light values around `oklch(0.49 0.18 25)` / `oklch(0.62 0.14 65)` / `oklch(0.65 0.02 80)` / `oklch(0.55 0.01 80)`; dark counterparts shift lighter. Implementer matches against the existing studio palette to keep the family.

### Why a separate token group

Status pills have six existing visual roles that map to lifecycle, not severity. Forcing severity through the same vocabulary would couple two orthogonal concepts and break the rule that pill roles describe "what's happening", not "how bad is it". Severity tokens stay separate so that `<FindingRow>` colorises without needing a wrapper pill.

### Consumers

- `<FindingRow>` (`packages/ui/src/components/finding-row.tsx`) — primary consumer.
- `<PhasePipeline>` Phase 3 header — applies a faint critical tint when at least one open critical finding exists in the phase.

---

## Depth tokens

Declared in `globals.css § Surface depth tokens`. Cross-ref: `archive/2026-05-05-enrich-surface-density/` for the depth-axis rationale.

### Depth axis

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--bg-1` | color | Lowest depth — page (light) / rail (dark) | `bg-bg-1` | light + dark | globals.css § Depth |
| `--bg-2` | color | Mid depth — panel (light) / canvas (dark) | `bg-bg-2` | light + dark | globals.css § Depth |
| `--bg-3` | color | Raised depth — cards / modals | `bg-bg-3` | light + dark | globals.css § Depth |

Polarity per mode:

- **Light:** `--bg-1` = page ivory (lowest) → `--bg-3` = raised card (highest).
- **Dark:** `--bg-1` = rail / deepest chrome (lowest) → `--bg-3` = raised card (highest).

The light `--bg-3` is forward-looking — declared for variant-portable primitives, no current shipping consumer (Q-DS-4).

---

## Surface frozen (applied 2026-05-05 from change `enrich-surface-density`)

Declared in `globals.css § Surface frozen token`. Used by snapshots, archived canvases, locked artefacts, disabled surfaces.

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--surface-frozen` | color | Frozen surface tint — cooler than `--bg-2`, chroma reduced | `bg-surface-frozen` | light + dark | globals.css § Surface frozen |

**Frozen treatment convention:** wrap with `bg-surface-frozen data-surface-frozen`. Component-local CSS applies `opacity: 0.85` to `[data-surface-frozen] > *:not([data-tactical])`. Tactical motif elements (corner brackets, blueprint lines, status pills) use `data-tactical` to stay at full saturation. Frozen ≠ dead — the brand signal holds.

---

## Glyph tokens (applied 2026-05-05 from change `enrich-tactical-grammar`)

Declared in `globals.css § Letter-glyph tokens`. Variant-independent (same values in every mode).

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--glyph-mono-weight` | fontWeight | All glyph characters — 500 weight | (consumed via component CSS) | shared | globals.css § Glyph |
| `--glyph-bracket-thickness` | dimension | Bracket-style glyph weight `[X]` — matches TacticalFrame sm bracket | (consumed via component CSS) | shared | globals.css § Glyph |
| `--glyph-size-sm` | fontSize | PersonaBadge `size="sm"` — inline / dense rows (9px) | `text-glyph-sm` | shared | globals.css § Glyph |
| `--glyph-size-md` | fontSize | PersonaBadge `size="md"` (default) — standalone badges (11px) | `text-glyph-md` | shared | globals.css § Glyph |
| `--glyph-size-lg` | fontSize | Reserved (12px) — was used by `ModeRailItem` (deleted via `consolidate-studio-sidebar-with-brand`, RAP-197); kept for future Tier-3 chip primitives like `BrandMark variant="compact"` | `text-glyph-lg` | shared | globals.css § Glyph |

Glyph governance rules live in [`design-components.md § Iconography & glyphs`](design-components.md).

---

## Workspace semantic tokens (dark)

Three additive aliases layered on top of the dark palette. **Dark-mode only** — no light counterparts. Each token has a Tailwind 4 `@theme inline` mapping (`--color-canvas-dark`, etc.) so utilities like `bg-canvas-dark` work as first-class classes.

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--canvas-dark` | color | Workspace content area — alias for `--bg-2` (`var(--background)`) in dark | `bg-canvas-dark` | dark only | globals.css § Workspace semantic |
| `--ink-on-dark` | color | All workspace text on canvas — alias for `--foreground` in dark | `text-ink-on-dark` | dark only | globals.css § Workspace semantic |
| `--accent-on-dark` | color | KPI values, active-state highlights, focus rings on dark surfaces — alias for `--accent` in dark | `text-accent-on-dark` | dark only | globals.css § Workspace semantic |

`--rail-bg` and `--rail-active` previously sat in this group but were removed in `consolidate-studio-sidebar-with-brand` (RAP-197) along with the rail surface itself; remaining surface tints (e.g. artifact-panel footer, file-preview-pane footer) read directly from `--bg-1` now.

Code-surface primitives (DiffView, InlineShiki, Markdown) **do not** consume these tokens — they use mode-aware tokens only (see § Code surface tokens below).

---

## Motion tokens

Authoritative narrative documentation lives in [`design-motion.md`](design-motion.md) (duration scale, easing curves, motion modes, reduced-motion handling, choreography rules). The rows below are the **registry mirror** required by R11 — every token in the studio carries a `$type` from the closed list, including motion. Values are copied here for at-a-glance lookup; if they drift from `design-motion.md`, that file is the source of truth.

Two `$type` values are used: `duration` for time-valued tokens (and the `--duration-scalar` multiplier that gates them via `calc()`), and `cubicBezier` for the easing curves.

### Duration scale

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--duration-instant` | duration | `0ms` — no-op for `prefers-reduced-motion`; explicit "no animation" signal | (consumed via component CSS) | shared | globals.css § Motion |
| `--duration-fast` | duration | `100ms` — hover, color shifts, focus rings | (consumed via component CSS) | shared | globals.css § Motion |
| `--duration-normal` | duration | `200ms` — productive UI default: dropdowns, toggles, micro-transitions | (consumed via component CSS) | shared | globals.css § Motion |
| `--duration-slow` | duration | `300ms` — dialogs, sheets, multi-property transitions | (consumed via component CSS) | shared | globals.css § Motion |
| `--duration-slower` | duration | `500ms` — largest allowed productive UI duration; signature moments only | (consumed via component CSS) | shared | globals.css § Motion |
| `--dur-quick` | duration | `120ms` — legacy (RAP grandfathered): hover transitions on bracketed-card primitives | (consumed via component CSS) | shared | globals.css § Motion |
| `--dur-deploy` | duration | `320ms` — legacy: status pill fade-in inside the card-reveal sequence | (consumed via component CSS) | shared | globals.css § Motion |
| `--dur-slow` | duration | `560ms` — legacy: bracket deploy + blueprint line scaleX in the card-reveal sequence (above the 500ms productive ceiling — expressive grandfathered, see [`design-motion.md § Existing durations preserved`](design-motion.md)) | (consumed via component CSS) | shared | globals.css § Motion |
| `--duration-scalar` | duration | `1` — multiplier applied to every duration via `calc(var(--duration-*) * var(--duration-scalar))`; flips to `0` under `prefers-reduced-motion: reduce` | (consumed via component CSS) | shared | globals.css § Motion |

### Easing curves

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--ease-standard` | cubicBezier | `cubic-bezier(0.4, 0, 0.2, 1)` — element visible from start to end (expanding tile, sorting reorder) | (consumed via component CSS) | shared | globals.css § Motion |
| `--ease-out` | cubicBezier | `cubic-bezier(0, 0, 0.2, 1)` — entry: modal opens, dropdown appears, toggle on | (consumed via component CSS) | shared | globals.css § Motion |
| `--ease-in` | cubicBezier | `cubic-bezier(0.4, 0, 1, 1)` — exit: modal closes, toaster leaves, item dismissed | (consumed via component CSS) | shared | globals.css § Motion |
| `--ease-spring` | cubicBezier | `cubic-bezier(0.34, 1.56, 0.64, 1)` — expressive moments only; rare | (consumed via component CSS) | shared | globals.css § Motion |
| `--ease-deploy` | cubicBezier | `cubic-bezier(0.2, 0.7, 0.2, 1)` — legacy: bracket deploy + blueprint line in the card-reveal sequence | (consumed via component CSS) | shared | globals.css § Motion |
| `--ease-settle` | cubicBezier | `cubic-bezier(0.4, 0, 0.2, 1)` — legacy alias for `--ease-standard` (kept for legacy reference) | (consumed via component CSS) | shared | globals.css § Motion |

---

## Code surface tokens (applied 2026-05-08 from change `add-studio-code-display`)

Theme JSONs at `packages/ui/src/syntax/studio-{light,dark}.ts` trace the `globals.css` palette so syntax highlighting stays aligned. Hex values are derived from canonical OKLCH and frozen in the theme JSON; when the OKLCH source changes, the theme JSON gets a follow-up edit.

| Scope | Light source token | Dark source token | Hex used in theme JSON |
|---|---|---|---|
| keywords (`if`, `function`, `import`, …) | `--primary` `oklch(0.583 0.159 35)` | `--primary` `oklch(0.684 0.149 35)` | `#C75B3A` / `#E8795A` |
| identifiers, functions, types, numbers, booleans | `--accent` `oklch(0.588 0.105 250)` | `--accent` `oklch(0.687 0.094 245)` | `#4A7FB5` / `#6B9FD0` |
| strings | warmer muted-foreground tone | warmer muted-foreground tone | `#7A6F5C` / `#C4B89E` |
| comments | `--muted-foreground` `oklch(0.520 0.008 85)` | `--muted-foreground` `oklch(0.700 0.006 85)` | `#818078` / `#B3B0A8` |
| punctuation, operators | `--foreground` direct | `--foreground` direct | `#1C1C1E` / `#E8E6E0` |

OKLCH literals here are preserved as historical record from the source change. They are the *only* place in `design-tokens.md` where OKLCH appears outside the `Source` column — see R1 exception in `_methodology/design-system-convention.md § 8.2`.

### Mode-aware token rule (code surfaces)

Code surfaces never reference `--ink-on-dark`, `--canvas-dark`, `--accent-on-dark`, or any other one-mode-only token. The full list of allowed tokens for code surfaces:

- `--background` / `--foreground`
- `--primary` / `--primary-foreground`
- `--accent` / `--accent-foreground`
- `--destructive`
- `--muted` / `--muted-foreground`
- `--color-grid` (mode-aware via `--border`)
- `--font-mono` (no light/dark variants — fonts don't need them)

---

## Typography

Three families, self-hosted as variable woff2. No CDN font calls.

### Type ramp

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--text-h1` | fontSize | H1 — Inter 600, 32px / 1.2, `letter-spacing: -0.01em` | `text-h1` | shared | globals.css § Typography |
| `--text-h2` | fontSize | H2 — Inter 500, 24px / 1.3 | `text-h2` | shared | globals.css § Typography |
| `--text-h3` | fontSize | H3 — Inter 500, 18px / 1.4 | `text-h3` | shared | globals.css § Typography |
| `--text-display-xl` | fontSize | Editorial display XL — Lora 500, `clamp(34px, 8vw, 64px)` / 1.05, `letter-spacing: -0.015em`. Hero / closing only.[^display-xl] | `text-display-xl` | shared | globals.css § Typography |
| `--text-display` | fontSize | Editorial display — Lora 500, 48px / 1.1, `letter-spacing: -0.015em` | `text-display` | shared | globals.css § Typography |
| `--text-body` | fontSize | Body — Lora 400, 18px / 1.8 | `text-body` | shared | globals.css § Typography |
| `--text-body-sm` | fontSize | Body small — Lora 400, 16px / 1.7 | `text-body-sm` | shared | globals.css § Typography |
| `--text-code` | fontSize | Inline code — JetBrains Mono 14px / 1.6 | `text-code` | shared | globals.css § Typography |
| `--text-caption` | fontSize | Captions — Inter 500, 12px / 1.4, uppercase, `letter-spacing: 0.08em` | `text-caption` | shared | globals.css § Typography |
| `--text-meta` | fontSize | Meta — JetBrains Mono 12px / 1.4 | `text-meta` | shared | globals.css § Typography |
| `--text-micro` | fontSize | Micro label — JetBrains Mono 10px / 500, uppercase, `letter-spacing: 0.2em`, terracotta | `text-micro` | shared | globals.css § Typography |

[^display-xl]: `--text-display-xl` value is `clamp(34px, 8vw, 64px)` — the fluid clamp from `home/typography.md § Display-xl token clamp` (archive `2026-05-14-tighten-home-above-fold-mobile`), which bakes fluid scaling into the canonical token so Hero h1 and ClosingCTA h2 reflow mobile-to-desktop without per-site clamp wrapping. The corpus previously carried three incompatible definitions of this token (fixed `64px`, `clamp(34px, 8vw, 64px)`, `clamp(40px, 6vw, 64px)`); sibling change RAP-1165 `reconcile-openspec-corpus-drift` is the reconciling authority and ratifies `clamp(34px, 8vw, 64px)` as canonical. This file consumes that decision; it does not re-litigate the value.

### Font tokens

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--font-sans` | fontFamily | `"Inter", system-ui, -apple-system, sans-serif` | `font-sans` | shared | globals.css § Fonts |
| `--font-serif` | fontFamily | `"Lora", Georgia, "Times New Roman", serif` — body + display anchor | `font-serif` | shared | globals.css § Fonts |
| `--font-mono` | fontFamily | `"JetBrains Mono", "Fira Code", ui-monospace, monospace` — meta + code | `font-mono` | shared | globals.css § Fonts |
| `--font-display` | fontFamily | Alias for `--font-serif` | `font-display` | shared (alias) | globals.css § Fonts |

### Deck ramp

Variant-independent (declared in `:root, .dark`). For projector / slide-deck contexts. Placement at `§ Typography › Deck ramp` is ratified by `D-DS-5` (closed Q-DS-3).

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--text-display-deck` | fontSize | Title slides — 96px, Lora 500 / 1.05 | `text-display-deck` | shared | globals.css § Deck typography |
| `--text-h1-deck` | fontSize | Section heads — 64px, Inter 600 / 1.15 | `text-h1-deck` | shared | globals.css § Deck typography |
| `--text-h2-deck` | fontSize | Subsections — 48px, Inter 500 / 1.2 | `text-h2-deck` | shared | globals.css § Deck typography |
| `--text-body-deck` | fontSize | Body on projector — 24px, Lora 400 / 1.5 | `text-body-deck` | shared | globals.css § Deck typography |

### Casing rules

Token-adjacent rules — the *application* of typography. Owned at the brand level; full prose lives in [`design-system.md § Voice & casing`](design-system.md).

- **Micro labels** (STATUS, STACK, LANGUAGES, STAGE, ROUTE): `UPPERCASE` + `letter-spacing: 0.2em`, JetBrains Mono 10–12px, terracotta.
- **Caption labels** (category tags like `/build`, `/signal`): lowercase with leading `/`, mono, muted.
- **Headings**: sentence case. Inter 600 for UI, Lora 500 for editorial display.
- **Body**: sentence case, serif (Lora).
- **Code / meta**: lowercase, mono.

---

## Spacing

12-column grid, 24px base unit. Everything snaps to 24.

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--space-2xs` | dimension | 4px | `gap-2xs` / `p-2xs` | shared | globals.css § Spacing |
| `--space-xs` | dimension | 8px | `gap-xs` | shared | globals.css § Spacing |
| `--space-sm` | dimension | 16px | `gap-sm` | shared | globals.css § Spacing |
| `--space-md` | dimension | 24px (1 unit) — base grid snap | `gap-md` | shared | globals.css § Spacing |
| `--space-lg` | dimension | 48px (2 units) | `gap-lg` | shared | globals.css § Spacing |
| `--space-xl` | dimension | 72px (3 units) | `gap-xl` | shared | globals.css § Spacing |
| `--space-2xl` | dimension | 96px (4 units) | `gap-2xl` | shared | globals.css § Spacing |

### Layout dimensions

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--width-reading` | dimension | ~56ch character-based reading column — consumed by `<Container width="reading">` (RAP-211) | `max-w-[var(--width-reading)]` | shared | globals.css § Layout |
| `--width-content` | dimension | 720px reading column (legacy) | `max-w-content` | shared | globals.css § Layout |
| `--width-page` | dimension | 1200px page max — consumed by `<Container width="wide">` (RAP-211) | `max-w-[var(--width-page)]` | shared | globals.css § Layout |
| `--gutter-desktop` | dimension | 48px desktop gutter | `gutter-desktop` | shared | globals.css § Layout |
| `--gutter-mobile` | dimension | 24px mobile gutter | `gutter-mobile` | shared | globals.css § Layout |

---

## Borders & radius

**Radius: 0px everywhere.** Sharp corners. Sole exception: `3px` on inline code pills and small tags. **No shadows.** No `box-shadow` except `none`.

| Token | $type | Semantic role | Tailwind utility | Mode parity | Source |
|---|---|---|---|---|---|
| `--border-grid` | dimension | `0.5px solid var(--color-grid)` — default divider | `border-grid` | shared | globals.css § Borders |
| `--border-section` | dimension | `1px solid var(--color-grid)` — stronger section rule | `border-section` | shared | globals.css § Borders |
| `--line-blueprint` | dimension | `2px solid var(--color-blueprint)` — accent rule | `border-line-blueprint` | shared | globals.css § Borders |
| `--radius` | dimension | 0px — global radius | `rounded` | shared | globals.css § Radius |
| `--radius-pill` | dimension | 3px — code pills only | `rounded-pill` | shared | globals.css § Radius |
| `--bracket-size` | dimension | 12px — TacticalFrame corner-bracket size | (consumed via component CSS) | shared | globals.css § Tactical |
| `--bracket-weight` | dimension | 1px — TacticalFrame corner-bracket weight | (consumed via component CSS) | shared | globals.css § Tactical |

---

## Reconciled mapping (applied 2026-05-03 from change `reconcile-token-names`)

`packages/ui/src/styles/globals.css` carries shadcn-style names that pre-date the formalized brand system. The reconcile change **added** brand-canonical names alongside without removing shadcn names. Both schemes coexist; components reference brand-canonical names via `@theme inline` mappings.

| Brand-canonical (spec) | Implementation (`globals.css`) | Notes |
|---|---|---|
| `--color-accent` (terracotta) | `--primary` | Names are inverted at the `--accent` slot |
| `--color-blueprint` (cool blue) | `--accent` | See above |
| `--color-canvas` | `--background` | |
| `--color-ink` | `--foreground` | |
| `--color-subtle` | `--muted` / `--card` | Context-dependent |

This mapping is **not** marked superseded; the older "Token implementation notes" footer that called for collapsing the schemes was incorrect. Do not collapse to one scheme without a full token-rename pass — tracked as a separate code change in `improve-design-system-token-coherence` (see [`ui.md § Known gaps`](ui.md)).

---

## Resolution order

When `@theme inline` aliases delegate to other tokens, resolution order is:

1. **`@theme inline` mapping** — Tailwind utility → CSS variable name (e.g. `bg-canvas-dark` → `var(--color-canvas-dark)`).
2. **Alias declaration in `:root` / `.dark`** — `--color-canvas-dark: var(--canvas-dark)` (the alias).
3. **Underlying value** — `--canvas-dark: var(--bg-2)` → `var(--background)` in dark (the actual chain).

A consumer reaching `bg-canvas-dark` resolves: utility → `@theme inline` → alias → underlying. The chain is documented once here; per-token rows reference Source by `globals.css § <block>` rather than repeating the chain.

### Light-mode token strategy

The depth axis (`--bg-1` / `--bg-2` / `--bg-3`) ships symmetrically in both modes even though current light surfaces use only two densities. This is a deliberate decision (Option A, RAP-25): variant-portable primitives use `bg-bg-3` without per-mode code branches. The light `--bg-3` value is forward-looking — tunable without breaking any shipping pixel. It is kept declared per `D-DS-6` (closed Q-DS-4).

---

## A11y baseline (token level)

Per R9 — described once here; not repeated per component. The corpus-wide accessibility contract this baseline conforms to is [`design-system.md § 5 Accessibility contract`](design-system.md) — **WCAG 2.2 Level AA** (`D-DS-10`).

- **Contrast.** Every (foreground, background) token combination meets WCAG AA at body size and AAA at large text. `--color-ink-muted` is the lowest-emphasis foreground; `--color-canvas` is the lightest background. Audit dimension #5 in [`design-system-audit-protocol.md`](../_methodology/design-system-audit-protocol.md) re-checks this quarterly.
- **Focus ring.** All interactive primitives use `outline: 1px solid var(--color-blueprint); outline-offset: 2px` on `:focus-visible`. Owned by primitives; declared in `globals.css § Focus`.
- **Touch target.** Per `D-DS-9` (see [`design-system.md § 5.2`](design-system.md)): the corpus-wide floor is **24×24 CSS px**, per WCAG 2.2 SC 2.5.8 Target Size (Minimum, AA) — `--space-md` (24px) is the matching token. **44×44 CSS px** (SC 2.5.5 Enhanced, AAA) is an *elective* goal a surface spec MAY raise to; the corpus does not require it. This row supersedes the earlier hedge ("production primitives target 44×44px per WCAG 2.5.5 enhanced"), which is now stated as the `D-DS-9` reconciliation so the registry and `design-system.md § 5` agree.
- **Reduced motion.** All durations multiply by `--duration-scalar` (see [`design-motion.md § Reduced motion`](design-motion.md)).

---

## Pending

The following token groups are defined in their respective RAP changes but not yet implemented in `globals.css`. Back-fill this section when each change archives.

| Group | Defined in | Tokens |
|---|---|---|
| Persona accents | RAP-24 | `--persona-origin`, `--persona-founder`, `--persona-architect`, `--persona-current` |

---

## Cross-references

- [`design-system.md`](design-system.md) — visual intent; the WCAG 2.2 AA accessibility contract this file's A11y baseline conforms to.
- [`design-motion.md`](design-motion.md) — authoritative narrative for motion (duration scale, easing curves, motion modes, reduced-motion, choreography). Registry rows for `--duration-*`, `--dur-*`, `--ease-*`, `--duration-scalar` live in this file under § Motion tokens (R11 registry mirror).
- [`design-components.md`](design-components.md) — every component row carries a `token map` cell pointing back into this file.
- [`ui.md`](ui.md) — implementation references; `path:line` anchors into `globals.css` and `packages/ui/src/components/`.
- [`_methodology/design-system-convention.md`](../_methodology/design-system-convention.md) — rules R1, R4, R8, R9, R11 govern this file.

---

> **Provenance.** This file consolidates the studio token registry into its convention-ratified `openspec/current/` home. The registry was previously authored to the convention's schema under `openspec/brands/studio/design-tokens.md`; that file remains the source of the applied-delta history (severity tokens, code-surface tokens, spacing-scale shipped, token-coherence, editorial-recession namespace). Whether `brands/studio/design-tokens.md` is later retired in favour of this file is registered as `Q-DS-9` and is out of scope here. No token value is changed by this consolidation — the single value stated differently from the brand-scoped source, `--text-display-xl`, is stated to *match* the canonical reconciliation (RAP-1165), not to introduce drift; see the footnote on the type-ramp row.
