Design Systems Cheat Sheet
Covers design tokens, component API patterns, governance and versioning practices, and documenting components with Storybook for a scalable system.
Core Building Blocks
What a mature design system is made of.
- Design tokens- Named, platform-agnostic values (color, spacing, typography) that are the source of truth for style
- Component library- Reusable, tested UI components (Button, Input, Modal) built on top of tokens
- Patterns- Documented solutions to recurring UX problems (forms, empty states) composed from components
- Brand guidelines- Voice, tone, imagery, and logo usage rules that complement the visual language
- Accessibility guidelines- Baseline requirements (contrast ratios, focus states, keyboard support) every component must meet
- Documentation site- Living reference, often Storybook, showing component usage, props, and do's/don'ts
Design Tokens
Source-of-truth values transformed for every platform.
{ "color": { "brand": { "500": { "value": "#6366f1" } }, "text": { "primary": { "value": "{color.gray.900}" } } }, "spacing": { "sm": { "value": "8px" }, "md": { "value": "16px" } }, "font": { "size": { "body": { "value": "16px" } } }}
Component API Pattern
Variant/size props keep visual choices constrained.
type ButtonVariant = 'primary' | 'secondary' | 'danger';type ButtonSize = 'sm' | 'md' | 'lg';interface ButtonProps { variant?: ButtonVariant; size?: ButtonSize; isDisabled?: boolean; children: React.ReactNode;}function Button({ variant = 'primary', size = 'md', isDisabled, children }: ButtonProps) { return ( <button className={`btn btn--${variant} btn--${size}`} disabled={isDisabled} aria-disabled={isDisabled} > {children} </button> );}
Governance & Process
What keeps a design system healthy at scale.
- Semantic versioning- Design system packages follow semver; breaking an API or visual contract is a major bump
- Contribution model- Documented process for proposing new components/tokens, e.g. RFC + design + a11y review
- Deprecation policy- Old components/props get a warning and a migration window before removal
- Design-dev pairing- Designers and engineers co-own components so Figma and code never drift apart
- Adoption metrics- Tracking which teams/products use which version, to plan safe rollout of breaking changes
Storybook Documentation
CSF3 story format for documenting a component's states.
// Button.stories.tsximport type { Meta, StoryObj } from '@storybook/react';import { Button } from './Button';const meta: Meta<typeof Button> = { title: 'Components/Button', component: Button, argTypes: { variant: { control: 'select', options: ['primary', 'secondary', 'danger'] }, },};export default meta;type Story = StoryObj<typeof Button>;export const Primary: Story = { args: { variant: 'primary', children: 'Click me' },};
Three-Tier Token Architecture
Core, semantic, and component tokens keep brand changes from rippling into component code.
{ "core": { "blue": { "500": { "value": "#6366f1" } }, "gray": { "900": { "value": "#111827" } } }, "semantic": { "color": { "action-primary": { "value": "{core.blue.500}" }, "text-default": { "value": "{core.gray.900}" } } }, "component": { "button": { "background": { "value": "{semantic.color.action-primary}" } } }}// Core = raw palette. Semantic = intent (what it means).// Component = where it's consumed. Rebrand by editing core only.
Style Dictionary Build Pipeline
Transform a single token source into CSS variables, iOS, and Android outputs.
// style-dictionary.config.jsmodule.exports = { source: ['tokens/**/*.json'], platforms: { css: { transformGroup: 'css', buildPath: 'build/css/', files: [{ destination: 'variables.css', format: 'css/variables' }], }, ios: { transformGroup: 'ios', buildPath: 'build/ios/', files: [{ destination: 'Tokens.swift', format: 'ios-swift/class.swift' }], }, android: { transformGroup: 'android', buildPath: 'build/android/', files: [{ destination: 'colors.xml', format: 'android/colors' }], }, },};// npx style-dictionary build --config style-dictionary.config.js
Compound Component Pattern
Context-driven composition gives consumers layout flexibility without prop explosion.
const TabsContext = React.createContext<{ active: string; setActive: (id: string) => void } | null>(null);function Tabs({ defaultTab, children }: { defaultTab: string; children: React.ReactNode }) { const [active, setActive] = React.useState(defaultTab); return <TabsContext.Provider value={{ active, setActive }}>{children}</TabsContext.Provider>;}Tabs.List = function TabsList({ children }: { children: React.ReactNode }) { return <div role="tablist">{children}</div>;};Tabs.Tab = function Tab({ id, children }: { id: string; children: React.ReactNode }) { const ctx = React.useContext(TabsContext)!; return ( <button role="tab" aria-selected={ctx.active === id} onClick={() => ctx.setActive(id)}> {children} </button> );};// <Tabs defaultTab="a"><Tabs.List><Tabs.Tab id="a">A</Tabs.Tab></Tabs.List></Tabs>
Scaling a System Past One Team
Patterns that keep a design system usable once dozens of teams depend on it.
- Federated contribution- Core team owns primitives/infra; product teams can propose and own domain-specific components via a review gate
- Multi-brand theming- Swap a theme object/CSS custom property scope at the root instead of forking components per brand
- Design system as a product- Treat it with a roadmap, changelog, support channel, and satisfaction survey, not as a side project
- RFC lifecycle- Draft -> design review -> a11y review -> implementation -> beta (opt-in) -> stable, each stage gated on sign-off
- Slot-based theming- Expose CSS variables or style props as override 'slots' instead of unlimited custom className escape hatches
- Component budget- Cap how many near-duplicate components ship (e.g. one Button, not ButtonPrimary/ButtonCta/ButtonAction)
Visual Regression in CI
Catch unintended pixel diffs on every PR by diffing Storybook snapshots.
# .github/workflows/visual-regression.ymlname: Visual Regressionon: [pull_request]jobs: chromatic: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npm ci - uses: chromaui/action@v1 with: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} onlyChanged: true exitZeroOnChanges: false
Version design tokens separately from components — tokens change far more often (a single color tweak) than component APIs, and coupling their releases forces consumers to take unrelated breaking changes just to get a color fix.