Every mature frontend leaks hardcoded hex colors, magic spacing, and one-off font sizes. An agent can find all of them in minutes and propose a token for each — but only a per-mapping evidence table, approved row by row by a human, keeps the sweep from silently repainting your product.
Tier: Core · Workflow area: Design-to-code & design systems
This playbook turns "we should really tokenize this" from a quarter-long chore into one afternoon of mechanical inventory, one agent-built mapping table, and one focused review session.
#333 will break it.tokens.json consumed by style-dictionary, a Tailwind @theme block, or a theme.ts / CSS-variables file.Size the problem mechanically before involving a model. Counting is a job for ripgrep, not tokens:
rg -n --no-heading \
-e '#[0-9a-fA-F]{3,8}\b' \
-e 'rgba?\(' \
-e '\b\d+px\b' \
src/ --glob '!**/tokens/**' --glob '!**/*.test.*' | wc -l
Record the number. It is your progress metric and your PR-slicing guide — 900 hits means several batched PRs, not one.
Load the token vocabulary into the agent. The single biggest failure mode is an agent that invents plausible-sounding tokens. Anchor it:
Read src/design-tokens/tokens.json (style-dictionary format). Build a
lookup of every color, spacing, radius, and typography token with its
RESOLVED value (follow aliases like {color.brand.600}). Output the
lookup as a table. Do not modify any files yet.
Spot-check five rows against the file. If an alias resolved wrong here, every mapping downstream is wrong.
Demand a mapping table with evidence — not edits. This is the heart of the playbook:
For every hardcoded color, spacing, radius, and font-size value under
src/features/billing/, produce a table with columns:
file:line | code excerpt | current value | proposed token |
token's resolved value | delta | confidence (exact/near/none).
"exact" = resolved values identical. "near" = within 2% lightness or
one spacing step. "none" = no token within that range — propose
nothing, flag for design. Never round silently. Do not edit files.
Sample of what a healthy table looks like:
| file:line | current | proposed token | resolves to | delta | confidence |
|---|---|---|---|---|---|
InvoiceCard.tsx:41 | #2563eb | color.brand.600 | #2563eb | none | exact |
InvoiceCard.tsx:78 | padding: 18px | space.400 | 16px | −2px | near |
StatusPill.tsx:12 | #e8fff1 | — | — | — | none |
Approve per mapping, not per file. Three verdicts, three speeds. Exact rows: skim and approve in bulk. Near rows: these are design decisions in disguise — an 18px padding becoming 16px moves pixels; get a designer's yes, row by row. None rows: send the list to the design-system owner; they either mint a new token or declare the value a bug.
Apply in confidence-ordered batches, gated visually. Have the agent apply only approved exact rows first, then run your screenshot diff:
Apply only the rows marked APPROVED-EXACT in mapping.md. One commit
per component directory. Touch nothing outside those rows.
The exact batch must produce a pixel-identical diff — if it does not, a "resolved value" was wrong, and you stop and re-audit step 2. Near batches land next, each with its expected visual delta named in the PR description.
Lock the door behind you. A sweep without a gate decays in a month. Add stylelint-declaration-strict-value (or an ESLint rule for CSS-in-JS) so raw hex and px literals fail CI, with an explicit /* token-exempt: reason */ escape hatch for the genuinely intentional cases.
"Near" matches are where AI sweeps quietly redesign your product. A model rounding #334155 to color.gray.700 feels harmless per line — across 400 lines it is an unreviewed restyle. Every near mapping gets a human eye, no exceptions.
mapping.md in the repo so approvals survive across sessions.build/css/variables.css) so it maps to the names your code actually imports.bg-[#2563eb], p-[18px]) and maps them to theme utilities (bg-brand-600, p-4); the same table format applies.Color(0xFF2563EB) → MaterialTheme.colorScheme) or SwiftUI (Color(red:green:blue:) → asset-catalog / token extension values).token-exempt with reasons.The deliverable is not the diff — it is the evidence table. An agent that must show file:line, resolved value, and delta for every proposal cannot hallucinate a token or round a color silently, and a human approving rows instead of reading diffs reviews ten times faster with more control, not less.