OTFotf
All posts

Your design system is the context your AI agent is missing

D
DaveAuthor
6 min read
Your design system is the context your AI agent is missing

Ask any AI agent for "a settings card" twice in the same project and you'll get two different blues. Ask for a third and you'll get a third radius, a fourth shadow, a padding value nobody chose. The components work. They just don't look like they came from the same app.

This gets blamed on the model. It isn't the model. The agent produced inconsistent UI because it had no design context — and no amount of "make it look consistent" in the prompt fixes a missing input. The fix is to stop describing your design system and start encoding it as something the agent can't violate.

Taste doesn't transfer as pixels

You can paste a screenshot of your beautiful app into the chat and ask the agent to match it. It will approximate. It'll eyeball the blue as #3B82F6 because that's the blue in its training data, not the #2D6FF2 you actually use. It'll guess 8px of padding where you use a 12px scale step. Every approximation is a small drift, and drift compounds across a UI.

A screenshot is the design as output. The agent needs the design as input — the values, named, that produced the screenshot. That's a token system.

// Not this — a literal the agent will round to the nearest training-data blue:
<View style={{ backgroundColor: '#2D6FF2', padding: 12, borderRadius: 8 }} />

// This — semantic tokens the agent can reuse exactly, every time:
<Button variant="primary" />        // primary = the one blue, defined once
<Card />                            // padding + radius from the scale, not guessed

Tokens transfer. Pixels don't.

A lint rule is context the agent can't ignore

A CLAUDE.md line that says "use tokens, never hex" helps. But it's advice, and an agent under pressure to ship will still drop a hex literal when it's in a hurry. Advice is skippable. A failing build is not.

So the design system isn't just tokens — it's a lint rule that bans the alternative:

# eslint-plugin-otf-design, on a hex literal in feature code:
error  Raw hex color "#3B82F6" is banned use a palette token  otf-design/no-hex-colors

Now the constraint is load-bearing. The agent writes a hex literal, the lint fails, the agent reads the error, and it does the only thing the error allows: it reaches for the token. You didn't have to be in the loop. The codebase told the agent its taste, in the one language an agent can't argue with — a red error.

This is the whole trick. Consistency you enforce survives the agent. Consistency you request survives until the agent is in a hurry.

Named tokens beat raw values for a language model

There's a reason semantic tokens work better than literals specifically for an LLM. A model reasons about meaning, not hex math. bg-card carries intent — it's the surface color for a card — so the agent uses it for cards. #1A1816 carries nothing; the agent has to remember it's the card color, and it won't.

// The agent knows what this is for. It'll reuse it correctly.
<div className="bg-card text-card-foreground border-border" />

Name the token after its job and the agent gets the job right. Name it after its value and you've handed the model a magic number to misremember.

The same tokens make cross-platform output consistent too

The payoff multiplies when the tokens are shared across platforms. When @otfdashkit/ui (web) and @otfdashkit/ui-native (mobile) read from the same @otfdashkit/tokens package, "primary" is one decision, resolved once, honored everywhere.

What the agent writesWebNative
variant="primary"same bluesame blue
<Card>same radius + shadow scalesame radius + shadow scale
a hex literallint errorlint error

Ask the agent for the same screen on web and native and you get the same screen — not because the model is consistent, but because both platforms read the same constrained input.

The agent reads the constraint, not your Figma

This is the mental shift: your agent will never open your Figma. It reads the repo. So the design system that actually governs the agent's output is the one that exists in the repo as code — tokens it imports and a lint rule it can't pass without obeying.

That's why every OTF kit ships @otfdashkit/tokens (four palettes — Slate, Warm, Cosmic, Terminal), SDK primitives that consume those tokens, and eslint-plugin-otf-design to ban the hex literals that cause the drift. It's also item one on the kit design checklist: no hex in feature code, tokens only. Not because hex is evil, but because tokens are the only form of your taste the agent can actually read and the lint is the only form it can't skip.

Your design system isn't a style guide for your designers. To your AI agent, it's context — the missing input that decides whether "a settings card" comes out looking like your app or like the model's best guess. Encode it as tokens, enforce it as lint, and the agent inherits your taste whether it meant to or not.

design-systemai-toolsarchitecture

On this page