The contract is a set of named design decisions (tokens plus component names), and designs,
apps and every framework rendering of them speak only in those names. Phase A installs the
structure; phase B settles the values in it. prototype-to-design reconciles a screen
against those values and never designs a scale. Every path, glob and command installed must
resolve to something real in this repo.
Everything installed here is durable, and that is what separates it from the designs it serves: a design is a ticket plus a branch that both go when its screen ships, while the kit below outlives every screen. Nothing here is written per screen.
A1. Detect the target stack
Look before asking (tailwindcss in the manifest? react-native / tamagui? a
Package.swift with a UI module? both?), confirm with the user, then pick the shape:
| Stack | Token contract | Wiring |
|---|---|---|
| Web + Tailwind | one CSS custom-property file (:root + theme variants) |
@theme inline maps vars → utilities |
| React Native / Tamagui | tokens.ts feeding createTokens() |
the Tamagui config imports it |
| Native (Swift / Kotlin) | a constants source in the UI module | consumed directly by views |
| Multi-platform | DTCG tokens.json |
a Style Dictionary build → per-target outputs; look up its current docs before wiring |
A2. Install or complete the token contract
If a token layer exists, extend it, never replace it. The contract has one slot per family
(references/foundations.md names the five); most projects that "have tokens" have only
colours.
Done when each family has a named slot, populated or marked TODO: phase B.
A3. Install the design kit
Three files under docs/design/, which every design page on a branch is built from:
tokens.css, the browser mirror of the contract. When the contract is CSS, mirror it; otherwise this is an export step, a small generator or a Style Dictionary target wired into the same command that builds the app or runs quality, never a hand copy.design-template.htmlfromtemplates/design-template.html.kit.js, seeded with one render function, since the template loads it.
A design page inlines both when it is pushed, so design.html is one self-contained file.
Its two readers pull that file out of the branch and open it over file://, where a link to a
sibling is a silent 404 and the page renders untokenised with nothing saying so.
Done when the three files exist and the template resolves every var(--…) it names.
A4. Install the no-raw-values check
Copy templates/check-design-tokens.sh to scripts/, substitute {{SRC_DIRS}} (the dirs to
scan) and {{EXCLUDE_FILES}} (token files where raw values are legal), add
"check:design-tokens": "sh scripts/check-design-tokens.sh", and wire it into the existing
lint workflow and pre-commit hook. On React Native, adapt the patterns to quoted hex literals
and numeric style-object values; on native targets, to a literal colour or size in a view
file; keep the allowlist mechanism. If the codebase fails, seed the allowlist with the
current offenders and file one debt ticket listing them, so the check is green on install
and the debt visible.
A5. Install the render method
pixel-review and design-to-code need one UI state rendered deterministically. Recommend
from what is present: .storybook/ → Storybook; a native isolated-state harness → that
harness, launched by state name; a live design branch → its design.html via file://; a
dev script → the dev server; several → the app's harness for built screens, the design page
for unbuilt ones. For browser targets copy templates/screenshot-states.mjs to scripts/ (it
needs Playwright; use the project's devDependency or note npx playwright). For native
targets record the project's own capture command and confirm it captures the window.
Done when the render command produces one PNG when run.
B1. Gather the raw material
Read references/foundations.md first: the shape of each family and how a value is judged.
From whatever exists, in order: moodboard or specimen pages, the flagship prototype, the
app's existing styles, reference screenshots. If nothing exists, generate one throwaway
specimen page per family and review it with the user first.
Done when there is one histogram (value × use-count) per property: size, weight, tracking, line-height, space, radius, duration, colour.
B2. Design each family from the histograms
Cluster the observed values, choose the role set (drop roles the product doesn't use, invent none), pin each role to a clean value at most a snap away from its cluster, complete each tuple from the dominant observed pairing, and snap off-step observations to a neighbour.
Done when every observed value maps to exactly one proposed token or a named component-local exception, with every row where judgement moved a value flagged, and by how much.
B3. Bless checkpoint
Present one table per family: observed cluster → proposed token (name and value), judgement rows called out with alternatives. The user adjusts and blesses.
Done when the user has answered each family's table; an unanswered table blocks B4.
B4. Land the contract and its specimen
Every family, every theme variant, full tuples, in the slots A2 made, with the framework
wiring in the same change; regenerate the mirror with the command A3 recorded. Then
docs/design/foundations.html imports the mirror and renders every role: a line per type
role, spacing blocks, the core ramps plus semantic chips per theme, radius and motion demos.
It styles only via var(--token), so it always shows the current contract.
Done when the no-raw-values check passes on the contract's consumers and every token name in the contract appears in the specimen (grep both, diff empty).
B5. Mark the live designs stale, when values moved
Snapped jitter (≤1px) leaves existing designs untouched. When the bless deliberately moved
values, every open design ticket now describes measurements the contract no longer holds:
label each one stale and hand the mapping tables to the user. prototype-to-design re-bases
a stale design when someone next works on it and removes the label there, which is where the
translation belongs — doing it here re-renders screens nobody is building. Until it is removed,
that label blocks every build ticket against the design.
Closed design tickets are shipped screens and take no label: the app is the truth for those.
C. Write the design stack and the design rule
docs/design-stack.md answers these questions and nothing else; every later design skill
reads it instead of hardcoding a framework:
# Design stack
- **Token contract** — <path>. The only place raw values live.
- **Design kit** — docs/design/, holding tokens.css (generated by <command>),
design-template.html and kit.js. A design page is built from these.
- **Components live in** — <path(s)>, and the rule for choosing between them.
- **Isolated-state mechanism** — <stories | specimen catalog | preview target>, added by <what a new state costs>.
- **Render a state** — <command>, output <where>.
Then write rules/design.md, the one page of design prose no check enforces, in this stack's
own spelling (grep the stack for the constructs you name before writing them):
- every visual constant is a token reached by name, never a literal at a call site, and a missing value is added to the contract first;
- tokens are named by role, never by value, and the role set stays small;
- every string the user reads goes through the one text primitive that applies the type ramp;
- every unit is placed in a tier (atom, molecule, organism) before it is written, and an existing primitive is never re-implemented inline;
- a screen is a thin container that resolves state and a pure View that takes a value;
- an approved design is a spec, never a source: its decisions survive as tokens and component names, and the page it was drawn on goes when the screen ships.
Add a Visual verification section to the project doc pointing at docs/design-stack.md
and rules/design.md.
D. Verify and report
A deliberately bad line makes the check fail and removing it makes it pass; the template
opens and renders with the token vocabulary; the render command produces a PNG; the mirror
generator run twice produces no diff. Report the stack, families settled against missing,
which judgement calls moved values and by how much, where the check is wired, the render
method, allowlist debt, and the next step: /prototype the first screen.
Re-running as an audit
Re-extract current usage (the live design branches plus app source), compare against the contract, and present only the drift: tokens nothing uses, values nothing names, families still missing, jitter that leaked into the contract. Fix through the same bless → land → specimen loop (B3–B5).