Building Design Systems from Scratch: A Design Engineer's Complete Guide (2026)

15 min read

A design system is a collection of reusable components, design tokens, and guidelines that ensure consistency across products. Start with color, typography, and spacing tokens before building components. This approach creates a solid foundation that scales across teams and products while maintaining visual and functional consistency.

TL;DR: Start with design tokens (colors, spacing, typography), build atomic components (buttons, inputs), document usage patterns in Storybook, and version your system like software. For AI-assisted development, create an llms.txt file documenting your components and tokens so AI generates consistent, on-brand UI.

Building a design system from scratch is one of the most impactful projects a design engineer can undertake. It’s not just about creating a component library. It’s about establishing a shared language between design and development that accelerates product development and ensures quality at scale.

In this guide, I’ll walk you through the complete process of building a production-ready design system, from foundational tokens to team-wide adoption. Whether you’re starting fresh or formalizing existing patterns, these principles will help you create a system that lasts.

What Is a Design System?

A design system is the single source of truth for building digital products. It combines three essential layers:

  1. Design Tokens: The foundational values (colors, spacing, typography, shadows)
  2. Components: Reusable UI building blocks built on those tokens
  3. Documentation: Guidelines for when and how to use everything

Unlike a simple component library, a design system includes governance, contribution processes, and the principles that guide design decisions. It’s a living product that evolves with your organization.

My Journey Building Design Systems

I’ve built design systems at different scales, from scrappy startups to enterprise teams, and I’ve learned that the technical implementation is often the easy part. The real challenge? Getting buy-in, establishing governance, and creating something people actually want to use.

My first attempt at a design system was a disaster. I spent three months building a comprehensive component library with perfect documentation, only to watch it collect dust. Nobody used it. The problem? I built what I thought teams needed instead of solving their actual pain points.

The turning point came when I started treating the design system as a product, not a project. I interviewed developers and designers, identified their biggest frustrations (inconsistent spacing, recreating the same button for the fifth time, dark mode nightmares), and built solutions for those specific problems. Adoption went from 0% to 80% in two months.

Here’s what I’ve learned works:

  • Start with pain, not perfection: Solve one real problem well before expanding
  • Ship early, iterate often: A basic button component in production beats a perfect one in development
  • Make adoption effortless: If using your system requires more effort than not using it, people won’t use it
  • Document the “why”: Developers can figure out the “how” from code; they need context for decisions
  • Celebrate contributions: Every PR from outside the core team is a win for adoption

Why Design Systems Matter

Organizations with mature design systems report significant improvements in development velocity and design consistency. According to the Sparkbox Design Systems Survey 2025, teams using design systems ship features 34% faster and reduce design-related bugs by 47%.

The benefits compound over time:

  • Consistency: Every product looks and feels unified
  • Efficiency: Developers stop rebuilding the same components
  • Quality: Accessibility and best practices are built in once
  • Scalability: New teams can ship faster with established patterns
  • Communication: Designers and developers share a common vocabulary

Core Components of a Design System

Every robust design system contains these foundational elements:

Foundation Layer

The foundation layer defines the raw design decisions:

├── tokens/
│   ├── colors.json
│   ├── typography.json
│   ├── spacing.json
│   ├── shadows.json
│   ├── borders.json
│   └── motion.json

Component Layer

Components are built on top of tokens and follow atomic design principles:

LevelExamplesDescription
AtomsButton, Input, Icon, BadgeSmallest functional units
MoleculesSearch Field, Form Group, CardCombinations of atoms
OrganismsNavigation, Hero Section, FooterComplex, distinct sections
TemplatesPage layouts, Grid systemsPage-level structures

Documentation Layer

Documentation transforms a component library into a true design system:

  • Usage guidelines: When to use each component
  • Accessibility requirements: WCAG compliance details
  • Code examples: Copy-paste ready implementations
  • Do’s and Don’ts: Visual examples of correct usage
  • Migration guides: How to upgrade between versions

Token Architecture: The Foundation of Everything

Design tokens are the atomic building blocks of your design system. They store design decisions as platform-agnostic data that can be transformed into any format: CSS, iOS, Android, or design tools.

Token Hierarchy

Structure tokens in three tiers for maximum flexibility:

// 1. Primitive Tokens (raw values)
const primitives = {
  blue: {
    50: '#eff6ff',
    100: '#dbeafe',
    500: '#3b82f6',
    600: '#2563eb',
    900: '#1e3a8a'
  },
  spacing: {
    1: '0.25rem',
    2: '0.5rem',
    4: '1rem',
    8: '2rem'
  }
};

