Write and maintain design system documentation using Storybook MDX, Notion, and inline Figma annotations — covering component usage, design decisions, accessibility requirements, and implementation guides that stay accurate as the system evolves.
Design documentation that does not stay current is worse than no documentation — it misleads teams, causes incorrect implementations, and erodes trust in the design system. The challenge is not writing documentation (teams do that at launch) but keeping it accurate as components evolve.
The solution is documentation as close to the component as possible. Storybook MDX puts documentation alongside interactive stories — when a developer updates a component, they see the documentation immediately and update it in the same PR. Figma annotations in Dev Mode stay with the design file. Notion captures decisions that do not belong in code or design files.
This lesson builds a sustainable documentation system for design-development teams.
MDX (Markdown + JSX) lets you write documentation pages in Storybook that include live interactive stories alongside prose documentation.
Creating an MDX documentation page:
1// Button.mdx
2import { Canvas, Controls, Meta, Story } from '@storybook/blocks';
3import * as ButtonStories from './Button.stories';
4
5<Meta of={ButtonStories} />
6
7# Button
8
9The primary interactive element. Use for the most important action on any view.
10
11## When to Use
12
13- **Primary**: One per view, the main conversion action (Enroll, Sign up, Save)
14- **Secondary**: Supporting actions (Preview, Cancel, Back)
15- **Ghost**: Tertiary actions in compact areas (Edit, Remove from list)
16- **Danger**: Destructive actions that cannot be undone (Delete account, Remove course)
17- **Link**: Navigation-style action within body text
18
19## Playground
20
21<Canvas of={ButtonStories.Primary} />
22<Controls of={ButtonStories.Primary} />
23
24## All Variants
25
26<Canvas of={ButtonStories.AllVariants} />
27
28## States
29
30<Canvas of={ButtonStories.AllStates} />
31
32## Accessibility
33
34- Always use a native `<button>` element — never `<div>` or `<a>` for actions
35- Loading state: set `aria-busy="true"` to announce to screen readers
36- Icon-only: add `aria-label` describing the action
37- Disabled: use native `disabled` attribute — not CSS-only
38- Focus ring: visible on keyboard focus via `:focus-visible`
39
40## Usage Guidelines
41// DO: Primary button for main action
<Button variant="primary" onClick={handleEnroll}>Enroll now</Button>
// DON'T: Two primary buttons on same view
<Button variant="primary">Enroll</Button>
<Button variant="primary">Sign up</Button> // Use secondary for second action
// DO: Loading state during async
<Button loading onClick={handlePayment}>Processing...</Button>
// DON'T: Disable instead of loading when waiting for response
<Button disabled>Loading...</Button> // Loses focus, bad UX
1
2## Design Tokens
3
4| Token | Value | Usage |
5|-------------------------|-----------|-------------------------|
6| --color-action | #2563eb | Primary bg |
7| --color-action-hover | #1d4ed8 | Primary hover bg |
8| --color-border-focus | #2563eb | Focus ring |
9| --radius-md | 0.5rem | All button border radius |MDX documentation advantages:
Notion captures design knowledge that does not belong in code (Storybook) or design files (Figma) — decisions, principles, getting-started guides, and changelogs.
Design system Notion structure:
1Design System Workspace
2├── Getting Started
3│ ├── Overview (what the design system is)
4│ ├── Installation (npm install, import tokens)
5│ ├── Contributing Guide (how to add components)
6│ └── Figma Access Guide (how to get Figma viewer access)
7│
8├── Foundations
9│ ├── Design Tokens (all tokens with values and usage)
10│ ├── Typography Scale (all text styles with CSS)
11│ ├── Color System (palette + semantic tokens)
12│ ├── Spacing System (spacing scale with examples)
13│ └── Motion Principles (easing, duration guidelines)
14│
15├── Component Library
16│ ├── Button (status: stable)
17│ ├── Input (status: stable)
18│ ├── Card (status: stable)
19│ ├── Modal (status: stable)
20│ └── Toast (status: beta)
21│
22├── Patterns
23│ ├── Form Design (validation, layout, accessibility)
24│ ├── Loading States (skeleton, spinner, progress)
25│ ├── Error Handling (inline errors, toast errors, empty states)
26│ └── Responsive Patterns (breakpoints, grid, fluid)
27│
28├── Decision Log
29│ └── [table: date, decision, rationale, author]
30│
31└── Changelog
32 └── [table: version, date, changes]Component documentation page template:
1## Button Component
2
3**Status**: Stable
4**Version**: 2.1.0
5**Figma**: [link to component]
6**Storybook**: [link to stories]
7**Source**: src/components/ui/Button.tsx
8**Accessibility**: WCAG 2.1 AA compliant
9
10### Overview
11[1-2 sentence description]
12
13### Usage Examples
14[code snippets for common use cases]
15
16### Variants and Sizes
17[table with variants and when to use each]
18
19### Do / Don't
20[specific guidance with examples]
21
22### Accessibility Notes
23[ARIA attributes, keyboard behavior, focus management]
24
25### Related Components
26[links to components commonly used together]
27
28### Change History
29[table: date, version, change, author]Figma annotations (added with the Annotate tool in design mode) appear as numbered pins in Dev Mode. They are visible to developers without leaving Figma, making them the fastest way to document design intent.
Annotation workflow:
What to annotate:
11. Motion specifications:
2 'Entry animation: translateY(-8px) → translateY(0), opacity 0→1
3 Duration: 200ms, Easing: cubic-bezier(0,0,0.2,1)'
4
52. Interaction behavior:
6 'On mobile: clicking anywhere outside this dropdown closes it
7 On desktop: pressing Escape also closes it
8 Focus management: return focus to the trigger button on close'
9
103. Responsive behavior:
11 'At 768px+: shows all 3 columns
12 At 375-767px: single column, cards full-width
13 At < 375px: adds horizontal scroll'
14
154. Content constraints:
16 'Maximum 2 lines of text — use line-clamp: 2
17 Minimum 1 line — show full text even if very short
18 Empty state: show skeleton placeholder, not empty space'
19
205. Platform-specific notes:
21 'iOS: safe-area-inset-bottom for sticky bar
22 Android: status bar overlap — add paddingTop: StatusBar.currentHeight'
23
246. API/data notes:
25 'This value from GET /courses/:id → duration field
26 Format: 'X hours Y minutes' from raw seconds'Annotation vs component spec:
Annotation best practices:
Documentation maintenance is the hardest part — not writing docs at launch, but keeping them accurate as components evolve. A maintenance process prevents documentation debt.
Documentation maintenance triggers:
1Triggered when any of these happen:
2
31. Component PR merged → documentation review required
4 - Who: developer who made the change
5 - Action: update Storybook MDX, update Notion page
6 - Checklist: 'Did any public API change? Any new states? Any removed variants?'
7
82. Design token changed → token documentation updated
9 - Who: designer who changed the token
10 - Action: update Figma variable description + Notion token table
11
123. New component added → documentation required before release
13 - Who: designer (Figma annotations) + developer (Storybook MDX)
14 - Blocked from release until documentation checklist is complete
15
164. Component deprecated → deprecation notice added
17 - Who: team lead
18 - Action: add deprecation banner to Storybook story + Notion page
19
205. Quarterly audit → all documentation reviewed
21 - Who: design system lead
22 - Action: compare Storybook stories to Figma, flag divergencesComponent documentation release checklist:
1Before any new component ships to production:
2[ ] Storybook stories: all states covered (default, hover, focus, disabled, loading, error)
3[ ] Storybook MDX: overview, usage examples, accessibility notes, design tokens
4[ ] Figma annotations: motion specs, edge cases, responsive behavior
5[ ] Notion component page: overview, variants table, do/don't, changelog entry
6[ ] TypeScript types: all props typed with JSDoc comments
7[ ] README or code comment: links to Storybook and Figma for the componentDocumentation debt prevention:
Quarterly documentation audit:
1Audit script (checks Figma components vs Storybook stories):
2 For each component in Figma library:
3 If no Storybook story: flag as undocumented
4 If Figma last updated > Storybook last updated: flag as potentially stale
5 If no Notion page: flag as undocumented
6
7 Output: documentation debt report
8 Action: assign each flagged item to a team member for next sprintWriting full documentation for a new Toast Notification component — Storybook MDX, Figma annotations, and Notion page — ready for design system release.
1// Toast.mdx — Storybook documentation page
2import { Canvas, Controls, Meta } from '@storybook/blocks';
3import * as ToastStories from './Toast.stories';
4
5<Meta of={ToastStories} />
6
7# Toast Notification
8
9Non-blocking feedback messages that appear in the corner of the screen.
10Use for operation results (success, error) and status updates.
11
12## When to Use
13
14- **Success**: operation completed successfully ('Course enrolled!')
15- **Error**: operation failed with recovery path ('Payment failed. Try again')
16- **Warning**: user should be aware but action is not blocked
17- **Info**: neutral information not requiring action
18
19## When NOT to Use
20
21- Do not use for errors that require immediate user input (use inline form errors instead)
22- Do not use for critical information users must read (use a modal or alert)
23- Do not use for persistent status (use a banner or progress bar)
24
25## Playground
26
27<Canvas of={ToastStories.Success} />
28<Controls of={ToastStories.Success} />
29
30## All Variants
31
32<Canvas of={ToastStories.AllVariants} />
33
34## Behavior
35
36- Appears from the bottom-right corner (desktop) or bottom of screen (mobile)
37- Auto-dismisses after 5 seconds by default
38- Can be dismissed manually by clicking the close (X) button
39- Multiple toasts stack vertically with 8px gap
40- Toast is announced to screen readers via `role='alert'`
41
42## Accessibility
43
44- Uses `role='alert'` to announce to screen readers immediately
45- Contains `aria-live='polite'` on the container for stacked toasts
46- Close button: `aria-label='Dismiss notification'`
47- Color is not the only indicator: icon + border-left color coding
48- WCAG 2.1 AA: all variants pass minimum 4.5:1 contrast
49
50## Usage
51
52```tsx
53import { toast } from '@/hooks/useToast';
54
55// Success
56toast.success('Course enrolled successfully');
57
58// Error with action
59toast.error('Payment failed', {
60 action: { label: 'Try again', onClick: handleRetry }
61});
62
63// Custom duration (milliseconds, 0 = no auto-dismiss)
64toast.info('Your session will expire in 5 minutes', { duration: 0 });
65```
66
67## Design Tokens
68
69| Token | Usage |
70|--------------------|--------------------------|
71| --color-success | Success variant border |
72| --color-danger | Error variant border |
73| --color-warning | Warning variant border |
74| --color-action | Info variant border |
75| --radius-md | Card border radius |
76| --space-4 | Internal padding |
77
78## Motion
79
80Entry: `translateX(100%) → translateX(0)`, 300ms, `cubic-bezier(0,0,0.2,1)`
81Exit: `translateX(0) → translateX(110%)`, 200ms, `cubic-bezier(0.4,0,1,1)`
82Stack: toasts shift vertically 300ms `cubic-bezier(0,0,0.2,1)` when a new one appears
83
84---
85
86/* Figma Annotations for the Toast component: */
87
88// Annotation 1 (on entry animation):
89// 'Entry: slide from right 100% → 0, 300ms cubic-bezier(0,0,0.2,1).
90// Exit: slide to right 110%, 200ms. Stack gap: 8px, animate 300ms.'
91
92// Annotation 2 (on close button):
93// 'aria-label: "Dismiss notification" | Focus: 2px solid #2563EB
94// Keyboard: Enter or Space dismisses | Esc key: no action (only click/Enter)'
95
96// Annotation 3 (on toast container):
97// 'position: fixed; bottom: 1.5rem; right: 1.5rem;
98// On mobile: bottom: 0; right: 0; left: 0; width: 100%;
99// iOS: bottom: env(safe-area-inset-bottom) + 1rem'
100
101// Annotation 4 (on auto-dismiss):
102// 'Auto-dismiss: 5 seconds default. Timer paused on hover.
103// User preference: if prefers-reduced-motion: duration: 0 (no auto-dismiss)
104// because users with motion sensitivity may not read fast enough'
105
106---
107
108// NOTION PAGE: Toast Component
109// Status: Stable (v1.0) | Figma: [link] | Storybook: [link] | Source: Toast.tsx
110//
111// Variants: Success, Error, Warning, Info
112// States: Default, Hovered (close button), Dismissed
113// Accessibility: WCAG 2.1 AA | role=alert | aria-live
114//
115// DO: Use for operation feedback after user action
116// DON'T: Use for critical blocking errors (use Modal instead)
117// DON'T: Use for persistent status (use Banner instead)
118//
119// Design decisions:
120// 'Icon + border-left color' instead of 'full background color'
121// Reason: full colored backgrounds failed contrast for text in testing
122//
123// Changelog:
124// v1.0.0 2026-06-07 Initial release
125// (future changes logged here)The documentation suite covers all four layers: Storybook MDX for interactive documentation with live stories and code examples; Figma annotations for motion specs, ARIA requirements, and iOS-specific notes directly in the design file; Notion page for usage guidelines, decisions, and the changelog. The 'When NOT to Use' section is as important as 'When to Use' — it prevents overuse of the Toast pattern for cases where a modal or banner is more appropriate. The motion annotation includes the prefers-reduced-motion consideration (timer: 0 = no auto-dismiss) which is a behavioral spec that cannot be expressed in the static design.
The next lesson covers Cross-Functional Teams — how design, development, product, and business stakeholders collaborate effectively, and the structures that prevent design becoming a bottleneck in fast-moving product teams.
Your progress is saved in this browser, on this device only.