Component-Based Design & Design Systems
TL;DR
Components: Reusable, self-contained UI building blocks with defined props, slots, and events. Enable consistency and reduce duplication.
Design Tokens: Semantic variables (colors, typography, spacing, shadows) that encode design decisions. Source of truth for visual consistency.
Design Systems: Documented collection of components, tokens, patterns, and guidelines. Single source of truth for UI across products.
Atomic Design: Hierarchy of components (atoms → molecules → organisms → templates → pages) for scalable composition.
Storybook: Interactive documentation tool for developing, testing, and showcasing components in isolation.
Learning Objectives
You will be able to:
- Design component APIs (props, slots, events) for flexibility, clarity, and type safety.
- Implement design tokens and token systems for visual consistency.
- Document components and patterns in Storybook for discoverability and collaboration.
- Version design systems and manage breaking changes.
- Scale design systems across multiple products and teams.
Motivating Scenario
Your company has 5 products: web app, mobile app, admin dashboard, marketing site, and partner portal. Each team rebuilds the same components (Button, Card, Modal, Form Input) slightly differently. Button styling differs across products. Form validation logic is reimplemented 5 times. A "danger" red is #ff0000 in web, #dd0000 in mobile, #ff3333 in partner portal. Marketing site has completely different Button component.
Users experience inconsistency. Designers maintain 5 separate design files. Engineers duplicate code. Simple feature (add tooltip to Button) requires changes in 5 places.
A design system solves this: single source of truth for all UI across all products. Teams use same Button, same color palette, same spacing rules. Changes cascade automatically. New team onboards in days, not weeks, by consuming the design system.
Core Concepts
What Are Components?
Components are encapsulated, reusable UI elements with:
- Defined Props: Inputs that configure appearance and behavior
- Slots/Children: Flexible content areas for composition
- Events: Callbacks for user interactions
- Styling: Internal styling, ideally scoped
Example: Button component
interface ButtonProps {
variant: 'primary' | 'secondary' | 'danger';
size: 'sm' | 'md' | 'lg';
disabled?: boolean;
loading?: boolean;
onClick?: (e: React.MouseEvent) => void;
children: React.ReactNode;
}
export function Button({
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
onClick,
children,
}: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled || loading}
onClick={onClick}
aria-busy={loading}
>
{loading && <Spinner size={size} />}
{children}
</button>
);
}
Benefits:
- Consistency: All buttons follow same visual rules
- Reusability: Import and use everywhere
- Maintainability: Change button logic in one place, applies globally
- Accessibility: Accessibility features built once, inherited everywhere
What Are Design Tokens?
Design tokens are semantic variables encoding design decisions:
{
"color": {
"primary": {
"50": "#f0f9ff",
"100": "#e0f2fe",
"500": "#0ea5e9",
"600": "#0284c7",
"900": "#0c2d6b"
},
"neutral": {
"50": "#fafafa",
"500": "#737373",
"900": "#171717"
},
"semantic": {
"success": "#10b981",
"warning": "#f59e0b",
"error": "#ef4444",
"info": "#3b82f6"
}
}
}
Instead of hardcoding colors:
/* Before: hardcoded */
.button {
background-color: #0ea5e9;
}
.button:hover {
background-color: #0284c7;
}
/* After: token-based */
.button {
background-color: var(--color-primary-500);
}
.button:hover {
background-color: var(--color-primary-600);
}
Benefits:
- Consistency: All buttons use same primary color
- Maintainability: Change primary color once, updates everywhere
- Scalability: Add dark mode by swapping token values
- Traceability: Know exactly where each color is defined
What Are Design Systems?
Design systems codify all design and component decisions into documentation:
Components + Tokens + Patterns + Guidelines = Design System
A design system includes:
- Component Library: Buttons, cards, modals, forms, etc.
- Design Tokens: Colors, typography, spacing, shadows
- Patterns: Common UI patterns (forms, tables, navigation)
- Documentation: Usage guidelines, do's/don'ts, accessibility notes
- Examples: Real-world usage of components
Popular design systems:
- Material Design (Google): Comprehensive, design-token-first
- Fluent Design (Microsoft): Focus on productivity
- Carbon (IBM): Enterprise-focused, accessible
Building Design Systems
Atomic Design Hierarchy
Atoms
├─ Button
├─ Input
├─ Label
├─ Icon
└─ Typography
Molecules
├─ Form Group (Label + Input)
├─ Card (Heading + Body + Actions)
├─ Breadcrumb (Link + Separator)
└─ Toast Notification
Organisms
├─ Form (Multiple Form Groups)
├─ Header (Nav + Search + User Menu)
├─ Table (Header + Rows + Pagination)
└─ Modal (Title + Body + Footer)
Templates
├─ Login Page Template
├─ Product List Template
└─ Dashboard Template
Pages
├─ Login Page (instance of template with real data)
├─ Product Listing (instance of template)
└─ Dashboard (instance of template)
This hierarchy enables:
- Composition: Complex UI from simple pieces
- Reusability: Molecules use atoms, organisms use molecules
- Testing: Test atoms first, then molecules, then organisms
- Team scaling: Teams work at different levels of abstraction
Implementing Design Tokens
Use a token framework to generate code from tokens:
- Design Tokens (JSON)
- Generated CSS
- Generated TypeScript
{
"color": {
"primary": {
"base": { "value": "#0ea5e9", "type": "color" }
}
},
"spacing": {
"xs": { "value": "4px", "type": "dimension" },
"sm": { "value": "8px", "type": "dimension" },
"md": { "value": "16px", "type": "dimension" },
"lg": { "value": "24px", "type": "dimension" }
},
"typography": {
"body": {
"fontSize": { "value": "14px", "type": "dimension" },
"lineHeight": { "value": "1.5", "type": "number" },
"fontWeight": { "value": "400", "type": "fontWeight" }
}
}
}
:root {
--color-primary-base: #0ea5e9;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--typography-body-fontSize: 14px;
--typography-body-lineHeight: 1.5;
}
export const tokens = {
color: {
primary: {
base: '#0ea5e9',
},
},
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
},
typography: {
body: {
fontSize: '14px',
lineHeight: '1.5',
fontWeight: '400',
},
},
} as const;
Use tools like Tokens Studio, Amazon Style Dictionary, or Figma Tokens to generate tokens from design files.
Building a Component Library with Storybook
Storybook is interactive documentation for components:
- Button Component
- Storybook Stories
export function Button({
variant = 'primary',
size = 'md',
disabled = false,
children,
onClick,
}) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
export default {
title: 'Components/Button',
component: Button,
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'select',
options: ['sm', 'md', 'lg'],
},
disabled: {
control: 'boolean',
},
},
};
export const Primary = {
args: { variant: 'primary', children: 'Click me' },
};
export const Secondary = {
args: { variant: 'secondary', children: 'Click me' },
};
export const Disabled = {
args: { disabled: true, children: 'Disabled' },
};
export const AllSizes = {
render: () => (
<div style={{ display: 'flex', gap: '10px' }}>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
),
};
Benefits of Storybook:
- Visual regression testing: Detect unintended style changes
- Documentation: Auto-generated from props and stories
- Development: Develop components in isolation
- Collaboration: Designers and engineers review components together
- Testing: Snapshot tests, accessibility checks
Versioning and Deprecations
Manage breaking changes carefully:
/**
* @deprecated Use `ButtonV2` instead. Will be removed in v3.0.0.
* @example
* // OLD
* <Button appearance="primary" />
*
* // NEW
* <Button variant="primary" />
*/
export function Button({ appearance, ...props }) {
return <ButtonV2 variant={appearance} {...props} />;
}
export function ButtonV2({ variant, ...props }) {
return <button className={`btn-${variant}`} {...props} />;
}
Semantic versioning for design systems:
- Major: Breaking changes to API or tokens (Button prop rename)
- Minor: New components or tokens (new color added)
- Patch: Bug fixes, documentation (typo correction)
Patterns & Pitfalls
Pattern: Compound Components
Allow flexible composition:
// Allow flexible layout
export function Form({ children }) {
return <form>{children}</form>;
}
Form.Group = function FormGroup({ label, children }) {
return (
<div className="form-group">
<label>{label}</label>
{children}
</div>
);
};
Form.Input = function FormInput(props) {
return <input {...props} />;
};
Form.Error = function FormError({ message }) {
return <span className="form-error">{message}</span>;
};
// Usage
<Form>
<Form.Group label="Email">
<Form.Input type="email" />
<Form.Error message="Invalid email" />
</Form.Group>
</Form>
Pitfall: Over-Engineering
Problem: Adding too many props/variants. Button with 50 combinations.
Mitigation: Limit variants to real use cases. Delete unused props annually.
// Too many props
<Button variant="secondary" size="lg" loading disabled shape="rounded" />
// Better: constrain to real use cases
<Button variant="secondary" size="lg" loading />
Pitfall: Design System Fragmentation
Problem: Teams ignore design system, build custom components.
Mitigation:
- Make design system easy to use (good DX)
- Enforce via linting rules
- Educate teams on benefits
- Gather feedback and iterate
Operational Considerations
Maintenance and Updates
Design systems require active maintenance:
- Update cycle: Review and update tokens quarterly
- Component feedback: Track issues, deprecate, sunset old versions
- Documentation: Keep examples and patterns current
- Testing: Visual regression tests on every change
Scaling to Multiple Platforms
Use design tokens to generate code for web, iOS, Android:
{
"color": {
"primary": "#0ea5e9"
}
}
Generate:
// iOS
let primaryColor = UIColor(red: 0.06, green: 0.65, blue: 0.92, alpha: 1.0)
// Android
val primaryColor = Color(0xFF0EA5E9)
/* Web */
--color-primary: #0ea5e9;
Design Review Checklist
- Is component API clear and well-documented (props, slots, events)?
- Are all design tokens defined and used consistently?
- Does Storybook cover all component variants and states?
- Are accessibility features built into components (ARIA, keyboard nav)?
- Is component library versioned (semver)?
- Are deprecated components clearly marked with migration paths?
- Is styling scoped (CSS Modules, BEM, CSS-in-JS) to prevent conflicts?
- Are type definitions complete (TypeScript interfaces)?
- Are visual regression tests configured?
- Is documentation current and helpful?
- Are tokens generated for all platforms (web, iOS, Android)?
When to Use / When Not to Use
Build a Design System When:
- Multiple products/teams (3+) sharing UI
- Need consistency across platforms
- Team size large enough to maintain (5+ engineers)
- Long-term product (multi-year roadmap)
Skip Design System If:
- Single product, single team
- Short-lived project
- One-off internal tool
- Rapidly changing design (prototyping phase)
Showcase: Design System Maturity Levels
Self-Check
- Why is atomic design useful? What problems does it solve?
- How would you handle a breaking change to the Button component (e.g., renaming
appearancetovariant) across 5 products? - What are the benefits and drawbacks of design tokens vs. hardcoded CSS values?
Next Steps
- Storybook: Develop Components in Isolation ↗️
- Learn about Performance & Core Web Vitals ↗️
- Explore State Management ↗️
- Study Design Tokens Format Specification ↗️
One Takeaway
Design systems are investments in consistency and velocity. Start with a small component library (Button, Card, Input). Document in Storybook. Use design tokens for visual consistency. As teams scale, the system becomes more valuable and required.
References
- Storybook: UI Component Development Environment
- Design Tokens Format Specification
- Atomic Design by Brad Frost
- Amazon Style Dictionary: Design Token Generator
- Design Systems 101 - Nielsen Norman