Skip to content
Principles

Storybook

Every component is proven in Storybook before it ships. The same stories drive the docs, the browser tests, the visual baselines and a live endpoint for coding agents.

Storybook is where each component is exercised, not a gallery bolted on afterwards. Write a story once and all four surfaces update with it.

Because the stories are shared, they follow a fixed structure. Several of the conventions below are enforced by a script that fails the build.

Inventory

Counted from the source and generated into the docs manifest.

171

story files

63

carry interaction tests

31

anatomy definitions

Framework - the one package every story builds on.

@storybook/react-vite

Addons - everything else loaded into Storybook. All are @storybook-scoped except storybook-addon-pseudo-states, a community package.

@storybook/addon-docs

Story conventions

Every component exports exactly one Base story, wired to the controls panel through {...args} so a reviewer can drive the real component. apps/storybook/scripts/audit-story-structure.ts walks every story file and fails the build if a component is missing its Base or hand-rolls a showcase in its place.

Matrix stories render through a shared VariantGrid, so every component's cross-product of props sits on one layout. StateMatrix puts pseudo-state classes on its cells, which drives real :hover, :focus-visible and :active CSS through storybook-addon-pseudo-states. These are genuinely exercised states, distinct from the prop-forced state columns beside them.

Shared control helpers map awkward props (an icon, an avatar, a kbd) to plain dropdowns, so objects don't leak into the controls panel. Story viewports come from the breakpoint tokens (BREAKPOINT_VALUES), so test frames can't drift from the breakpoints components ship against.

Docs from the same source

Autodocs is on globally, so every component gets a generated reference page. A custom ComponentDocs template (packages/ui/src/dev/component-docs.tsx) adds each component's anatomy table from its defineAnatomy definition, the same trees shown on this site's component pages. One definition feeds both, so the slot names always match.

Stories as tests

The vitest addon runs every story as a real browser render: it mounts the component, plays its interaction script and runs axe in report mode. A story that throws, or whose play step fails, fails the test.

A separate control-effect harness catches dead controls. It renders each Base story with composeStories, flips every control in turn and asserts the DOM changed, which catches props that appear in the panel but do nothing. Playwright visual regression (packages/ui/visual-regression.spec.ts) captures the Base and Matrix stories against committed baselines, with animated components on a documented skip-list so motion doesn't cause false diffs.

Agent access

In development, the MCP addon serves the running catalogue at an MCP endpoint (configured in apps/storybook/.storybook/main.ts), so a coding agent can query live stories and components instead of guessing at the API.

Known gaps

The @storybook/addon-designs package is installed but not yet wired to anything, and accessibility checks run report-only rather than failing the build. Dark mode is covered: visual regression captures every story in both themes, and an opt-in test:browser:dark script runs the axe sweep against the dark token set.