// 2. Semantic Tokens (purpose-driven)
const semantic = {
  color: {
    primary: '{blue.600}',
    primaryHover: '{blue.700}',
    background: '{gray.50}',
    text: '{gray.900}',
    textMuted: '{gray.600}'
  },
  spacing: {
    componentGap: '{spacing.4}',
    sectionGap: '{spacing.8}'
  }
};

// 3. Component Tokens (component-specific)
const component = {
  button: {
    primary: {
      background: '{color.primary}',
      backgroundHover: '{color.primaryHover}',
      text: '{color.white}',
      paddingX: '{spacing.4}',
      paddingY: '{spacing.2}'
    }
  }
};

Token Naming Conventions

Use a consistent naming pattern that scales:

[category]-[property]-[variant]-[state]

Examples:

  • color-background-primary
  • color-text-secondary-hover
  • spacing-component-gap-lg
  • typography-heading-1-font-size

Implementing Tokens with Style Dictionary

Style Dictionary transforms tokens into platform-specific outputs:

// config.js
module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: 'dist/css/',
      files: [{
        destination: 'tokens.css',
        format: 'css/variables'
      }]
    },
    js: {
      transformGroup: 'js',
      buildPath: 'dist/js/',
      files: [{
        destination: 'tokens.js',
        format: 'javascript/es6'
      }]
    },
    typescript: {
      transformGroup: 'js',
      buildPath: 'dist/ts/',
      files: [{
        destination: 'tokens.ts',
        format: 'typescript/es6-declarations'
      }]
    }
  }
};

Output CSS:

:root {
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-background: #f9fafb;
  --color-text: #111827;
  --spacing-1: 0.25rem;
  --spacing-2: 0.5rem;
  --spacing-4: 1rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
}

Building Atomic Components

With tokens established, build components following atomic design methodology. Start with the smallest units and compose upward.

Button Component Example

Here’s a production-ready button component using design tokens:

// Button.tsx
import { forwardRef } from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';

const buttonVariants = cva(
  // Base styles using design tokens
  'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
  {
    variants: {
      variant: {
        primary: 'bg-[var(--color-primary)] text-white hover:bg-[var(--color-primary-hover)]',
        secondary: 'bg-[var(--color-secondary)] text-[var(--color-text)] hover:bg-[var(--color-secondary-hover)]',
        outline: 'border border-[var(--color-border)] bg-transparent hover:bg-[var(--color-background-subtle)]',
        ghost: 'hover:bg-[var(--color-background-subtle)]',
        destructive: 'bg-[var(--color-destructive)] text-white hover:bg-[var(--color-destructive-hover)]'
      },
      size: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4 text-base',
        lg: 'h-12 px-6 text-lg'
      }
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md'
    }
  }
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  isLoading?: boolean;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, isLoading, children, disabled, ...props }, ref) => {
    return (
      <button
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        disabled={disabled || isLoading}
        {...props}
      >
        {isLoading ? (
          <span className="mr-2 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
        ) : null}
        {children}
      </button>
    );
  }
);

Button.displayName = 'Button';

Component API Design Principles

Follow these principles for consistent, intuitive APIs:

  1. Composition over configuration: Prefer composable parts over prop explosion
  2. Sensible defaults: Components should work with zero props
  3. Consistent naming: Use the same prop names across components (size, variant, disabled)
  4. TypeScript first: Full type safety with autocomplete
  5. Accessibility built-in: ARIA attributes, keyboard navigation, focus management
// Good: Composable API
<Card>
  <CardHeader>
    <CardTitle>Title</CardTitle>
    <CardDescription>Description</CardDescription>
  </CardHeader>
  <CardContent>Content here</CardContent>
  <CardFooter>
    <Button>Action</Button>
  </CardFooter>
</Card>

// Avoid: Prop explosion
<Card
  title="Title"
  description="Description"
  content="Content here"
  footerContent={<Button>Action</Button>}
/>

Documentation That Actually Gets Used

Documentation is where design systems succeed or fail. The best components are worthless if teams don’t know how to use them correctly.

Storybook Setup

Storybook is the industry standard for component documentation:

npx storybook@latest init

Configure for design systems:

// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-a11y',       // Accessibility testing
    '@storybook/addon-designs',     // Figma embeds
    '@chromatic-com/storybook'       // Visual regression
  ],
  framework: '@storybook/react-vite',
  docs: {
    autodocs: 'tag'
  }
};

export default config;

Writing Effective Stories

Document components with comprehensive stories:

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'outline', 'ghost', 'destructive'],
      description: 'Visual style variant'
    },
    size: {
      control: 'select',
      options: ['sm', 'md', 'lg'],
      description: 'Size of the button'
    },
    isLoading: {
      control: 'boolean',
      description: 'Shows loading spinner and disables interaction'
    }
  },
  parameters: {
    design: {
      type: 'figma',
      url: 'https://figma.com/file/xxx/Design-System?node-id=123'
    }
  }
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    children: 'Primary Button',
    variant: 'primary'
  }
};

export const AllVariants: Story = {
  render: () => (
    <div className="flex gap-4">
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="destructive">Destructive</Button>
    </div>
  )
};

export const Sizes: Story = {
  render: () => (
    <div className="flex items-center gap-4">
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg">Large</Button>
    </div>
  )
};

export const Loading: Story = {
  args: {
    children: 'Loading...',
    isLoading: true
  }
};

Documentation Beyond Code

Include these in your documentation:

{/* Button.mdx */}
import { Canvas, Meta, Story } from '@storybook/blocks';
import * as ButtonStories from './Button.stories';

<Meta of={ButtonStories} />

# Button

Buttons trigger actions or navigation. Use them for the primary calls to action on a page.

## When to Use

- **Primary**: Main action on a page (one per view)
- **Secondary**: Supporting actions
- **Outline**: Lower emphasis actions
- **Ghost**: Tertiary actions, often in toolbars
- **Destructive**: Irreversible or dangerous actions

## Accessibility

- Always include descriptive text or `aria-label`
- Ensure 4.5:1 color contrast ratio
- Support keyboard activation (Enter and Space)
- Disabled buttons should use `aria-disabled` for screen readers

## Do's and Don'ts

**Do**
- Use action verbs: "Save", "Submit", "Delete"
- Keep labels concise (1-3 words)
- Use only one primary button per section

**Don't**
- Use vague labels like "Click here" or "OK"
- Disable buttons without explanation
- Use buttons for navigation (use links instead)

<Canvas of={ButtonStories.AllVariants} />

Scaling Across Teams

A design system’s value multiplies when adopted organization-wide. Here’s how to scale effectively.

Versioning Strategy

Version your design system like software using Semantic Versioning:

MAJOR.MINOR.PATCH

1.0.0 → 1.0.1  (Patch: bug fixes, no API changes)
1.0.0 → 1.1.0  (Minor: new features, backward compatible)
1.0.0 → 2.0.0  (Major: breaking changes)

Maintain a detailed changelog:

# Changelog

## [2.0.0] - 2026-01-10

### Breaking Changes
- Renamed `Button` prop `loading` to `isLoading`
- Removed deprecated `Card` `flat` variant

### Migration Guide
```jsx
// Before
<Button loading>Submit</Button>

// After
<Button isLoading>Submit</Button>

[1.5.0] - 2025-12-15

Added

  • New Tooltip component
  • Badge now supports dot variant

Fixed

  • Input focus ring color in dark mode

### Package Structure

Organize for flexible consumption:

@company/design-system/ ├── packages/ │ ├── tokens/ # @company/tokens │ ├── core/ # @company/core (React components) │ ├── icons/ # @company/icons │ └── themes/ # @company/themes ├── apps/ │ └── docs/ # Storybook documentation └── tools/ └── figma-plugin/ # Figma integration


Teams can install what they need:

```bash
# Full system
npm install @company/design-system

# Just tokens (for non-React projects)
npm install @company/tokens

# Specific packages
npm install @company/core @company/icons

Governance Model

Establish clear ownership and contribution processes:

RoleResponsibility
Core TeamMaintains packages, reviews PRs, releases
ContributorsProposes changes, submits PRs
ConsumersUses system, reports issues, requests features

Create a contribution workflow:

  1. RFC (Request for Comments): Propose significant changes
  2. Design Review: Validate with design team
  3. Implementation: Build with tests and documentation
  4. Review: Core team approval
  5. Release: Versioned release with changelog

Automated Quality Gates

Enforce quality with CI/CD:

