Design Systems: Scalable UI Architecture

Medium

Design systems are the foundation of scalable product development - invest early for long-term ROI.

Quick Decision Guide

Design System = Tokens + Components + Patterns + Docs

Tokens: { colors, spacing, typography } Primitives: Button, Input, Text (atomic) Components: Card, Modal, Dropdown (composed) Patterns: Forms, dashboards, layouts Tools: Storybook, Figma, versioning

Result: 3-5x faster feature development, consistent UX, easier maintenance.

Atomic Design Methodology

Hierarchy

Atoms       → Button, Input, Text, Icon
Molecules   → SearchBar (Input + Button), FormField (Label + Input)
Organisms   → Header (Logo + Nav + SearchBar), Card
Templates   → PageLayout, DashboardTemplate
Pages       → Homepage, Dashboard, Settings

Example: Building Up

// 1. Atoms
const Button = ({ children, ...props }) => (
  <button className="btn" {...props}>{children}</button>
);

const Input = ({ ...props }) => (
  <input className="input" {...props} />
);

// 2. Molecules
const SearchBar = ({ onSearch }) => (
  <div className="search-bar">
    <Input placeholder="Search..." />
    <Button onClick={onSearch}>Search</Button>
  </div>
);

// 3. Organisms
const Header = () => (
  <header>
    <Logo />
    <Navigation />
    <SearchBar />
    <UserMenu />
  </header>
);

// 4. Templates
const DashboardTemplate = ({ header, sidebar, content }) => (
  <div className="dashboard">
    {header}
    <div className="layout">
      {sidebar}
      {content}
    </div>
  </div>
);

Design Tokens

Token Structure

// tokens/colors.js
export const colors = {
  // Brand colors
  brand: {
    primary: '#0070f3',
    secondary: '#7928ca',
  },
  
  // Semantic colors
  semantic: {
    success: '#0070f3',
    error: '#e00',
    warning: '#f5a623',
    info: '#0070f3',
  },
  
  // Neutral palette
  neutral: {
    white: '#fff',
    black: '#000',
    gray: {
      50: '#fafafa',
      100: '#f5f5f5',
      200: '#e5e5e5',
      // ... through 900
    },
  },
  
  // Text colors
  text: {
    primary: '#000',
    secondary: '#666',
    tertiary: '#999',
    inverse: '#fff',
  },
};

// tokens/spacing.js
export const spacing = {
  0: '0',
  1: '0.25rem',  // 4px
  2: '0.5rem',   // 8px
  3: '0.75rem',  // 12px
  4: '1rem',     // 16px
  5: '1.25rem',  // 20px
  6: '1.5rem',   // 24px
  8: '2rem',     // 32px
  10: '2.5rem',  // 40px
  12: '3rem',    // 48px
  16: '4rem',    // 64px
};

// tokens/typography.js
export const typography = {
  fontFamily: {
    sans: "'Inter', -apple-system, sans-serif",
    mono: "'Fira Code', monospace",
  },
  fontSize: {
    xs: '0.75rem',    // 12px
    sm: '0.875rem',   // 14px
    base: '1rem',     // 16px
    lg: '1.125rem',   // 18px
    xl: '1.25rem',    // 20px
    '2xl': '1.5rem',  // 24px
    '3xl': '1.875rem',// 30px
    '4xl': '2.25rem', // 36px
  },
  fontWeight: {
    normal: 400,
    medium: 500,
    semibold: 600,
    bold: 700,
  },
  lineHeight: {
    tight: 1.25,
    normal: 1.5,
    relaxed: 1.75,
  },
};

Using Tokens

import { colors, spacing, typography } from '@/tokens';

const Button = styled.button`
  background: ${colors.brand.primary};
  padding: ${spacing[3]} ${spacing[4]};
  font-family: ${typography.fontFamily.sans};
  font-weight: ${typography.fontWeight.semibold};
`;

Component API Design

Consistent Props

// All components follow similar patterns
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  loading?: boolean;
  children: React.ReactNode;
  onClick?: () => void;
}

interface InputProps {
  variant?: 'default' | 'filled' | 'outline';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  error?: boolean;
  helperText?: string;
  // ...
}

Composition Over Configuration

// ❌ Bad: Too many props
<Modal
  title="Confirm"
  content="Are you sure?"
  primaryButton="Yes"
  secondaryButton="No"
  onPrimary={handleYes}
  onSecondary={handleNo}
/>

// ✅ Good: Composable
<Modal>
  <Modal.Header>
    <Modal.Title>Confirm</Modal.Title>
  </Modal.Header>
  <Modal.Body>
    Are you sure?
  </Modal.Body>
  <Modal.Footer>
    <Button onClick={handleNo}>No</Button>
    <Button variant="primary" onClick={handleYes}>Yes</Button>
  </Modal.Footer>
</Modal>

Theming

Theme Provider

// ThemeProvider.jsx
import { createContext, useContext } from 'react';

const ThemeContext = createContext();

export const ThemeProvider = ({ theme, children }) => (
  <ThemeContext.Provider value={theme}>
    {children}
  </ThemeContext.Provider>
);

export const useTheme = () => useContext(ThemeContext);

// Usage
const lightTheme = {
  colors: { primary: '#0070f3', background: '#fff' }
};

const darkTheme = {
  colors: { primary: '#0070f3', background: '#000' }
};

<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
  <App />
</ThemeProvider>

Theme-Aware Components

const Button = styled.button`
  background: ${({ theme }) => theme.colors.primary};
  color: ${({ theme }) => theme.colors.text};
`;

// Or with hooks
function Button() {
  const theme = useTheme();
  
  return (
    <button style={{
      background: theme.colors.primary,
      color: theme.colors.text
    }}>
      Click
    </button>
  );
}

Documentation & Tooling

Storybook Setup

// .storybook/main.js
module.exports = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-a11y',
  ],
};

// Button.stories.jsx
export default {
  title: 'Primitives/Button',
  component: Button,
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'outline'],
    },
  },
};

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

Versioning

{
  "name": "@company/design-system",
  "version": "2.3.0",
  "peerDependencies": {
    "react": "^18.0.0"
  }
}

Usage in Products

npm install @company/design-system@^2.0.0
import { Button, Input, Card } from '@company/design-system';

function App() {
  return (
    <Card>
      <Input placeholder="Email" />
      <Button variant="primary">Submit</Button>
    </Card>
  );
}