Component specs are framework-agnostic YAML files that define a component's API, structure, behaviour, and keyboard interactions. The AI agent generates these before writing any code.
format v1This page documents spec format version 1. Specs declare the version they follow via specVersion; a spec without one reads as version 1. The version bumps only for a breaking change to the format itself: new optional fields never bump it, so version 1 grows backwards-compatibly.
Purpose
Specs serve as the source of truth for what a component does, independent of how it's implemented. The same spec can drive component generation across React, Vue, Lit, Svelte, or any custom platform.
Specs live in specs/*.spec.yaml at the project root.
Recipe Specs
Recipes are non-exported Storybook compositions for full page mockups, flows, dashboards, settings pages, and other examples that show how exported components work together. Recipe specs live in specs/recipes/*.recipe.yaml.
name: SettingsPage type: recipe exported: false description: A full settings page composition using existing primitives. intent: | Demonstrates how Card, Heading, Button, and form controls combine into a dense settings screen. components: - Card - Heading - Button scenarios: - name: Default description: Account profile and notification settings with save actions. decisions: - decision: Recipe uses existing Button and Card components rationale: Keeps the example focused on composition instead of creating new reusable primitives.
Top-Level Fields
| Field | Type | Description |
|---|---|---|
| specVersion | integer | Spec format version (currently 1; absent reads as 1) |
| name | string | PascalCase component name (e.g., "AlertDialog") |
| status | string | Lifecycle: draft, experimental, stable, or deprecated |
| description | string | Brief description of the component's purpose |
| intent | string | What the component is for, when to use it, and how it differs from adjacent components |
| usage | object | Structured do/avoid guidance, with pointers at the better alternative |
| decisions | array | Design decisions with their rationale, the reasoning that travels with the spec |
| api | object | Props and events definitions |
| composition | object | Component dependencies (lists other components used) |
| structure | object | Regions, slots, and layout hints |
| behaviour | object | State machines and interactions |
| keyboard | object | Keyboard interaction bindings |
| accessibility | object | ARIA semantics, focus rules, announcements, motion and contrast constraints |
api
api.props
Each prop is defined with a type, optional enum of allowed values, and a default:
api: props: variant: type: string enum: [primary, secondary, danger, ghost] default: primary description: Visual style variant size: type: string enum: [sm, md, lg] default: md disabled: type: boolean default: false
api.events
Events the component can emit, with optional payload types:
api: events: onClick: description: Fired when the button is clicked payload: MouseEvent onDismiss: description: Fired when the component is dismissed
composition
Declares which other components this component depends on:
composition: uses: - Button - Icon - Badge
structure
Describes the component's internal layout in framework-agnostic terms:
structure: regions: - name: header element: div description: Top section with title and close button - name: body element: div description: Main content area slots: - name: children description: Main content slot - name: footer description: Optional footer actions
behaviour
Defines state machines and interactions using a trigger/guard/effect model:
behaviour: states: - idle - open - closing interactions: - trigger: click guard: "state === 'idle'" effect: "setState('open')" - trigger: escape guard: "state === 'open'" effect: "setState('closing')"
keyboard
Accessibility keyboard bindings, independent of framework:
keyboard: bindings: - key: Enter action: activate description: Activates the button - key: Space action: activate description: Activates the button - key: Escape action: dismiss description: Closes the dialog
usage
Structured do/don't guidance, the checkable form of intent. Each avoid entry can point at the component that fits better:
usage: do: - Surface short, non-blocking confirmation after a user action avoid: - situation: Errors the user must acknowledge before continuing instead: Alert why: Toasts auto-dismiss, so an unacknowledged error can be missed.
accessibility
The accessibility contract beyond keyboard bindings: ARIA semantics, focus management, screen reader announcements, and motion/contrast constraints. Every platform rendering is built from these fields, so assistive-technology behaviour stays identical across React, Vue, Lit, and the rest.
accessibility: aria: - element: root role: alert attributes: aria-live="assertive" aria-atomic="true" rationale: Errors interrupt the screen reader; info toasts use "polite". focus: - Opening the toast never steals focus from the user's current task announcements: - trigger: toast appears message: The toast's text content politeness: polite reducedMotion: Replace the slide-in animation with an opacity fade. contrast: All variants meet WCAG AA (4.5:1) against their token backgrounds.
Full Example
# specs/button.spec.yaml specVersion: 1 name: Button status: stable description: Interactive button with multiple variants and sizes api: props: variant: type: string enum: [primary, secondary, danger, ghost] default: primary size: type: string enum: [sm, md, lg] default: md disabled: type: boolean default: false loading: type: boolean default: false events: onClick: description: Fired on click when not disabled or loading payload: MouseEvent composition: uses: - Icon structure: regions: - name: root element: button description: The button element slots: - name: children description: Button label content - name: icon description: Optional leading icon behaviour: states: - idle - hover - active - loading - disabled interactions: - trigger: click guard: "!disabled && !loading" effect: "emit('onClick')" keyboard: bindings: - key: Enter action: activate - key: Space action: activate