Tutorial: Build a Production Design System Kernel
Why this tutorial matters
A design system is not a component gallery. It is a product infrastructure layer: tokens, primitives, accessibility contracts, API design, theming, documentation, testing, release governance, and migration support. This tutorial builds a mid-level Design System Kernel that can support product teams and GenUI component registries.
The word "kernel" is intentional. You are not building every component. You are building the smallest stable foundation that future components can trust.
What you will build
| Asset | Purpose |
|---|---|
| Design tokens | Shared color, spacing, typography, radius, motion, z-index |
| Primitive components | Button, TextField, Dialog, Tabs, Toast, DataTable shell |
| Accessibility contracts | Keyboard, focus, labels, ARIA, live regions |
| Storybook docs | Isolated states, usage examples, anti-patterns |
| Test harness | Unit, interaction, accessibility, visual checks |
| Release process | Versioning, changelog, migration notes |
| GenUI registry bridge | Approved components and prop schemas for AI-generated UI |
Milestone 1: Token architecture
Create layered tokens:
base tokens color.blue.600, space.4, font.size.3
semantic tokens color.action.primary, color.surface.danger
component tokens button.primary.bg, dialog.backdrop.bg
Rules:
- product code uses semantic/component tokens, not raw palette tokens
- dark/high-contrast themes override semantic tokens
- tokens are exported to CSS variables and TypeScript types
- token changes have visual regression coverage
Acceptance criteria:
- Button and Dialog can switch theme without changing component code.
- No hard-coded product colors remain in kernel examples.
- Token documentation explains intended usage, not only values.
Milestone 2: Build accessible primitives
Start with six primitives:
| Component | Required states |
|---|---|
| Button | default, hover, focus-visible, loading, disabled, destructive |
| TextField | label, hint, error, required, disabled, async validation |
| Dialog | open, close, focus trap, restore focus, escape, backdrop |
| Tabs | keyboard navigation, selected state, disabled tab |
| Toast | status, error, action, live region behavior |
| DataTable shell | caption, headers, empty, loading, row action |
For each component, document:
- anatomy
- props
- accessibility behavior
- keyboard behavior
- error states
- composition limits
- examples and anti-patterns
Acceptance criteria:
- Every form input has label and error association.
- Dialog focus is trapped and restored.
- Tabs work with keyboard.
- Toasts use appropriate live region behavior.
Milestone 3: Component API design
Design APIs for durable use.
Bad:
<Button blue big rounded shadow submitThing />
Better:
<Button variant="primary" size="md" type="submit" isLoading={saving}>
Save changes
</Button>
API rules:
- use semantic variants, not visual adjectives
- avoid boolean prop explosions
- allow composition where product needs vary
- keep destructive and loading states explicit
- use controlled/uncontrolled patterns intentionally
- reserve escape hatches for reviewed cases
Acceptance criteria:
- Button and TextField APIs support common product use without style overrides.
- Invalid combinations are prevented by types or runtime warnings.
- Component docs explain when not to use the component.
Milestone 4: Storybook as verification surface
Storybook should become a workshop and contract surface.
Required story categories:
- default
- all variants
- long text
- narrow viewport
- keyboard/focus
- loading
- empty
- error
- disabled
- RTL or localization stress where relevant
Quality gates:
- story exists for every public component
- accessibility addon/manual checks run on stories
- visual snapshots cover stable states
- docs include usage and anti-patterns
Acceptance criteria:
- A reviewer can inspect every critical state without running the app.
- Component examples are not marketing demos; they include failure states.
Milestone 5: Theming and white-label constraints
Add two themes:
- default
- high-contrast or partner theme
Theming contract:
| Concern | Rule |
|---|---|
| color | semantic tokens only |
| spacing | component density tokens |
| typography | role-based text styles |
| logos/images | app-level, not primitive-level |
| motion | respects reduced motion |
| contrast | verified for text and controls |
Acceptance criteria:
- Components pass contrast checks in both themes.
- Theme change does not alter layout unexpectedly.
- Partner theme cannot override component behavior.
Milestone 6: GenUI registry bridge
Create a registry file that exposes only safe components to GenUI:
export const genUIRegistry = {
summary_panel: {
version: "1",
component: SummaryPanel,
propsSchema: SummaryPanelSchema,
risk: "display",
},
approval_action: {
version: "1",
component: ApprovalAction,
propsSchema: ApprovalActionSchema,
risk: "approval",
},
};
Rules:
- not every design-system component is GenUI-safe
- GenUI props are stricter than React props
- generated content must have fallback and validation
- AI-accessible components include accessibility requirements
Acceptance criteria:
- Unknown GenUI components are rejected.
- Invalid GenUI props render fallback.
- Approval components require explicit action and audit metadata.
Milestone 7: Release governance
Use semver-like rules:
| Change | Version impact |
|---|---|
| new optional prop | minor |
| visual fix within contract | patch |
| removed prop | major |
| changed keyboard behavior | major or explicit migration |
| token semantic change | migration note required |
Release checklist:
- changelog
- migration notes
- visual regression
- accessibility checks
- consuming app smoke test
- design review signoff for behavior changes
Package structure
Use a package layout that separates primitives, tokens, docs, and test utilities:
packages/design-system/
src/
tokens/
primitives/
recipes/
theme/
genui/
testing/
stories/
docs/
| Folder | Rule |
|---|---|
tokens | no React imports |
primitives | generic accessible components |
recipes | composed patterns such as form rows or empty states |
theme | CSS variables and theme providers |
genui | strict schemas and safe registry exports |
testing | helpers for consumers |
Component readiness checklist
Before a component is public:
- props are typed and documented
- keyboard behavior is documented
- focus behavior is tested
- error/loading/empty states exist
- long text and localization stress stories exist
- accessibility tests run
- visual snapshots exist
- mobile/narrow layout story exists
- usage examples include do/don't guidance
If a component lacks failure states, it is not production-ready.
Product adoption plan
Adopt through one vertical slice:
- Replace buttons and text fields in one form.
- Replace dialog behavior in one high-risk workflow.
- Add table shell to one data-heavy surface.
- Move one route to semantic tokens.
- Add Storybook links to product PRs.
Track:
- component adoption
- style override count
- accessibility defects by component
- duplicate component implementations
- design-token violations
GenUI-specific hardening
For GenUI-safe components:
| Requirement | Reason |
|---|---|
| strict prop schema | model output is untrusted |
| max text length | prevents layout breakage |
| required labels | protects accessibility |
| allowed event names | prevents arbitrary actions |
| citation support where needed | supports trust |
| fallback rendering | handles invalid generated UI |
Do not expose raw Dialog, Table, or Form composition to GenUI until you have safe wrappers. AI should target recipes, not low-level primitives.
Capstone review
Answer:
- Which components are primitives vs product-specific?
- Which tokens can product teams use?
- Which accessibility behaviors are guaranteed?
- Which escape hatches exist and who approves them?
- Which components are safe for GenUI?
- How are breaking changes communicated?
Source lens
This tutorial is informed by Storybook's component isolation/documentation model, WCAG 2.2 principles, and the book's chapters on design systems, tokens, durable component APIs, accessibility contracts, theming, and inclusive component recipes.