# Design Tokens

Concrete values implementing `01-design-principles.md`. These are the single source of truth — every other document in this phase and every screen built in Phase 5+ references these, not ad hoc values.

## Color

### Neutrals (the majority of the UI)

| Token | Value | Use |
|---|---|---|
| `color.neutral.0` | `#FFFFFF` | Page background, card surfaces |
| `color.neutral.50` | `#F7F7F8` | Subtle section backgrounds, hover states on neutral rows |
| `color.neutral.100` | `#EEEEF0` | Decorative borders, dividers (card edges, table row separators) — never the sole indicator of an interactive element's boundary |
| `color.neutral.300` | `#C7C7CE` | Disabled text, placeholder text only — exempt from WCAG contrast minimums (inactive UI components), see `06-open-questions.md` audit note |
| `color.neutral.400` | `#8F8F9B` | Interactive UI component borders (inputs, secondary buttons) — meets the 3:1 non-text contrast minimum against `neutral.0`; kept distinct from `neutral.300` specifically because that token is contrast-exempt and this one isn't |
| `color.neutral.500` | `#63636E` | Secondary/muted text (meets 4.5:1 against `neutral.0`) |
| `color.neutral.700` | `#4A4A56` | Body text |
| `color.neutral.900` | `#16161D` | Headings, primary text |

### Accent (used sparingly, per `01-design-principles.md` rule 1)

| Token | Value | Use |
|---|---|---|
| `color.accent.500` | `#4338CA` (deep indigo) | Primary buttons, active nav item, links, Store Health Score, focus rings |
| `color.accent.100` | `#E7E5FB` | Accent-tinted backgrounds (e.g. a selected row), used very sparingly |

Indigo is chosen over a brighter blue or a trend color (teal, purple gradients) because it reads as trustworthy and professional without feeling clinical — appropriate for a product merchants run their business on — and holds up over time rather than tracking a UI trend cycle (`01-design-principles.md` rule 7).

### Semantic (status only, never decoration)

| Token | Value | Use |
|---|---|---|
| `color.success.500` | `#15803D` | Positive status ("Active," "Delivered," health-score "good" signals) |
| `color.success.100` | `#DCFCE7` | Success status badge background — see `03-component-conventions.md` |
| `color.warning.500` | `#B45309` | Attention-needed status (low stock, a recommendation) |
| `color.warning.100` | `#FEF3C7` | Warning status badge background |
| `color.danger.500` | `#B91C1C` | Destructive actions, failed status, refunds |
| `color.danger.100` | `#FEE2E2` | Danger status badge background |

**Note:** these are the post-audit values — see the WCAG AA audit below. The badge/status use case (text at `type.caption`, 12px, on the corresponding `*.100` tinted background) is the binding constraint on all three; each value clears 4.5:1 against both its `*.100` background and against `neutral.0`/white, so the same token works whether it's rendered as badge text or as a standalone icon/text color elsewhere.

## Typography

**Typeface:** Inter (or the closest available equivalent in the chosen frontend stack), a neutral, highly-legible UI sans-serif that scales well from dense tables to large headings — with a system-font fallback stack (`-apple-system, Segoe UI, Roboto, sans-serif`) so the product never blocks on a webfont load.

**Scale** (three weights only, per `01-design-principles.md` rule 4 — Regular 400, Medium 500, Semibold 600; no Bold 700 except rare, deliberate emphasis):

| Token | Size / line-height | Weight | Use |
|---|---|---|---|
| `type.display` | 28px / 36px | Semibold | Page titles (e.g. "Products") |
| `type.heading` | 20px / 28px | Semibold | Section headings, card titles |
| `type.subheading` | 16px / 24px | Medium | Sub-sections, table headers |
| `type.body` | 14px / 22px | Regular | Default UI text, form labels, table cells |
| `type.small` | 13px / 18px | Regular | Secondary/muted text, timestamps, helper text |
| `type.caption` | 12px / 16px | Medium | Status badges, tags |

## Spacing

4px base unit (per `01-design-principles.md` rule 3, skewing generous):

| Token | Value |
|---|---|
| `space.1` | 4px |
| `space.2` | 8px |
| `space.3` | 12px |
| `space.4` | 16px |
| `space.6` | 24px |
| `space.8` | 32px |
| `space.12` | 48px |
| `space.16` | 64px |