# .github/workflows/ci.yml
name: Design System CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install dependencies
        run: npm ci
      
      - name: Lint
        run: npm run lint
      
      - name: Type check
        run: npm run typecheck
      
      - name: Unit tests
        run: npm run test
      
      - name: Build
        run: npm run build
      
      - name: Visual regression (Chromatic)
        uses: chromaui/action@latest
        with:
          projectToken: ${{ secrets.CHROMATIC_TOKEN }}
      
      - name: Accessibility audit
        run: npm run test:a11y

Key Takeaways

Building a design system is a journey, not a destination. Here’s what to remember:

  1. Start with design tokens (colors, spacing, typography). They’re the foundation everything else builds on

  2. Build atomic components first (buttons, inputs, cards). Master the basics before tackling complex patterns

  3. Document usage patterns, not just props. Teams need to know when and why, not just how

  4. Use Storybook for component documentation. Interactive docs beat static documentation every time

  5. Version your design system like software. Semantic versioning and changelogs build trust with consumers

Design Systems in the Age of AI and Vibe Coding

The way we build software is changing. With AI coding assistants like GitHub Copilot, Cursor, and Claude becoming standard tools, and “vibe coding” (where developers describe what they want and let AI generate the implementation) becoming mainstream, design systems need to evolve too.

I’ve been using AI assistants for over a year now, and I’ve noticed something interesting: AI makes design systems more important, not less. Here’s why, and how to adapt your approach.

Why AI Makes Design Systems Essential

When you vibe code without a design system, you get chaos. Ask an AI to “create a button” five times, and you’ll get five different buttons with different padding, colors, border radii, and hover states. The AI doesn’t have context about your brand, your existing patterns, or your accessibility requirements.

But give that same AI a well-documented design system, and everything changes. The AI becomes a force multiplier for consistency rather than a source of entropy.

Without a design system:

You: "Create a primary button"
AI: *generates random button with arbitrary styles*

You: "Create another button for the checkout page"  
AI: *generates completely different button*

Result: Visual chaos, inconsistent UX

With a design system:

You: "Create a primary button using our design system"
AI: *uses your Button component with correct tokens*

You: "Create a checkout button"
AI: *uses the same Button component with appropriate variant*

Result: Consistent, on-brand UI

Structuring Your Design System for AI Consumption

To make your design system AI-friendly, you need to think about how AI assistants consume and understand your code. Here’s what I’ve learned works:

1. Create an AI-readable system overview

Add a DESIGN_SYSTEM.md or llms.txt file at the root of your project:

# Design System Overview

## Quick Start
Import components from `@company/ui`. All components use our design tokens.

## Core Principles
- Use semantic color tokens (primary, secondary) not raw colors
- Spacing uses 4px base unit: 4, 8, 12, 16, 24, 32, 48, 64
- All interactive elements must be keyboard accessible

## Component Usage

