Design System Engineering
Build and maintain a design system that scales across teams, products, and platforms. Covers component API design, token architecture, documentation-driven development, versioning, and adoption strategies.
A design system is not a component library. A component library is code. A design system is a shared language — design tokens, patterns, principles, and guidelines — that enables consistent, high-quality user interfaces across every product and team in the organization.
Architecture
Design Tokens (foundation)
└── Colors, typography, spacing, shadows, borders
Primitive Components (atoms)
└── Button, Input, Icon, Badge, Avatar
Composite Components (molecules)
└── SearchBar, Card, Dropdown, Modal, Toast
Layout Components (organisms)
└── Header, Sidebar, PageLayout, DataTable
Patterns (templates)
└── Form patterns, CRUD patterns, Dashboard layouts
Design Tokens
Design tokens are the single source of truth for visual properties:
{
"color": {
"primary": {
"50": { "value": "#eff6ff" },
"500": { "value": "#3b82f6" },
"900": { "value": "#1e3a8a" }
},
"semantic": {
"success": { "value": "{color.green.500}" },
"warning": { "value": "{color.amber.500}" },
"error": { "value": "{color.red.500}" }
}
},
"spacing": {
"xs": { "value": "4px" },
"sm": { "value": "8px" },
"md": { "value": "16px" },
"lg": { "value": "24px" },
"xl": { "value": "32px" }
},
"typography": {
"heading": {
"1": { "fontSize": "2.25rem", "fontWeight": "700", "lineHeight": "1.2" },
"2": { "fontSize": "1.875rem", "fontWeight": "600", "lineHeight": "1.3" },
"3": { "fontSize": "1.5rem", "fontWeight": "600", "lineHeight": "1.4" }
}
}
}
Tokens transform into platform-specific formats:
# CSS custom properties
--color-primary-500: #3b82f6;
# iOS (Swift)
static let primaryColor = Color(hex: "#3b82f6")
# Android (Kotlin)
val primaryColor = Color(0xFF3B82F6)
Component API Design
Composition Over Configuration
// BAD: Monolithic API
<Button
label="Submit"
icon="check"
iconPosition="left"
loading={true}
loadingText="Saving..."
variant="primary"
size="large"
disabled={false}
/>
// GOOD: Composable API
<Button variant="primary" size="lg" loading>
<Icon name="check" />
Submit
</Button>
Accessibility Built-In
function Dialog({ open, onClose, title, children }) {
return (
<dialog
open={open}
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
onKeyDown={e => e.key === 'Escape' && onClose()}
>
<h2 id="dialog-title">{title}</h2>
{children}
<button onClick={onClose} aria-label="Close dialog">×</button>
</dialog>
);
}
Documentation-Driven Development
Every component ships with:
# Button
## Usage
Use buttons for primary actions. Use links for navigation.
## Variants
- `primary`: Main call to action (1 per page)
- `secondary`: Supporting actions
- `ghost`: Low-emphasis actions
- `destructive`: Delete, remove, cancel operations
## Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | string | 'primary' | Visual style |
| size | 'sm' | 'md' | 'lg' | 'md' | Button size |
| loading | boolean | false | Shows loading spinner |
| disabled | boolean | false | Prevents interaction |
## Do's and Don'ts
✅ Use clear, action-oriented labels ("Save changes", not "OK")
✅ Use destructive variant for irreversible actions
❌ Don't use more than one primary button per page section
❌ Don't use a button when a link is semantically correct
Versioning and Release
Semantic versioning:
1.0.0 → 1.0.1 (patch: bug fix, no API change)
1.0.0 → 1.1.0 (minor: new component, backward compatible)
1.0.0 → 2.0.0 (major: breaking change)
Release cadence:
Patches: As needed (security, bugs)
Minor: Biweekly (new components, enhancements)
Major: Quarterly at most (breaking changes with migration guide)
Adoption Metrics
Track whether the design system is actually being used:
Coverage: % of UI built with design system components
Adoption: # of teams actively using the system
Consistency: # of custom overrides / escape hatches
Satisfaction: Developer survey score (quarterly)
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| Build everything before adoption | Wasted effort, wrong abstractions | Build what teams need now |
| No documentation | Components misused, re-implemented | Documentation is a release requirement |
| Too rigid (no escape hatches) | Teams work around the system | Provide composition patterns and slots |
| One team owns everything | Bottleneck, slowdowns | Federated contributions with review |
| No breaking change process | Updates break consuming apps | Semver, migration guides, deprecation periods |
A design system succeeds when teams choose to use it because it makes their work faster and better — not because they are forced to.