Skip to content

Design System Basics ​

A design system is not a UI kit --- it is the single source of truth that keeps your SaaS product consistent as your team and codebase scale.

Why This Matters ​

Without a design system, every developer and designer reinvents the wheel. Buttons look different on every page. Spacing is inconsistent. Colors drift. The product feels stitched together rather than unified.

RoleWhy You Should Care
🏒 OwnerInconsistent UI erodes trust. A design system reduces design/dev time by 30-50% once established.
πŸ’» DevA component library means building features, not rebuilding buttons. Fewer bugs, faster shipping.
πŸ“‹ PMConsistent patterns let you spec features faster. "Use the standard data table" vs. describing every detail.
🎨 DesignerFrees you from pixel-pushing to focus on solving actual user problems.

The Concept (Simple) ​

Think of a design system like LEGO bricks. Each brick (component) is well-defined, consistent, and connects to other bricks in predictable ways. You don't redesign the brick every time --- you combine existing bricks to build new things.

A design system has three layers:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           PATTERNS                  β”‚  ← How components combine
β”‚  (forms, data tables, navigation)   β”‚    into common layouts
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚           COMPONENTS                β”‚  ← Reusable UI elements
β”‚  (buttons, inputs, cards, modals)   β”‚    built from tokens
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚           TOKENS                    β”‚  ← Foundational values
β”‚  (colors, spacing, typography)      β”‚    everything inherits from
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Change a token, and everything built on it updates. Change a component, and every pattern using it updates. That is the power.

How It Works (Detailed) ​

Design Tokens ​

Tokens are the atomic values your entire UI inherits from. They are named, not hard-coded.

Color Tokens:

Token NameValueUsage
--color-primary#2563EBPrimary buttons, links, active states
--color-primary-hover#1D4ED8Hover state for primary elements
--color-secondary#64748BSecondary buttons, muted text
--color-success#16A34ASuccess messages, positive indicators
--color-warning#D97706Warnings, caution states
--color-danger#DC2626Errors, destructive actions
--color-bg-primary#FFFFFFMain background
--color-bg-secondary#F8FAFCCard backgrounds, sidebar
--color-bg-tertiary#F1F5F9Table row hover, input backgrounds
--color-border#E2E8F0Borders, dividers
--color-text-primary#0F172AHeadings, body text
--color-text-secondary#64748BCaptions, labels, hints

Spacing Scale:

Token                Value    Usage
──────────────────────────────────────────────
--space-1            4px      Tight internal padding
--space-2            8px      Icon-to-label gap
--space-3           12px      Small component padding
--space-4           16px      Standard component padding
--space-5           20px      Card internal padding
--space-6           24px      Section spacing
--space-8           32px      Major section breaks
--space-10          40px      Page-level padding
--space-12          48px      Large section separators
--space-16          64px      Hero/banner spacing

Typography Scale:

TokenSizeWeightLine HeightUsage
--text-xs12px40016pxLabels, captions, badges
--text-sm14px40020pxSecondary text, table cells
--text-base16px40024pxBody text, inputs
--text-lg18px50028pxCard titles, sub-headings
--text-xl20px60028pxSection headings
--text-2xl24px60032pxPage titles
--text-3xl30px70036pxDashboard KPIs

Why tokens, not raw values?