### Button
\`\`\`tsx
import { Button } from '@company/ui';

<Button variant="primary" size="md">Label</Button>
\`\`\`
Variants: primary, secondary, outline, ghost, destructive
Sizes: sm, md, lg

### Card
\`\`\`tsx
import { Card, CardHeader, CardContent } from '@company/ui';

<Card>
  <CardHeader>Title</CardHeader>
  <CardContent>Content</CardContent>
</Card>
\`\`\`

## Token Reference
- Colors: --color-primary, --color-secondary, --color-background
- Spacing: --spacing-1 (4px) through --spacing-16 (64px)
- Typography: --font-size-sm, --font-size-base, --font-size-lg

2. Use descriptive, consistent naming

AI models understand semantic naming better than cryptic abbreviations:

// ✅ AI-friendly: Clear, semantic names
<Button variant="destructive" size="large" isLoading>
  Delete Account
</Button>

// ❌ AI-unfriendly: Cryptic abbreviations
<Btn v="dng" sz="lg" ld>
  Delete Account
</Btn>

3. Include JSDoc comments with examples

AI assistants read your comments. Make them count:

/**
 * Primary button component for user actions.
 * 
 * @example
 * // Primary action
 * <Button variant="primary">Save Changes</Button>
 * 
 * @example
 * // Destructive action with confirmation
 * <Button variant="destructive" onClick={handleDelete}>
 *   Delete Project
 * </Button>
 * 
 * @example
 * // Loading state
 * <Button isLoading disabled>
 *   Saving...
 * </Button>
 */
export const Button = ({ variant, size, isLoading, children, ...props }) => {
  // implementation
};

4. Provide copy-paste patterns

Create a patterns library that AI can reference:

// patterns/form-with-validation.tsx
/**
 * Standard form pattern with validation feedback.
 * Use this pattern for all user input forms.
 */
export function FormPattern() {
  return (
    <Form onSubmit={handleSubmit}>
      <FormField>
        <Label htmlFor="email">Email</Label>
        <Input 
          id="email" 
          type="email" 
          placeholder="you@example.com"
          aria-describedby="email-error"
        />
        <FormError id="email-error">
          Please enter a valid email
        </FormError>
      </FormField>
      <Button type="submit" variant="primary">
        Submit
      </Button>
    </Form>
  );
}

Prompting Strategies for Design System Consistency

When vibe coding with a design system, your prompts matter. Here are patterns I use daily:

Reference your system explicitly:

"Using our design system components from @company/ui, create a 
user profile card with avatar, name, email, and an edit button."

Specify constraints:

"Create a pricing table using only our existing Card, Badge, and 
Button components. Use semantic color tokens, not hardcoded colors.
Follow our 8px spacing grid."

Ask for system compliance:

"Review this component and ensure it:
1. Uses design tokens instead of hardcoded values
2. Follows our Button API conventions
3. Includes proper accessibility attributes
4. Matches our existing component patterns"

Request token-based styling:

"Style this component using CSS custom properties from our token 
system. Reference --color-*, --spacing-*, and --font-* variables.
Don't use arbitrary values like #3b82f6 or 16px."

Building Components with AI Assistance

Here’s my workflow for building new design system components with AI:

Step 1: Define the spec

"I need a Tooltip component for our design system. Requirements:
- Appears on hover/focus with 200ms delay
- Positions automatically (top, bottom, left, right)
- Uses --color-tooltip-bg and --color-tooltip-text tokens
- Max width of 200px with text wrapping
- Accessible: role=tooltip, aria-describedby connection
- Keyboard dismissible with Escape
- Follows our existing component API patterns (see Button, Popover)"

Step 2: Generate and review

Let AI generate the initial implementation, then review for:

  • Token usage (no hardcoded values)
  • API consistency with existing components
  • Accessibility compliance
  • TypeScript types

Step 3: Iterate with context

"The Tooltip looks good, but:
1. Change padding to use --spacing-2 and --spacing-3
2. Add a 'variant' prop matching our Badge variants
3. Export types for consumers
4. Add Storybook stories following our Button.stories.tsx pattern"

The Human-AI Design System Workflow

After a year of AI-assisted design system work, here’s the workflow I’ve settled on:

TaskHuman RoleAI Role
Token definitionDefine values, semantic meaningGenerate token files, CSS output
Component API designDecide props, variants, behaviorSuggest patterns from similar systems
ImplementationReview, refine, ensure qualityGenerate initial code, handle boilerplate
DocumentationWrite “why” and guidelinesGenerate API docs, prop tables
TestingDefine test cases, edge casesGenerate test implementations
AccessibilityAudit, define requirementsImplement ARIA, keyboard handling

The key insight: AI accelerates execution, but humans must own the decisions. Your design system’s value comes from the intentional choices: the “why” behind every token, the principles that guide component APIs, the accessibility standards you uphold. AI can’t make those decisions for you, but it can implement them faster than ever before.

Common AI Pitfalls to Avoid

I’ve made these mistakes so you don’t have to:

1. Accepting AI-generated arbitrary values

// ❌ AI often generates hardcoded values
<div style={{ padding: '14px', color: '#6366f1' }}>

// ✅ Always convert to tokens
<div style={{ padding: 'var(--spacing-3)', color: 'var(--color-primary)' }}>

2. Letting AI create inconsistent APIs

// ❌ AI might generate inconsistent prop names
<Button loading={true} />
<Spinner isLoading={true} />
<Card isLoading={true} />

// ✅ Enforce consistency in your prompts
// "Use 'isLoading' for all loading states, matching our Button component"

3. Skipping accessibility in AI prompts

// ❌ Vague prompt
"Create a dropdown menu"

// ✅ Accessibility-aware prompt
"Create an accessible dropdown menu with:
- Keyboard navigation (arrow keys, Enter, Escape)
- ARIA: role=menu, role=menuitem, aria-expanded
- Focus management on open/close
- Screen reader announcements"

4. Not validating AI output against your system

Always run AI-generated components through your quality gates:

  • Does it use only design tokens?
  • Does it follow your component API conventions?
  • Does it pass accessibility audits?
  • Does it match your TypeScript patterns?

The Future: AI-Native Design Systems

Looking ahead, I see design systems evolving to be AI-native, built from the ground up to be consumed by both humans and AI assistants. This means:

  • Structured metadata: Components with machine-readable descriptions of their purpose, constraints, and relationships
  • Semantic tokens everywhere: No arbitrary values that AI might misinterpret
  • Pattern libraries: Documented compositions that AI can reference and adapt
  • Validation layers: Automated checks that catch AI-generated inconsistencies before they ship

The teams that figure this out first will have a massive advantage. Their AI assistants will generate consistent, on-brand UI while competitors struggle with chaos.

My advice? Start now. Document your system for AI consumption. Create that llms.txt file. Write prompts that reference your patterns. The investment pays off immediately in consistency and compounds over time as AI tools improve.

Learning from the Best: How Top Design Systems Started

The most successful design systems didn’t emerge fully formed. They evolved from real problems and grew through iteration. Here’s how some of the industry’s most influential systems got their start, and what you can learn from their journeys.

Material Design (Google)

Origin: Material Design launched in 2014, but its roots trace back to Google’s struggle with inconsistency across products. Gmail looked nothing like Google Maps, which looked nothing like Android. The design team, led by Matías Duarte, set out to create a unified visual language.

Key insight: Material Design started with principles, not components. The team defined a “material” metaphor: digital surfaces that behave like physical paper with realistic shadows and motion. Components came later, built on these foundational principles. For implementing motion in React, Framer Motion provides a declarative API that aligns well with design system principles.

What you can learn: Define your design philosophy before building components. Material’s success came from having a clear, opinionated point of view that guided every decision.

Where they are now: Material Design 3 (2021) introduced dynamic color, making the system more flexible while maintaining consistency. It powers Android, Google Workspace, and countless third-party apps.

Carbon Design System (IBM)

Origin: IBM had over 30 different design systems across the company in 2015. Products looked completely different, and teams were duplicating effort constantly. Carbon emerged from a grassroots effort to unify the experience.

Key insight: Carbon succeeded because it was built by practitioners, not mandated from above. The team started by auditing existing patterns across IBM products, identifying commonalities, and standardizing the best solutions.

What you can learn: Don’t start from scratch. Audit what already exists. The best patterns in your organization are already being used; your job is to find, formalize, and scale them.

Where they are now: Carbon is now open source with implementations for React, Angular, Vue, and Web Components. It includes specialized variants for AI interfaces and data visualization.

Polaris (Shopify)

Origin: Shopify’s design system started in 2017 when the company realized their merchant admin was becoming inconsistent as teams scaled. Different squads were building similar components with subtle differences that confused merchants.

Key insight: Polaris focused heavily on content guidelines alongside components. They recognized that consistent UI means nothing if the words and tone vary wildly. Their content strategy became as important as their component library.

What you can learn: Design systems aren’t just visual. They include voice, tone, and content patterns. Consider how your system addresses the words users see, not just the pixels.

Where they are now: Polaris has evolved to include extensive accessibility guidelines, internationalization support, and a Figma UI kit that stays in sync with code.

Atlassian Design System

Origin: Atlassian’s design system grew from the chaos of acquisitions. As the company bought Trello, Jira, Confluence, and others, each product had its own design language. The design system team formed to create coherence without forcing uniformity.

Key insight: Atlassian embraced “unity, not uniformity.” They created shared foundations (tokens, primitives) while allowing products to maintain distinct personalities. This balance between consistency and flexibility was key to adoption.

What you can learn: Not everything needs to be identical. Define what must be consistent (accessibility, spacing, core interactions) and what can flex (color accents, illustrations, product-specific patterns).

Where they are now: Atlassian Design System includes comprehensive token documentation, accessibility guidelines, and a unique approach to design system governance that balances central control with product autonomy.

Primer (GitHub)

Origin: GitHub’s design system started as a CSS framework in 2016, born from the need to maintain consistency across a rapidly growing codebase. What began as shared styles evolved into a full component system.

Key insight: Primer grew incrementally. Instead of a big-bang rewrite, the team extracted patterns from production code, documented them, and gradually replaced ad-hoc implementations with system components.

What you can learn: You don’t need to stop the world to build a design system. Extract, document, and replace incrementally. This approach reduces risk and builds trust through small wins.

Where they are now: Primer includes React components, CSS utilities, and extensive documentation. It’s open source and powers all of GitHub’s interfaces, including the new GitHub Copilot features.

Lessons from the Giants

Studying these systems reveals common patterns:

SystemStarted WithKey Differentiator
Material DesignDesign principlesStrong metaphor and motion language
CarbonPattern auditPractitioner-led, bottom-up adoption
PolarisComponent inconsistencyContent guidelines as first-class citizens
AtlassianAcquisition chaosUnity without uniformity philosophy
PrimerCSS frameworkIncremental extraction from production

The common thread? None of these systems started with “let’s build a design system.” They started with real problems (inconsistency, duplication, scaling challenges) and built solutions that evolved into systems over time.

My Recommendation for Getting Started

Based on my experience and studying these successful systems, here’s the approach I recommend:

  1. Week 1-2: Audit your existing products. Screenshot every button, form, card, and modal. You’ll be shocked at the inconsistency.

  2. Week 3-4: Interview 5-10 developers and designers. Ask: “What’s the most frustrating thing about building UI here?” Their answers will prioritize your roadmap.

  3. Week 5-8: Build your token foundation and 3-5 core components that solve the biggest pain points. Ship them to one team.

  4. Month 3+: Iterate based on feedback, expand component coverage, and gradually onboard more teams.

This isn’t the only path, but it’s one that balances speed with sustainability. The goal is to create value quickly while building toward something comprehensive.

The best design systems evolve with their organizations. Start small, ship early, and iterate based on real usage. Your future self and every team that builds with your system will thank you.


Ready to dive deeper? Explore how React Server Components can optimize your design system’s performance, or learn about the modern design engineer role that makes this work possible.

Frequently Asked Questions

What is a design system?
A design system is a collection of reusable components, design tokens, and guidelines that ensure consistency across products. It includes foundational elements like color palettes, typography scales, and spacing systems, along with UI components and documentation that teams use to build cohesive user experiences.
What are design tokens?
Design tokens are the smallest, most atomic pieces of a design system. They store design decisions as named variables (colors, spacing, typography, shadows, and more) in a platform-agnostic format that can be transformed into CSS, iOS, Android, or any other platform's native styling language.
How do I start building a design system?
Start with design tokens (colors, spacing, typography) before building components. Define your foundational tokens first, then create atomic components like buttons and inputs. Document usage patterns, not just props, and use Storybook for interactive documentation. Version your design system like software.
What is the difference between a component library and a design system?
A component library is a collection of reusable UI components, while a design system is broader. It includes the component library plus design tokens, documentation, usage guidelines, accessibility standards, and governance processes. A design system is the complete ecosystem for building consistent products.
How do I scale a design system across multiple teams?
Scale through clear governance, semantic versioning, and comprehensive documentation. Establish a core team for maintenance, create contribution guidelines, use automated testing and visual regression tools, and publish your system as versioned packages that teams can depend on reliably.
Should I use CSS variables or CSS-in-JS for design tokens?
CSS custom properties (variables) offer runtime theming and broad compatibility, while CSS-in-JS provides type safety and co-location with components. Many modern design systems use both: CSS variables for runtime theming and CSS-in-JS for component-scoped styles with TypeScript integration.
What are the best design systems to learn from?
The most influential open-source design systems include Material Design (Google), Carbon (IBM), Polaris (Shopify), Atlassian Design System, and Primer (GitHub). Each offers unique insights: Material for design principles, Carbon for enterprise patterns, Polaris for content guidelines, and Primer for incremental adoption strategies.
How long does it take to build a design system?
A minimal viable design system with tokens and 5-10 core components can be built in 4-8 weeks. However, a comprehensive system with full documentation, governance, and broad adoption typically takes 6-12 months to mature. The key is shipping incrementally rather than waiting for perfection.
How do I use a design system with AI coding assistants?
Create an AI-readable overview file (like llms.txt or DESIGN_SYSTEM.md) documenting your components, tokens, and patterns. Use semantic naming, include JSDoc comments with examples, and explicitly reference your design system in prompts. AI assistants work best when given clear constraints and examples to follow.
Does vibe coding make design systems obsolete?
No. AI makes design systems more important. Without a design system, AI generates inconsistent UI with arbitrary values. With a well-documented system, AI becomes a force multiplier for consistency. The key is structuring your system for AI consumption with clear documentation, semantic tokens, and copy-paste patterns.

Sources & References