AI-generated UI gets ~90% right. These are field notes on the 10% — and what 45,000 impressions, 60 comments, and practitioner calls taught me about killing it.
The missing 10% is never layout. It is precision. Four classes of drift, each one invisible at a glance.
A control spec'd at 60px renders at 48px. Plausible, proportional, wrong — 48 is a believable height, which is exactly why nobody catches it.
Bind every dimension to a named token the AI can point at — never a number it has to retype.
A 3px stroke returns as 2px. The component is right, the geometry is almost right — and almost compounds across every surface that inherits it.
Give hairline values their own tokens. Thin details drift first, because a 1px miss looks like rendering, not error.
A tint fill quietly becomes a solid. Same hue family, different treatment — a reviewer approves it at speed, an audit rejects it on sight.
Name the treatment per state in the spec — "tint", not "light red". Adjectives are where treatments swap.
A missing cell in a color table gets a confidently invented swatch. To a generative model, absence reads as permission.
Render every empty cell as an explicit "—". A declared gap is an instruction; a blank is an invitation.
Better prompts did not fix it. Documentation a machine cannot misread did.
"Never do X" degrades at generation time. The same rule as a positive directive with the exact value holds. Published research agrees: models get measurably worse at negated instructions as they scale.
Rewrite one "never" rule in your docs as a positive directive with its exact value. Then regenerate and watch which version survives.
If the AI retypes "48" from memory, it can misremember. If it points at one named token, there is nothing to misremember. The discipline tokens always promised, now load-bearing.
Audit one component spec for retyped numbers. Replace each with its token name. Count what you find — the count is your risk surface.
The verification taught me more than the finding did. Five rules for testing without fooling yourself.
Prompts name pages and point at the system — never the correction. If the fix lands unprompted, your documentation captured it and activated it. Name it, and you have tested your prompt, not your system.
Regenerate one page from a generic "match the spec" prompt. Count what lands unprompted — that count is your documentation's real score.
They need different repairs — diagnose before you fix, or you fix the wrong layer.
Label every miss in your next review "untouched" or "touched". Untouched: make the rule fire. Touched: fix the spec.
In revision passes, models happily add what the spec defines but rarely remove what it does not. Stale UI survives every update unless subtraction is itself a positive directive.
Add one line to every revision prompt: "remove whatever the spec does not define." Watch what disappears.
A single pixel off means the token chain broke somewhere upstream — long before a human eye would catch it.
Plant one odd-valued dimension as your tripwire. Check it first on every regeneration — it audits the whole chain in one glance.
Color and token errors ripple into every downstream component. Settle upstream truth before judging components, or you will re-review everything twice.
Re-order your next review bottom-up. If the color page is wrong, stop — nothing above it is worth judging yet.
Nobody disagrees drift exists. Teams disagree on where to kill it. Six enforcement points along one pipeline — tap each to see whether it fits your stack.
Acts at the docs & tokens stage. A guideline per component, positive directives, exact values, and a stylesheet generated from the production token file — one token truth. Verification is a library diff on every regeneration.
Acts at the generation stage. Drift dies at the source: the agent uses the actual coded component instead of re-creating it. "It IS the actual design system component."
Acts at the docs → generation seam. Tell the agent where to look — tokens in JSON, YAML, CSS, plus a usage spec — instead of rebuilding components from prose. Rebuild-from-description varies run to run no matter how stringent the text.
Acts at the output stage. Generator agent plus a separate reviewer/validator agent — a common pattern in practice, and common is not a demotion: it is the most portable point on the map, because it asks nothing of your stack. A manual diff loop is the human-powered version.
Replaces the generation stage. What I would call a deterministic materializer: the AI works on a description of the system, and a deterministic factory generates the code — generation variance disappears entirely.
Acts at the design system root. Audit the production system, swap in an AI-legible token layer, confirm compatibility, ship. Authority is won by evidence, not negotiation: read-only access, a staging proof, dark mode as the diagnostic, then the CTO.
Each stack and each generation point needs its own enforcement point, or a combination. The question is never "how do I stop drift" — it is "which enforcement points can my stack actually support."
Mark the two points your stack can support today. Run them in combination — one upstream, one downstream.
Two questions still open. This is where the notes grow next.
Self-supporting documentation loops: docs that produce their own next source. If output becomes input, drift compounds quietly — unless something outside the loop stays truth. My anchor today: the library, plus a diff on every regeneration.
If your docs feed their own regeneration, name your outside anchor now. No anchor, no truth — only momentum.
The real boundary is not legacy vs greenfield — it is how token authority is held. Where it is shared, fidelity lives in the contract: one canonical, executable token layer — design owning semantic intent, engineering owning production integrity.
Write down, today, who owns semantic intent and who owns production integrity for your tokens. If two systems hold truth, drift already lives between them.
If your team kills drift at a point not shown here — or you have watched one of these six fail — I want to hear about it. The notes grow as the conversations do.