Design Systems & Component Libraries

A design system is the difference between a product that feels cohesive and one that feels like it was built by 15 different people on 15 different days. At scale, without a design system, every new feature introduces visual inconsistencies, accessibility gaps, and duplicated work.

What Is a Design System?

A design system is the single source of truth for your product's interface. It's not just a component library. It's an interconnected set of:

LayerWhat It ContainsPurpose
Design tokensColors, spacing, typography, shadows, radiiShared visual language between design and code
ComponentsButtons, inputs, cards, modals, tablesReusable UI building blocks
PatternsAuth flows, search, settings, navigationReusable solutions to common UX problems
GuidelinesVoice/tone, accessibility standards, writing rulesConsistent decisions across teams
DocumentationUsage guidelines, code examples, dos/don'tsEnables adoption without tribal knowledge

What a Design System Is NOT

Common MisconceptionReality
"It's a Figma file with components"That's a UI kit. A design system includes code, documentation, and governance.
"It's a CSS framework"A framework provides generic styles. A design system is specific to your product.
"We build it once, then we're done"A design system is a living product that evolves with your product.
"Only big companies need one"Even a 3-person team benefits from shared tokens and documented components.

Atomic Design Methodology

Brad Frost's Atomic Design is the most practical mental model for structuring components:

Atoms → Molecules → Organisms → Templates → Pages

Atoms:       The smallest building blocks (can't be broken down further)
             Button, Input, Label, Icon, Badge, Avatar, Checkbox

Molecules:   Groups of atoms working together
             Search field (input + button)
             Form group (label + input + help text)
             Nav item (icon + label)

Organisms:   Complex, distinct sections of the interface
             Header (logo + nav items + search field + user menu)
             Card (image + title + description + actions)
             Data table (header row + data rows + pagination)

Templates:   Page-level layout structures (no real content)
             Dashboard layout, Settings layout, List-detail layout

Pages:       Templates filled with real content
             The actual screens users see

Practical Component Hierarchy

LevelExamplesHow Many?Design Effort
AtomsButton, Input, Badge, Avatar, Icon, Checkbox, Radio, Toggle15-25Low per component, high total
MoleculesForm group, Search bar, Dropdown menu, Card header, Stat card10-20Medium
OrganismsHeader, Sidebar, Data table, Form, Card, Modal, Navigation8-15High
TemplatesDashboard, Settings, List view, Detail view, Auth pages5-10High

Start here: Define your atoms and core molecules first. Organisms and templates emerge naturally from composing them.

Design Tokens

Design tokens are the atomic values that define your visual language. They're the bridge between design (Figma) and code (CSS/JS).

Token Categories

{
  "color": {
    "primary": {
      "50": "#EBF5FF",
      "100": "#CCE5FF",
      "500": "#0066CC",
      "600": "#0052A3",
      "700": "#003D7A"
    },
    "neutral": {
      "0": "#FFFFFF",
      "50": "#F8F9FA",
      "100": "#F1F3F5",
      "500": "#868E96",
      "900": "#212529"
    },
    "semantic": {
      "success": "#2F9E44",
      "warning": "#F08C00",
      "error": "#E03131",
      "info": "#1C7ED6"
    }
  },
  "spacing": {
    "1": "4px",
    "2": "8px",
    "3": "12px",
    "4": "16px",
    "6": "24px",
    "8": "32px",
    "12": "48px",
    "16": "64px"
  },
  "typography": {
    "fontFamily": {
      "sans": "Inter, system-ui, sans-serif",
      "mono": "JetBrains Mono, monospace"
    },
    "fontSize": {
      "xs": "12px",
      "sm": "14px",
      "base": "16px",
      "lg": "18px",
      "xl": "20px",
      "2xl": "24px",
      "3xl": "32px",
      "4xl": "40px"
    },
    "fontWeight": {
      "regular": 400,
      "medium": 500,
      "semibold": 600,
      "bold": 700
    },
    "lineHeight": {
      "tight": 1.2,
      "normal": 1.5,
      "relaxed": 1.6
    }
  },
  "borderRadius": {
    "sm": "4px",
    "md": "8px",
    "lg": "12px",
    "xl": "16px",
    "full": "9999px"
  },
  "shadow": {
    "sm": "0 1px 2px rgba(0,0,0,0.05)",
    "md": "0 4px 6px rgba(0,0,0,0.07)",
    "lg": "0 10px 15px rgba(0,0,0,0.1)",
    "xl": "0 20px 25px rgba(0,0,0,0.15)"
  }
}

Semantic Tokens (Aliases)

Raw tokens (like primary-500) are implementation details. Semantic tokens describe purpose, making theming and dark mode easier:

:root {
  /* Semantic aliases — these are what components reference */
  --color-bg-primary: var(--neutral-0);       /* Main background */
  --color-bg-secondary: var(--neutral-50);    /* Card/section backgrounds */
  --color-bg-inverse: var(--neutral-900);     /* Dark sections */

  --color-text-primary: var(--neutral-900);   /* Headings, important text */
  --color-text-secondary: var(--neutral-500); /* Body text */
  --color-text-on-accent: var(--neutral-0);   /* Text on colored buttons */

  --color-border: var(--neutral-200);         /* Default borders */
  --color-border-focus: var(--primary-500);   /* Focus rings */

  --color-action-primary: var(--primary-500); /* Primary buttons, links */
  --color-action-hover: var(--primary-600);   /* Primary hover state */
}

/* Dark mode — just reassign the aliases */
[data-theme="dark"] {
  --color-bg-primary: var(--neutral-900);
  --color-bg-secondary: #1E1E1E;
  --color-text-primary: var(--neutral-100);
  --color-text-secondary: var(--neutral-400);
  --color-border: var(--neutral-700);
  --color-action-primary: var(--primary-400);
}

Component States

Every interactive component needs these states designed and documented:

Required States

StateVisual TreatmentWhen Active
DefaultBase appearanceNo interaction
HoverSlight background change, cursor pointerMouse over (desktop)
FocusVisible focus ring (2px+ outline)Keyboard navigation or click
Active/PressedSlightly darker, pressed-in feelMouse down / touch
DisabledReduced opacity (50-60%), no pointer cursorAction unavailable
LoadingSpinner or skeleton, disabled interactionAsync operation in progress
ErrorRed border, error icon, error messageValidation failure
SuccessGreen indicator, checkmarkOperation completed
SelectedHighlighted background, checkmarkItem chosen from a list
EmptyIllustration + helpful text + CTANo data to display

State Example: Button

.btn-primary {
  /* Default */
  background: var(--primary-500);
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 150ms ease;
}

.btn-primary:hover {
  background: var(--primary-600);     /* Slightly darker */
}

.btn-primary:focus-visible {
  outline: 2px solid var(--primary-500);
  outline-offset: 2px;               /* Ring visible around button */
}

.btn-primary:active {
  background: var(--primary-700);
  transform: scale(0.98);            /* Subtle press effect */
}

.btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;              /* Prevents click AND hover */
}