BAD:  .button { background: #2563EB; }     ← Magic number. What is this?
BAD:  .card   { background: #2563EB; }     ← Same color, but is that intentional?

GOOD: .button { background: var(--color-primary); }     ← Clear intent
GOOD: .card   { background: var(--color-bg-secondary); } ← Different token, clear purpose

If you rebrand and your primary color changes from blue to purple, you update ONE token. Every button, link, and active state updates automatically.

Component Library Structure ​

A well-organized component library follows an atomic design hierarchy.

COMPONENT HIERARCHY
===================

PRIMITIVES (atoms)
β”œβ”€β”€ Button          [primary | secondary | ghost | danger]
β”œβ”€β”€ Input           [text | number | email | password]
β”œβ”€β”€ Checkbox
β”œβ”€β”€ Radio
β”œβ”€β”€ Toggle
β”œβ”€β”€ Badge           [info | success | warning | danger]
β”œβ”€β”€ Avatar          [sm | md | lg | xl]
β”œβ”€β”€ Icon
β”œβ”€β”€ Tooltip
└── Spinner

COMPOSITES (molecules)
β”œβ”€β”€ Form Field      = Label + Input + Help Text + Error
β”œβ”€β”€ Search Bar      = Icon + Input + Clear Button
β”œβ”€β”€ Dropdown        = Button + Menu + Menu Items
β”œβ”€β”€ Breadcrumb      = Links + Separators
β”œβ”€β”€ Pagination      = Buttons + Page Numbers
β”œβ”€β”€ Tab Group       = Tab Items + Active Indicator
β”œβ”€β”€ Alert           = Icon + Text + Dismiss Button
└── Tag Input       = Input + Badge list

PATTERNS (organisms)
β”œβ”€β”€ Data Table      = Table + Pagination + Sort + Filter
β”œβ”€β”€ Form            = Form Fields + Buttons + Validation
β”œβ”€β”€ Modal           = Overlay + Card + Header + Footer
β”œβ”€β”€ Sidebar Nav     = Nav Groups + Nav Items + Collapse
β”œβ”€β”€ Command Palette = Search + Results + Keyboard Nav
β”œβ”€β”€ Card Grid       = Cards + Responsive Layout
β”œβ”€β”€ Settings Panel  = Sections + Form Fields + Save
└── Empty State     = Illustration + Text + CTA

Each component should define:

  • [ ] Props/variants --- what configurations are available
  • [ ] States --- default, hover, focus, active, disabled, loading, error
  • [ ] Accessibility --- ARIA labels, keyboard navigation, focus management
  • [ ] Responsive behavior --- how it adapts at breakpoints
  • [ ] Usage guidelines --- when to use (and when not to)

Build vs Buy Comparison ​

FactorBuild Your OwnUse Existing (Radix, Headless UI, shadcn)
Time to start3-6 months1-2 weeks
CustomizationTotal controlModerate to high (depends on library)
MaintenanceYour team owns it allCommunity + your customization layer
ConsistencyAs good as your disciplineStrong baseline, you extend
AccessibilityMust build from scratchOften built-in
CostHigh dev hours upfrontLow upfront, possible constraints later
Best forLarge teams (10+ devs), unique needsSmall-to-mid teams, standard SaaS patterns

Recommendation for most SaaS startups:

START HERE ──> Use headless component library (Radix, Headless UI)
                    β”‚
                    β–Ό
              Add your design tokens (colors, spacing, fonts)
                    β”‚
                    β–Ό
              Build a thin wrapper layer (your <Button>, your <Input>)
                    β”‚
                    β–Ό
              Compose into patterns (your <DataTable>, your <FormLayout>)
                    β”‚
                    β–Ό
              Document in Storybook or similar
                    β”‚
                    β–Ό
              ONLY build custom primitives when existing ones
              genuinely cannot meet your needs

Documentation and Adoption ​

A design system nobody uses is just a side project. Adoption requires:

Documentation structure:

docs/design-system/
β”œβ”€β”€ getting-started.md          ← Installation, setup
β”œβ”€β”€ principles.md               ← Design philosophy
β”œβ”€β”€ tokens/
β”‚   β”œβ”€β”€ colors.md
β”‚   β”œβ”€β”€ spacing.md
β”‚   └── typography.md
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ button.md               ← Props, variants, examples
β”‚   β”œβ”€β”€ input.md
β”‚   β”œβ”€β”€ data-table.md
β”‚   └── ...
β”œβ”€β”€ patterns/
β”‚   β”œβ”€β”€ forms.md                ← How to compose form layouts
β”‚   β”œβ”€β”€ navigation.md
β”‚   └── empty-states.md
└── changelog.md                ← What changed, migration guides

Adoption checklist:

  • [ ] Components are installable via package manager (npm, internal registry)
  • [ ] Storybook (or equivalent) is deployed and accessible to the whole team
  • [ ] Each component has at least one copy-pasteable usage example
  • [ ] Designers use the same token names in Figma as devs use in code
  • [ ] New hires get a design system walkthrough in their first week
  • [ ] There is a Slack channel or forum for questions and proposals
  • [ ] Breaking changes follow semver and include migration guides

Design System Maturity Model ​

Not every team needs a full design system on day one. Grow it as your product grows.

MATURITY LEVELS
===============

Level 0: AD HOC
β”œβ”€β”€ No shared components
β”œβ”€β”€ Each page styled independently
β”œβ”€β”€ Inconsistency is the norm
└── Every feature takes maximum effort

         β”‚
         β–Ό

Level 1: SHARED STYLES
β”œβ”€β”€ Common CSS/token file
β”œβ”€β”€ Basic color and font consistency
β”œβ”€β”€ Components duplicated (not shared)
└── "It looks roughly the same"

         β”‚
         β–Ό

Level 2: COMPONENT LIBRARY
β”œβ”€β”€ Shared, importable components
β”œβ”€β”€ Documented props and variants
β”œβ”€β”€ Storybook or equivalent
└── Devs use components instead of raw HTML

         β”‚
         β–Ό

Level 3: DESIGN SYSTEM
β”œβ”€β”€ Tokens + Components + Patterns + Docs
β”œβ”€β”€ Figma library mirrors code library
β”œβ”€β”€ Governance: proposal process for changes
β”œβ”€β”€ Used across multiple products or surfaces
└── Dedicated owner (person or team)

         β”‚
         β–Ό

Level 4: PLATFORM
β”œβ”€β”€ Design system as internal product
β”œβ”€β”€ Versioned, published, consumed by teams
β”œβ”€β”€ Contribution model (teams submit components)
β”œβ”€β”€ Analytics: usage tracking, adoption metrics
└── Only needed for large orgs (50+ devs)

Where you should be based on team size:

Team SizeTarget MaturityTimeline
1-3 devsLevel 1 (Shared Styles)Immediately
4-8 devsLevel 2 (Component Library)Within first quarter
9-20 devsLevel 3 (Design System)Within 6 months
20+ devsLevel 3-4Ongoing investment

In Practice ​

Example: Defining a Button Component ​

Here is what a well-documented button component looks like.

BUTTON COMPONENT
================

Variants:        [primary] [secondary] [ghost] [danger]
Sizes:           [sm]      [md]        [lg]
States:          default | hover | focus | active | disabled | loading

Props:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Prop         β”‚ Type     β”‚ Default   β”‚ Description              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ variant      β”‚ string   β”‚ "primary" β”‚ Visual style             β”‚
β”‚ size         β”‚ string   β”‚ "md"      β”‚ Button size              β”‚
β”‚ disabled     β”‚ boolean  β”‚ false     β”‚ Prevents interaction     β”‚
β”‚ loading      β”‚ boolean  β”‚ false     β”‚ Shows spinner, disables  β”‚
β”‚ leftIcon     β”‚ ReactNodeβ”‚ null      β”‚ Icon before label        β”‚
β”‚ rightIcon    β”‚ ReactNodeβ”‚ null      β”‚ Icon after label         β”‚
β”‚ fullWidth    β”‚ boolean  β”‚ false     β”‚ Stretches to container   β”‚
β”‚ onClick      β”‚ function β”‚ -         β”‚ Click handler            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Usage Guidelines:
- Use PRIMARY for the single most important action on the page
- Use SECONDARY for alternative actions alongside a primary
- Use GHOST for low-emphasis actions (cancel, dismiss)
- Use DANGER only for destructive/irreversible actions
- Never place two PRIMARY buttons side by side
- Always include a text label (icon-only buttons need aria-label)

Common Mistakes ​

MistakeWhy It HappensFix
Building too much too soonDesigning for hypothetical future needsBuild components as features need them
No single owner"Everyone owns it" means nobody doesAssign a maintainer, even part-time
Design-code driftFigma and code diverge over monthsSync reviews quarterly, automate where possible
Over-engineering tokensTokens for every conceivable valueStart with 80/20: the 20 tokens that cover 80% of cases
No versioningBreaking changes surprise consumersUse semver from day one
Ignoring accessibility"We'll add it later"Bake it into components from the start --- retrofitting is 10x harder

Key Takeaways ​

  • A design system has three layers: tokens (values), components (elements), and patterns (compositions)
  • Design tokens replace magic numbers with named, meaningful values that propagate changes automatically
  • Start with a headless component library and add your token layer on top --- do not build from scratch unless you must
  • Documentation and adoption strategy matter as much as the components themselves
  • Grow your system's maturity alongside your team size --- Level 2 is the sweet spot for most early SaaS teams
  • Accessibility is not optional and is dramatically cheaper to include from the start

Action Items ​

RoleNext Step
🏒 OwnerAssess your current maturity level using the model above. Budget time to reach the next level.
πŸ’» DevSet up a shared token file (CSS variables or theme object) and migrate the 5 most common colors and spacing values.
πŸ“‹ PMIdentify the 10 most-used components in your product. These are your priority for standardization.
🎨 DesignerCreate a Figma component library that mirrors the token names and component variants your devs use. Align naming conventions.

Previous: Chapter 16 --- SaaS UX Principles | Next: Chapter 18 --- Designing for Roles

The Product Builder's Playbook