Component Spec
Component schemas that AI can interpret and generate accurately.
Key takeaways
- A component spec defines props, slots, states, variants, constraints, examples, and anti-patterns so designers, engineers, and agents share one blueprint.
- Keep variants finite (e.g. primary/secondary/danger/ghost) and avoid open-ended
variant?: string, which invites agents to invent styles. - Document a finite state model (default, hover, focus-visible, disabled, loading, error, empty, selected) or agents render only the happy path.
- Slot contracts assign ownership: the parent owns layout rhythm and external spacing, the child owns internal spacing and semantics.
- Pair prose with machine-readable JSON examples and explicit do-not-invent rules, then type-check and accessibility-test every example.
A component spec is the blueprint agents use to choose, configure, and compose UI. Good specs define props, slots, states, allowed variants, constraints, examples, and failure modes.
The spec should be useful to three readers at once: designers validating intent, engineers implementing behavior, and agents generating code from constraints.
Minimum Spec Shape
export type ComponentSpec = {
name: string
purpose: string
props: Record<string, PropSpec>
slots?: Record<string, SlotSpec>
states?: string[]
variants?: string[]
accessibility: AccessibilitySpec
examples: ExampleSpec[]
antiPatterns: string[]
}What Agents Need
| Spec area | Agent failure it prevents |
|---|---|
| Purpose | Choosing the wrong component. |
| Prop constraints | Invalid or unsupported combinations. |
| State model | Missing loading, disabled, error, or empty states. |
| Slot rules | Broken composition and nested-card layouts. |
| Examples | Generic UI and invented patterns. |
| Anti-patterns | Repeating known bad outputs. |
Variant Design
Variants should be finite and meaningful.
type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost'
type ButtonSize = 'sm' | 'md' | 'lg'Avoid open-ended strings such as variant?: string. They invite agents to invent unsupported
styles.
State Model
Every interactive component needs a finite state model. If the state is not documented, agents tend to render only the happy path.
| State | Required detail |
|---|---|
default | Normal interactive behavior and visual baseline. |
hover | Pointer affordance and whether the state is purely decorative. |
focus-visible | Keyboard focus ring and focus target. |
disabled | Disabled semantics, pointer behavior, and contrast rule. |
loading | Whether the label stays visible and whether layout width is stable. |
error | Error ownership, message placement, and aria-describedby linkage. |
empty | Placeholder content and next action. |
selected | Selected semantics and how it differs from focus. |
Slot Contract
Slots let teams compose richer UI without giving agents unlimited structure.
export type CardSpec = {
slots: {
media?: 'image' | 'icon' | 'none'
header: 'required'
description?: 'short-text'
actions?: 'one-primary-or-two-secondary'
metadata?: 'badge-list'
}
rules: [
'Do not nest Card inside Card',
'Parent layout owns external spacing',
'Actions stay in the footer slot on mobile',
]
}Slot specs should describe ownership: the parent controls layout rhythm, while the child controls internal spacing and semantics.
Composition Rules
- Define which components can contain which children.
- Mark components that must not be nested.
- Document layout responsibilities: parent owns spacing, child owns internal rhythm.
- Provide examples for empty, loading, error, and long-text cases.
Machine-Readable Examples
Pair prose docs with structured examples. Agents can search and transform structured records more reliably than narrative-only guidance.
{
"component": "Button",
"intent": "submit destructive action",
"allowed": {
"variant": ["danger"],
"size": ["sm", "md"],
"iconOnly": false
},
"accessibility": {
"requiresVisibleLabel": true,
"requiresAriaLabel": false
},
"antiPatterns": [
"Using danger for non-destructive actions",
"Combining danger with ghost in confirmation dialogs"
]
}Do-Not-Invent Rules
Design-system docs should be explicit about what the agent may not create.
| Rule | Why |
|---|---|
| Do not invent component names. | Prevents parallel UI systems. |
| Do not invent variants. | Keeps visual states reviewable. |
| Do not hard-code raw tokens. | Preserves theming and accessibility. |
| Do not change primitive behavior in feature code. | Keeps accessibility fixes centralized. |
| Do not add decorative wrappers around stable components. | Prevents card overload and layout drift. |
Example Contract
Each component should include:
- a default example;
- a complex realistic example;
- an accessibility example;
- an error or disabled example;
- a "do not do this" example.
Verification
- Type-check every generated example.
- Run Storybook or preview smoke tests.
- Add accessibility checks for required roles, labels, and focus behavior.
- Validate examples against prop schemas.