Design System
Overview
Define the visual + interaction foundation before features get built. Output is .forge/design-system.md — the semantic token layer (color, typography, spacing, radius, motion), the breakpoint and dark-mode rules, the primitive component inventory with all states, the gallery, the composition rules, and the documented anti-patterns. Consumed by every UI-shipping skill (incremental-implementation, interaction-patterns, accessibility, demo-narrative).
When to Use
- A new product or marketing surface is being designed and nothing exists yet
- The codebase has 20+ components with inconsistent spacing, radii, or color usage
- A redesign is starting and the team wants a foundation before screens get built
- Dark mode is being added and the existing palette has raw hex everywhere
- Two designers or two engineers are about to ship competing button styles
When NOT to Use
- A single one-off internal admin page with no future
- A backend-only service with no UI surface
- A tiny bug fix to one component — that's
incremental-implementation
Common Rationalizations
| Thought |
Reality |
| "We'll standardize later" |
Later means 50 components with 50 different spacing values. Standardization costs nothing on day one, costs a refactor on day 200. |
| "Just use Tailwind defaults" |
Defaults without a semantic mapping create implicit coupling to the framework. When the framework changes, every component breaks. |
| "Dark mode can wait" |
Retrofitting dark mode costs 5x building it in. Every color picked without dark-mode awareness becomes a contrast bug. |
| "Loading states aren't important" |
A component without a loading state is a component that lies about its readiness. The user sees a button that looks ready and isn't. |
| "Designer will fix it later" |
The component shipped to production is the design. Later means never. |
| "It looks fine in Figma" |
Figma is not the runtime. Real fonts render differently, real data overflows, real users have light/dark/HC modes. |
Red Flags
- A hex code (
#3B82F6) anywhere in a component file
- A spacing value not from the scale (
padding: 13px)
- A component without a
disabled or loading state
- Inconsistent
border-radius across two buttons in the same product
- A color used to convey meaning with contrast below WCAG AA
- A component that "works" in light mode and breaks in dark
- A skeleton screen on one page and a spinner on the next
- A button that has hover but no focus-visible outline
Core Process
Step 1: Define the brand foundation
Pin down before tokens:
- Brand voice (formal / friendly / technical / playful) — informs typography choice and motion timing
- One accent color (the brand color)
- One neutral scale (warm / cool / true gray)
- Density target (compact / comfortable / spacious)
Step 2: Write the semantic token layer
Two levels:
Reference tokens → Semantic tokens
gray-50, gray-100, ... → surface, surface-muted, border, text, text-muted
blue-500, blue-600, ... → accent, accent-hover, accent-active
red-500 → danger, danger-bg
Components use only semantic tokens. Never reference tokens directly. Dark mode is a remapping of semantic → reference tokens, not a rewrite.
Document every semantic token with: light hex, dark hex, WCAG contrast against its default partner, intended use.
Step 3: Define the scales
- Typography: 6-7 sizes (caption / body / body-lg / heading-sm / heading / heading-lg / display). Line height per size. Font family + weight roles (regular / medium / semibold).
- Spacing: 4px or 8px grid. 6-8 stops (
0, 2, 4, 8, 12, 16, 24, 32, 48, 64). No values outside this scale.
- Radius: 3-4 values (
none, sm, md, lg, full). One canonical radius per component type.
- Motion: 3 durations (
fast 150ms / normal 250ms / slow 400ms). 2 easings (standard, emphasized). Respect prefers-reduced-motion. For detailed motion conventions, generate and reference references/motion-system.md alongside the design-system artifact.
- Breakpoints: mobile-first. 3-4 stops (
sm 640 / md 768 / lg 1024 / xl 1280). Document which is the "design target."
Step 4: Define primitive components with all 6 states
Every interactive primitive has default / hover / active / focus-visible / disabled / loading / error documented. Build the inventory:
| Primitive |
States required |
| Button (primary, secondary, ghost, danger) |
all 6 |
| Input (text, number, password, search) |
all 6 + filled, with-error |
| Select / combobox |
all 6 + open, with-search |
| Checkbox / radio / switch |
all 6 + checked, indeterminate |
| Toast / banner |
info, success, warning, error, with-action |
| Skeleton |
matches every component that fetches |
Skeleton screens, not spinners, for content-shaped loading.
Step 5: Write the gallery
A single Storybook (or equivalent) page per primitive showing every state, every size, every variant in light + dark. The gallery is the design system documentation — code and Figma diverge; the gallery doesn't.
Step 6: Composition rules + anti-patterns
In .forge/design-system.md:
- How primitives compose into patterns (toolbar, form row, list item, empty state, error state).
- The anti-patterns: don't nest cards 3-deep, don't use red for anything but danger, don't put a spinner inside a button without disabling the button, don't mix toast and modal for the same severity.
Verification
1---2name: design-system3description: Use when establishing visual foundations for a new product, when defining design tokens, when building or auditing a component library, before any UI work begins on a fresh codebase, or when the existing UI has drifted into hex codes and magic numbers.4---56# Design System78## Overview910Define the visual + interaction foundation *before* features get built. Output is `.forge/design-system.md` — the semantic token layer (color, typography, spacing, radius, motion), the breakpoint and dark-mode rules, the primitive component inventory with all states, the gallery, the composition rules, and the documented anti-patterns. Consumed by every UI-shipping skill (`incremental-implementation`, `interaction-patterns`, `accessibility`, `demo-narrative`).1112## When to Use1314- A new product or marketing surface is being designed and nothing exists yet15- The codebase has 20+ components with inconsistent spacing, radii, or color usage16- A redesign is starting and the team wants a foundation before screens get built17- Dark mode is being added and the existing palette has raw hex everywhere18- Two designers or two engineers are about to ship competing button styles1920## When NOT to Use2122- A single one-off internal admin page with no future23- A backend-only service with no UI surface24- A tiny bug fix to one component — that's `incremental-implementation`2526## Common Rationalizations2728| Thought | Reality |29|---------|---------|30| "We'll standardize later" | Later means 50 components with 50 different spacing values. Standardization costs nothing on day one, costs a refactor on day 200. |31| "Just use Tailwind defaults" | Defaults without a semantic mapping create implicit coupling to the framework. When the framework changes, every component breaks. |32| "Dark mode can wait" | Retrofitting dark mode costs 5x building it in. Every color picked without dark-mode awareness becomes a contrast bug. |33| "Loading states aren't important" | A component without a loading state is a component that lies about its readiness. The user sees a button that looks ready and isn't. |34| "Designer will fix it later" | The component shipped to production *is* the design. Later means never. |35| "It looks fine in Figma" | Figma is not the runtime. Real fonts render differently, real data overflows, real users have light/dark/HC modes. |3637## Red Flags3839- A hex code (`#3B82F6`) anywhere in a component file40- A spacing value not from the scale (`padding: 13px`)41- A component without a `disabled` or `loading` state42- Inconsistent `border-radius` across two buttons in the same product43- A color used to convey meaning with contrast below WCAG AA44- A component that "works" in light mode and breaks in dark45- A skeleton screen on one page and a spinner on the next46- A button that has hover but no focus-visible outline4748## Core Process4950### Step 1: Define the brand foundation5152Pin down before tokens:53- Brand voice (formal / friendly / technical / playful) — informs typography choice and motion timing54- One accent color (the brand color)55- One neutral scale (warm / cool / true gray)56- Density target (compact / comfortable / spacious)5758### Step 2: Write the semantic token layer5960Two levels:6162```63Reference tokens → Semantic tokens64gray-50, gray-100, ... → surface, surface-muted, border, text, text-muted65blue-500, blue-600, ... → accent, accent-hover, accent-active66red-500 → danger, danger-bg67```6869Components use **only** semantic tokens. Never reference tokens directly. Dark mode is a remapping of semantic → reference tokens, not a rewrite.7071Document every semantic token with: light hex, dark hex, WCAG contrast against its default partner, intended use.7273### Step 3: Define the scales7475- **Typography:** 6-7 sizes (caption / body / body-lg / heading-sm / heading / heading-lg / display). Line height per size. Font family + weight roles (regular / medium / semibold).76- **Spacing:** 4px or 8px grid. 6-8 stops (`0, 2, 4, 8, 12, 16, 24, 32, 48, 64`). No values outside this scale.77- **Radius:** 3-4 values (`none, sm, md, lg, full`). One canonical radius per component type.78- **Motion:** 3 durations (`fast 150ms / normal 250ms / slow 400ms`). 2 easings (`standard, emphasized`). Respect `prefers-reduced-motion`. For detailed motion conventions, generate and reference [references/motion-system.md](../../references/motion-system.md) alongside the design-system artifact.79- **Breakpoints:** mobile-first. 3-4 stops (`sm 640 / md 768 / lg 1024 / xl 1280`). Document which is the "design target."8081### Step 4: Define primitive components with all 6 states8283Every interactive primitive has **default / hover / active / focus-visible / disabled / loading / error** documented. Build the inventory:8485| Primitive | States required |86|---|---|87| Button (primary, secondary, ghost, danger) | all 6 |88| Input (text, number, password, search) | all 6 + filled, with-error |89| Select / combobox | all 6 + open, with-search |90| Checkbox / radio / switch | all 6 + checked, indeterminate |91| Toast / banner | info, success, warning, error, with-action |92| Skeleton | matches every component that fetches |9394Skeleton screens, not spinners, for content-shaped loading.9596### Step 5: Write the gallery9798A single Storybook (or equivalent) page per primitive showing every state, every size, every variant in light + dark. The gallery *is* the design system documentation — code and Figma diverge; the gallery doesn't.99100### Step 6: Composition rules + anti-patterns101102In `.forge/design-system.md`:103- How primitives compose into patterns (toolbar, form row, list item, empty state, error state).104- The anti-patterns: don't nest cards 3-deep, don't use red for anything but danger, don't put a spinner inside a button without disabling the button, don't mix toast and modal for the same severity.105106## Verification107108- [ ] `.forge/design-system.md` written109- [ ] No raw hex in any component (grep for `#[0-9a-fA-F]{6}`)110- [ ] All spacing values come from the scale (grep for `px:` and `padding:` values not in the scale)111- [ ] Every interactive primitive has all 6 states documented in the gallery112- [ ] Dark mode has parity — every screen renders cleanly in both modes113- [ ] Every text/background pairing meets WCAG AA (4.5:1 for body, 3:1 for large text)114- [ ] Every async action has a skeleton or loading state — no bare spinners on content115- [ ] `prefers-reduced-motion` respected — no animation longer than 100ms when set116- [ ] Focus-visible outline on every interactive element (no `outline: none` without a replacement)