.btn-primary.is-loading {
  color: transparent;                 /* Hide text */
  pointer-events: none;
  position: relative;
}
.btn-primary.is-loading::after {
  content: '';
  /* Spinner styles */
}

Designing Empty States

Empty states are often overlooked but they're the first thing new users see. A good empty state includes:

┌─────────────────────────────────────────┐
│                                         │
│          [Illustration/Icon]            │
│                                         │
│       No projects yet                   │  ← What state they're in
│                                         │
│   Create your first project to get      │  ← Why it's empty
│   started tracking your work.           │
│                                         │
│       [+ Create Project]                │  ← Clear action to fix it
│                                         │
└─────────────────────────────────────────┘

Component Documentation Template

Every component in your design system should have:

## Button

### Description
A clickable element that triggers an action.

### Variants
- Primary: Main actions (submit, save, confirm)
- Secondary: Supporting actions (cancel, back)
- Destructive: Dangerous actions (delete, remove)
- Ghost: Minimal-emphasis actions (tertiary actions, icon buttons)

### Sizes
- Small: 32px height, 14px text — dense UIs, inline actions
- Medium: 40px height, 16px text — default
- Large: 48px height, 16px text — CTAs, mobile primary actions

### States
Default, Hover, Focus, Active, Disabled, Loading

### Props/API
- variant: primary | secondary | destructive | ghost
- size: sm | md | lg
- disabled: boolean
- loading: boolean
- icon: ReactNode (optional, leading icon)
- fullWidth: boolean

### Accessibility
- Uses native <button> element
- Disabled buttons use aria-disabled, not removed from tab order
- Loading state announces to screen readers via aria-live

