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.txtfile 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:
- Design Tokens: The foundational values (colors, spacing, typography, shadows)
- Components: Reusable UI building blocks built on those tokens
- 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:
| Level | Examples | Description |
|---|---|---|
| Atoms | Button, Input, Icon, Badge | Smallest functional units |
| Molecules | Search Field, Form Group, Card | Combinations of atoms |
| Organisms | Navigation, Hero Section, Footer | Complex, distinct sections |
| Templates | Page layouts, Grid systems | Page-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-primarycolor-text-secondary-hoverspacing-component-gap-lgtypography-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:
- Composition over configuration: Prefer composable parts over prop explosion
- Sensible defaults: Components should work with zero props
- Consistent naming: Use the same prop names across components (
size,variant,disabled) - TypeScript first: Full type safety with autocomplete
- 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
Tooltipcomponent Badgenow supportsdotvariant
Fixed
Inputfocus 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:
| Role | Responsibility |
|---|---|
| Core Team | Maintains packages, reviews PRs, releases |
| Contributors | Proposes changes, submits PRs |
| Consumers | Uses system, reports issues, requests features |
Create a contribution workflow:
- RFC (Request for Comments): Propose significant changes
- Design Review: Validate with design team
- Implementation: Build with tests and documentation
- Review: Core team approval
- 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:
-
Start with design tokens (colors, spacing, typography). They’re the foundation everything else builds on
-
Build atomic components first (buttons, inputs, cards). Master the basics before tackling complex patterns
-
Document usage patterns, not just props. Teams need to know when and why, not just how
-
Use Storybook for component documentation. Interactive docs beat static documentation every time
-
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:
| Task | Human Role | AI Role |
|---|---|---|
| Token definition | Define values, semantic meaning | Generate token files, CSS output |
| Component API design | Decide props, variants, behavior | Suggest patterns from similar systems |
| Implementation | Review, refine, ensure quality | Generate initial code, handle boilerplate |
| Documentation | Write “why” and guidelines | Generate API docs, prop tables |
| Testing | Define test cases, edge cases | Generate test implementations |
| Accessibility | Audit, define requirements | Implement 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:
| System | Started With | Key Differentiator |
|---|---|---|
| Material Design | Design principles | Strong metaphor and motion language |
| Carbon | Pattern audit | Practitioner-led, bottom-up adoption |
| Polaris | Component inconsistency | Content guidelines as first-class citizens |
| Atlassian | Acquisition chaos | Unity without uniformity philosophy |
| Primer | CSS framework | Incremental 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:
-
Week 1-2: Audit your existing products. Screenshot every button, form, card, and modal. You’ll be shocked at the inconsistency.
-
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.
-
Week 5-8: Build your token foundation and 3-5 core components that solve the biggest pain points. Ship them to one team.
-
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.