Beyond tokens: teaching your component library to AI agents with DESIGN.md
Beyond tokens: teaching your component library to AI agents with DESIGN.md
You spent months building a component library. You’ve got Button with 6 variants, Card with 4, Modal with 3 sizes, a full Table system. Everything tested, documented in Storybook, with interactive demos and a playground. Then you ask an agent to create a new screen and it ignores every single one of those components. Builds everything from scratch with raw divs and inline classes.
The reason is painfully simple: the agent doesn’t read Storybook. It doesn’t navigate interactive URLs. It doesn’t open iframe demos. It reads text. If your components don’t exist as text in its context window, they don’t exist. Full stop.
The paradox: perfect library, blind agent
I keep seeing this exact scenario play out:
- Team builds a beautiful design system with Storybook
- Team adopts Cursor/Copilot/Claude to code faster
- Agent generates UI that uses zero components from the design system
- Team: 😠
The natural reaction is “stupid agent.” The reality is “agent without context.” If you never declared in your DESIGN.md that an <AlertDialog> exists with info | warning | destructive variants, the agent will build a generic modal from scratch with a div, a manually coded backdrop, and handwritten buttons.
The official spec from Google Labs (Design Spec for agents) addresses this explicitly in the Do/Don’t section:
Use the Do/Don’t format to give the agent clear boundaries. “Do: use AlertDialog for destructive actions. Don’t: build custom modals when a system component exists.”
Seems obvious. Almost nobody does it. Most DESIGN.md files I encounter in the wild end right after the tokens section, as if the agent would magically discover that a <Tooltip> component exists and is available for use.
What the agent actually needs to know about each component
Not everything. The agent doesn’t need the full API reference. For that, it can look at the source code or the TypeScript types. What it needs is:
- That the component exists (name, import path)
- When to use it (usage context)
- When NOT to use it (anti-patterns)
- Which variants are available (and when each one applies)
- Slots/children (what goes inside)
- Composition (which other components it combines with)
That’s surprisingly little text per component. But it solves the entire problem.
Anatomy of a component documented for agents
Here’s how I document a Card component so any agent uses it correctly:
### Card
**Import:** `@/components/ui/card`
**Variants:**
- `default` — white background, subtle border, no elevation
- `elevated` — shadow-md, used for highlight/emphasis
- `interactive` — cursor pointer, hover state, entire card is clickable
- `outlined` — stronger border, no shadow, for dense grids
**When to use:**
- Content containers that group related information
- Pricing cards, feature cards, testimonial cards, user profile cards
- Dashboard widgets and metrics
**When NOT to use:**
- ❌ Wrapping a single form field (use the field directly)
- ❌ Navigation items (use NavItem or Link)
- ❌ Full-width content that spans the viewport (use Section)
- ❌ Nested inside another Card (flat hierarchy only)
**Slots:**
- `CardHeader` — title + optional description + optional action button
- `CardContent` — main body content
- `CardFooter` — actions (buttons, links) aligned right
**Composition:**
- Inside: Grid layouts, Dashboard panels
- Contains: Form fields, Lists, Charts, Stats
- Never inside: Another Card, Modal body (use plain content)
**Do/Don't:**
- ✅ DO: Use `elevated` for the ONE highlighted card in a pricing grid
- ✅ DO: Use `interactive` when the entire card navigates somewhere
- ❌ DON'T: Mix `elevated` and `outlined` in the same grid
- ❌ DON'T: Put more than 2 action buttons in CardFooter
- ❌ DON'T: Use Card for less than 3 lines of content (too small, looks weird)
That’s about 30 lines. The agent reads it in milliseconds. And with this information, it will never:
- Nest a card inside another card
- Use elevated for every card in a grid
- Jam 5 buttons into the footer
- Wrap a lone input in a card for no reason
Each line is a prevented error.
Copy-paste template: document your components
Here’s the template I use. Copy it, paste it, fill it in for each component in your library:
### [ComponentName]
**Import:** `[path]`
**Variants:**
- `variant1` — [when to use, appearance]
- `variant2` — [when to use, appearance]
**When to use:**
- [Use case 1]
- [Use case 2]
- [Use case 3]
**When NOT to use:**
- ❌ [Anti-pattern 1]
- ❌ [Anti-pattern 2]
**Slots:**
- `[SlotName]` — [what goes inside]
**Composition:**
- Inside: [where this component lives]
- Contains: [what goes inside it]
- Never with: [forbidden combinations]
**Do/Don't:**
- ✅ DO: [correct behavior]
- ❌ DON'T: [common mistake the agent would make]
Takes 5 minutes per component. If your library has 30 components, that’s 2.5 hours of work that will save you weeks of manually fixing agent output. The ROI on this is absurd.
Full example: a 5-component library
Here’s what a real, functional Components section looks like in a DESIGN.md:
## Components
### Button
**Import:** `@/components/ui/button`
**Variants:**
- `primary` — main action, solid primary color background
- `secondary` — alternative action, subtle background
- `ghost` — no background, no border, subtle hover. Toolbars, tertiary actions
- `destructive` — red, irreversible actions
- `link` — looks like a text link, no padding
**Sizes:** `sm`, `md` (default), `lg`, `icon` (square, icon only)
**Rules:**
- ✅ Maximum ONE primary button per visible viewport
- ✅ Destructive always paired with confirmation dialog
- ✅ Icon button always has aria-label or tooltip
- ❌ NEVER two primary buttons side by side
- ❌ NEVER destructive without explicit text label (not just icon)
- ❌ NEVER ghost button as main CTA
---
### Dialog (Modal)
**Import:** `@/components/ui/dialog`
**Sizes:** `sm` (400px), `md` (560px), `lg` (720px)
**When to use:**
- Confirmations that require explicit user decision
- Forms that don't warrant a full page
- Detail views triggered from lists/tables
**When NOT to use:**
- ❌ Notifications or alerts (use Toast)
- ❌ Simple yes/no (use AlertDialog, not full Dialog)
- ❌ Content that needs URL/bookmark (use page route instead)
**Structure:**
- `DialogHeader` — title (required) + description (optional)
- `DialogBody` — content, scrollable if long
- `DialogFooter` — actions, right-aligned. Cancel left, confirm right.
**Do/Don't:**
- ✅ DO: Close on Escape and backdrop click (unless destructive)
- ✅ DO: Focus trap inside dialog
- ❌ DON'T: Dialog inside dialog (nested modals)
- ❌ DON'T: Dialog wider than 720px
- ❌ DON'T: Dialog for content longer than 2 scrolls (use page)
---
### Toast (Notification)
**Import:** `@/components/ui/toast`
**Variants:**
- `default` — informational, neutral
- `success` — action completed successfully
- `error` — something failed
- `warning` — attention needed but not blocking
**Rules:**
- Auto-dismiss after 5s (success, default) or persistent (error, warning)
- Position: bottom-right (desktop), bottom-center (mobile)
- Max 3 toasts stacked simultaneously
- ✅ DO: Use for async operation results (save, delete, send)
- ❌ DON'T: Use for validation errors (use inline field errors)
- ❌ DON'T: Use for critical actions that need response (use Dialog)
---
### DataTable
**Import:** `@/components/ui/data-table`
**Features available:** sorting, filtering, pagination, row selection, column resizing
**When to use:**
- Structured data with 5+ columns
- Data that benefits from sorting/filtering
- Lists where bulk actions are needed
**When NOT to use:**
- ❌ Less than 4 columns (use simple List component)
- ❌ Media-heavy content (use Grid/Cards)
- ❌ Timeline/activity feeds (use ActivityFeed)
**Do/Don't:**
- ✅ DO: Always include pagination for 20+ rows
- ✅ DO: Skeleton loading state with 5 placeholder rows
- ✅ DO: Empty state with illustration + CTA
- ❌ DON'T: Horizontal scroll on desktop (reduce columns instead)
- ❌ DON'T: More than 8 visible columns (use column toggle)
- ❌ DON'T: Sortable columns without visual indicator
---
### Sidebar Navigation
**Import:** `@/components/ui/sidebar`
**Structure:**
- `SidebarHeader` — logo/brand + collapse toggle
- `SidebarContent` — nav groups with items
- `SidebarFooter` — user avatar + settings
**Behavior:**
- Desktop: fixed, 280px wide, collapsible to 64px (icons only)
- Mobile: overlay drawer, opened by hamburger menu
- Active item: highlighted background, bold text
- Groups: collapsible sections with chevron
**Do/Don't:**
- ✅ DO: Max 2 nesting levels (groups → items)
- ✅ DO: Icons on every item for collapsed state
- ❌ DON'T: More than 8 top-level items (group them)
- ❌ DON'T: Sidebar + top navbar simultaneously (choose one)
- ❌ DON'T: Links that open in new tab from sidebar
The output difference
Without component documentation, I asked an agent: “Create a dashboard with sidebar, user table, and metric cards.”
Result without docs: Sidebar with 12 ungrouped items, table without pagination that scrolls horizontally, 6 metric cards all the same size and style, a generic modal for editing users built with manual divs.
Result with docs: Sidebar with 3 collapsible groups (7 items total), DataTable with pagination and sorting, 4 cards in a responsive grid (one elevated for the primary metric), a 560px Dialog with a form for user editing.
The difference is dramatic. This isn’t cosmetic tweaking. It’s the difference between “this works as a product” and “this looks like a hackathon prototype someone built at 3am.”
How much to document: the 80/20 rule
You don’t need to document ALL components on day one. Here’s the practical priority:
Document first:
- Layout components (Grid, Container, Section, Sidebar)
- Action components (Button, Link, IconButton)
- Feedback components (Toast, Dialog, AlertDialog)
- Data components (Table, List, Card)
Document later (when the agent gets it wrong): 5. Form components (Input, Select, Checkbox, DatePicker) 6. Navigation components (Tabs, Breadcrumb, Pagination) 7. Decorative components (Badge, Avatar, Tooltip, Skeleton)
The first 10-15 components cover 80% of the decisions the agent makes. The rest you add incrementally when you catch the agent making mistakes. This isn’t laziness. It’s prioritization. Document what causes the most damage when wrong.
Patterns that emerge: the agent learns composition
Something interesting happens when you document composition (what goes inside what): the agent starts composing components correctly without explicit instructions for each screen.
If it knows that:
- Card contains FormFields
- Dialog has DialogHeader + DialogBody + DialogFooter
- DialogFooter has Button (cancel ghost on left, confirm primary on right)
- DataTable has an empty state with illustration + CTA
It extrapolates:
- “Profile edit form” means Dialog md + form fields in body + cancel/save in footer
- “Empty project list” means DataTable with a custom empty state
- “Pricing cards” means a Grid of Cards with one elevated (the highlight)
Composition is the superpower here. Tokens tell the agent “how it looks.” Components tell it “what to use.” Composition tells it “how to assemble.” That third layer is where the real magic happens, where you go from an agent that knows the parts to an agent that builds coherent wholes.
Integrating with your existing workflow
If you already have Storybook, the migration is mechanical:
- Open each component in Storybook
- Extract: variants, when to use, when not to use
- Add to DESIGN.md using the template format
- Prioritize the 15 most-used components
If you don’t have Storybook (you use copied Tailwind components, shadcn/ui, or custom stuff):
- List the components that exist in your
/componentsfolder - For each one, define variants and usage rules
- Think about mistakes a junior dev would make, then document those as Don’t rules
The investment is minimal compared to the return. Each documented component saves dozens of manual corrections over its lifetime.
The anti-pattern: documentation that becomes an obstacle
A warning: don’t turn the Components section into a 500-line bible for each component. The agent needs quick decision-making context, not an exhaustive spec.
Good: 20-30 lines per component focused on decisions Bad: 200 lines per component with every prop documented
If the agent needs to know that Button accepts leftIcon and rightIcon, it can discover that by looking at the TypeScript types. What it cannot discover on its own is that “maximum one primary button per viewport” is a design system rule. Document what isn’t obvious from the code. Everything else is noise.
There’s a balance. I’ve seen teams swing too far in both directions. Too little documentation and the agent guesses wrong constantly. Too much and the context window fills with information that doesn’t help with the current task. The sweet spot is: enough to prevent the common errors, brief enough to skim in seconds.
DESIGN.md as a living contract
The Components section isn’t static. It evolves through a feedback loop:
- Agent generates something wrong - identify which rule was missing
- Add the rule to the relevant component as a Do/Don’t
- Test by asking the agent to generate something similar
- If it gets it right - the rule works. If it still gets it wrong, rewrite with more clarity.
This loop transforms DESIGN.md into a living contract between you and the agent. Each iteration improves output quality. After 2-3 weeks of active use, the DESIGN.md stabilizes. The agent rarely makes mistakes, and when it does, it’s on exotic edge cases that nobody would have predicted.
I think of this process as training without fine-tuning. You’re not adjusting model weights. You’re adjusting the input. But the effect is similar: the agent’s output gets measurably better over time, specific to your project, your design system, your preferences. And unlike actual model training, you can see exactly why it’s working (or not working) by reading the document.
A note on token budget
People worry about context length. “Won’t 30 documented components blow my token budget?” Let’s do the math.
30 components at 25 lines each is 750 lines. That’s roughly 3000-4000 tokens. Even the smallest modern context windows (Claude Haiku at 200K, GPT-4o-mini at 128K) have capacity for this with room to spare. You’d need to document 500+ components before context becomes a real concern.
And here’s the thing: those 3000-4000 tokens of component docs prevent the agent from generating 200+ lines of wrong code that you then spend 30 minutes fixing. The token cost pays for itself in the first generation.
Conclusion: components are the multiplier
Tokens make the agent get colors right. Components make the agent get everything else right. If I had to choose between a DESIGN.md with 100 perfect tokens and a DESIGN.md with 15 well-documented components, I’d pick the components without thinking twice.
Colors? The agent solves those with a hex code. Knowing that AlertDialog exists, that it differs from Dialog, that its max size is sm, and that it always needs an explicit confirm button? The agent only knows that if you tell it.
Start today. Pick your 5 most-used components. Document them with the template. Ask the agent to generate a screen. See the difference. I promise you’ll never go back to tokens-only DESIGN.md files again.
Want a DESIGN.md with components already mapped automatically? designmd.app analyzes your site, identifies recurring components, and generates structured documentation with variants, composition rules, and anti-patterns. Your component library, translated for agents. Try it now.