### Do / Don't
✓ Use primary for the single most important action per screen
✗ Don't use multiple primary buttons in the same view
✓ Use verb-based labels: "Save Changes", "Delete Account"
✗ Don't use vague labels: "OK", "Submit", "Click Here"

Building a Design System: Practical Steps

Phase 1: Audit (Week 1)

  1. Screenshot every unique component in your current product
  2. Group similar components (you'll find 5 different button styles)
  3. Document inconsistencies (different padding, colors, border-radius)
  4. Identify the 10-15 most-used components

Phase 2: Define Tokens (Week 2)

  1. Lock down your color palette (primary, neutrals, semantic)
  2. Define your type scale (7-8 sizes)
  3. Define spacing tokens (8px grid)
  4. Define border-radius, shadow, and transition tokens
  5. Implement as CSS custom properties or a tokens file

Phase 3: Build Core Components (Weeks 3-6)

Start with the highest-impact, most-reused components:

PriorityComponentsWhy First
1Button, Input, Select, CheckboxUsed everywhere
2Card, Modal, Dropdown, BadgeCore containers and indicators
3Table, Form, Navigation, TabsComplex, high-value
4Toast, Tooltip, Popover, AvatarSupporting elements

Phase 4: Document and Launch (Week 7-8)

  1. Write usage guidelines for each component
  2. Create a documentation site (Storybook, Docusaurus, or custom)
  3. Provide copy-paste code examples
  4. Show do/don't examples with screenshots

Design Systems to Study

SystemOwnerStrengthURL
Material DesignGoogleDeepest documentationmaterial.io
Human Interface GuidelinesApplePlatform-specific excellencedeveloper.apple.com/design
CarbonIBMEnterprise-focused, accessiblecarbondesignsystem.com
PolarisShopifyE-commerce patterns, excellent docspolaris.shopify.com
Atlassian Design SystemAtlassianTeam collaboration patternsatlassian.design
GOV.UKUK GovernmentAccessibility-first, clear patternsdesign-system.service.gov.uk
RadixWorkOSUnstyled, accessible primitivesradix-ui.com
Shadcn/uiCommunityCopy-paste components, Tailwind-basedui.shadcn.com

Governance

A design system without governance will drift into inconsistency within 6 months.

Versioning

Use semantic versioning (MAJOR.MINOR.PATCH):

Change TypeVersion BumpExample
Breaking change (API, removed component)MAJOR (2.0.0)Button prop type renamed to variant
New component or featureMINOR (1.1.0)Added DatePicker component
Bug fix, style tweakPATCH (1.0.1)Fixed focus ring on Checkbox

Contribution Process

1. Proposal     → Open an issue describing the need
2. Discussion   → Team reviews, asks questions, suggests alternatives
3. Design       → Create designs following existing token system
4. Review       → Design review by system maintainers
5. Build        → Implement with tests and documentation
6. Code Review  → Review by system maintainers
7. Release      → Merge, version bump, changelog update

Maintenance Practices

  • Regular audits: Quarterly review of all components for consistency
  • Usage tracking: Monitor which components are actually used (and which aren't)
  • Deprecation process: Announce deprecation, then provide a migration path, then remove after 2 major versions
  • Changelog: Every change documented with before/after when applicable
  • Office hours: Regular time slot where teams can ask questions or propose changes

Common Mistakes

MistakeImpactFix
Building the system before auditingCreates components nobody usesAudit existing UI first, then build what's needed
Over-engineering from the startSystem never shipsStart with tokens + 5 core components, grow from there
No documentationDevelopers guess how to use componentsDocument every component with examples and guidelines
Design and code divergeFigma shows one thing, code renders anotherSync regularly, or use design-to-code tools
No governance processInconsistencies creep back inAssign maintainers, establish a contribution process
Building for every edge caseComponents become bloated and hard to useBuild for 80% of cases, allow composition for the rest

Key Takeaways

  • A design system is a product, not a project. It needs ongoing maintenance and governance.
  • Start small: tokens + 5 core components. Grow based on actual needs, not speculation.
  • Tokens are the foundation. Define colors, spacing, typography, and shadows before building components.
  • Document every component with variants, states, accessibility notes, and do/don't guidelines.
  • Study established design systems (Material, Polaris, GOV.UK) for patterns and documentation structure.
  • Every component needs at minimum: default, hover, focus, active, disabled, error, and empty states.
  • Use semantic tokens (aliases) instead of raw values so dark mode and theming are simple.