Default component internal padding is `space.4` (16px); default gap between unrelated sections on a page is `space.8` (32px) or greater.

## Radius

| Token | Value | Use |
|---|---|---|
| `radius.sm` | 6px | Buttons, inputs, badges |
| `radius.md` | 8px | Cards, dropdowns |
| `radius.lg` | 12px | Modals, large containers |

Deliberately restrained — not the very sharp (0px, "cold") or very rounded (16px+, "playful") extremes, consistent with "professional" and "timeless" over trendy.

## Shadow (used only for genuine elevation, per `01-design-principles.md` rule 2)

| Token | Value | Use |
|---|---|---|
| `shadow.none` | none | Default cards, table rows, static page content — a 1px `color.neutral.100` border instead |
| `shadow.sm` | `0 1px 2px rgba(22,22,29,0.06)` | Dropdown menus, popovers |
| `shadow.md` | `0 4px 12px rgba(22,22,29,0.10)` | Modals, toasts |

No `shadow.lg` or higher — if something needs more visual separation than `shadow.md` provides, it's a sign the interaction shouldn't be a floating overlay at all.

## Motion

Base tokens; full application in `05-motion-and-microinteractions.md`.

| Token | Value |
|---|---|
| `motion.duration.fast` | 120ms |
| `motion.duration.default` | 180ms |
| `motion.duration.slow` | 240ms |
| `motion.easing.default` | `cubic-bezier(0.2, 0, 0, 1)` (ease-out) |

No spring/bounce/elastic easing anywhere — consistent with "calm" over "playful."

## WCAG AA contrast audit

Resolves `06-open-questions.md` #2. Every text/foreground-on-background pairing used in `03-component-conventions.md` was checked against WCAG 2.1's relative-luminance contrast formula (4.5:1 for normal text, 3:1 for large text and required non-text UI-component boundaries). Three failures were found in the first-draft values and corrected above; the table below is the final, passing state.

| Pairing | Ratio | Requirement | Result |
|---|---|---|---|
| `neutral.900` text on `neutral.0` | 18.0:1 | 4.5:1 | Pass |
| `neutral.700` body text on `neutral.0` | 8.7:1 | 4.5:1 | Pass |
| `neutral.500` muted text on `neutral.0` (revised from `#8A8A94`) | 5.9:1 | 4.5:1 | Pass (was 3.4:1 — failed) |
| `neutral.400` input/button border on `neutral.0` (new token, split out from `neutral.300`) | 3.2:1 | 3:1 | Pass |
| `neutral.300` disabled/placeholder text | — | exempt | WCAG exempts inactive UI components; not held to a ratio |
| White text on `accent.500` button | 7.9:1 | 4.5:1 | Pass |
| `accent.500` text/link on `neutral.0` | 7.9:1 | 4.5:1 | Pass |
| `accent.500` active-nav text on `accent.100` | 6.4:1 | 4.5:1 | Pass |
| `success.500` badge text on `success.100` (revised from `#16A34A`) | 4.6:1 | 4.5:1 | Pass (was 3.0:1 — failed) |
| `warning.500` badge text on `warning.100` (revised from `#D97706`) | 4.5:1 | 4.5:1 | Pass (was 2.9:1 — failed) |
| `danger.500` badge text on `danger.100` (revised from `#DC2626`) | 5.3:1 | 4.5:1 | Pass (was 4.0:1 — failed) |

**What changed and why:** the original `neutral.500`, `success.500`, `warning.500`, and `danger.500` values were chosen for visual balance against the palette and missed AA in their actual usage contexts (muted body text, and status-badge text on a tinted background respectively). All four were darkened until they cleared their binding constraint, without changing hue enough to break the "the same word/status always looks the same" consistency rule (`../ux/03-ux-patterns-and-conventions.md` rule 7) — these still read as the same green/amber/red/gray, just with enough contrast to be legible. `neutral.300` was split into two tokens (`neutral.300` for exempt disabled/placeholder text, new `neutral.400` for interactive borders that do require 3:1) because the original single token was being used for both purposes, and only one of those two purposes is contrast-exempt — `03-component-conventions.md` has been updated so inputs and secondary buttons use `neutral.400`, not `neutral.300`.
