Building Design Systems That Scale: Lessons from Figma, Vercel, and Linear
"How the best product teams build and maintain design systems that grow with their products."
Lisa Chen
Design Systems Lead
Published
January 5, 2024
The Anatomy of World-Class Design Systems
Design systems are the backbone of modern product development. They enable consistency, accelerate development, and create cohesive user experiences across platforms. But building a design system that scales requires more than a component library—it demands a systematic approach to design language, governance, and organizational alignment.
This guide examines how industry leaders like Figma, Vercel, and Linear have built design systems that support hundreds of engineers and designers while maintaining quality and coherence. We'll explore the architectural decisions, processes, and cultural elements that make these systems successful, and provide actionable strategies for building your own.
Foundations: Design Tokens and Primitives
Every scalable design system starts with a solid foundation of design tokens—platform-agnostic variables that store visual design attributes. These tokens are the single source of truth for your design language.
The Token Hierarchy: Organize tokens in three layers:
- Core Tokens: Raw values like #007bff or 16px. These rarely change and serve as the building blocks.
- Semantic Tokens: Contextual names like color-primary or spacing-md. These map core tokens to meanings.
- Component Tokens: Specific to components, like button-primary-bg or input-border-radius. These reference semantic tokens.
Implementation Strategy: Use tools like Style Dictionary or Tokens Studio to transform tokens for multiple platforms. A single source of truth generates variables for CSS, iOS, Android, and Figma:
// tokens.json
{
"color": {
"core": {
"blue": { "value": "#007bff" },
"red": { "value": "#dc3545" }
},
"semantic": {
"primary": { "value": "{color.core.blue}" },
"danger": { "value": "{color.core.red}" }
}
},
"spacing": {
"sm": { "value": "0.25rem" },
"md": { "value": "0.5rem" },
"lg": { "value": "1rem" }
}
}
Dark Mode and Theming: Design tokens make multi-theme support straightforward. Define token sets for each theme and switch at runtime:
:root {
--color-bg: #ffffff;
--color-text: #000000;
--color-primary: #007bff;
}
[data-theme="dark"] {
--color-bg: #1a1a1a;
--color-text: #ffffff;
--color-primary: #4dabf7;
}
Component Architecture Patterns
The way you architect components determines how flexible, maintainable, and performant your design system will be. Leading teams use specific patterns to balance consistency with customization.
Composition over Configuration: Instead of components with dozens of props, use composition patterns:
// Instead of this:
// Prefer this:
Confirm
Are you sure?
This pattern, used by Radix UI and Headless UI, provides maximum flexibility while maintaining internal state management and accessibility.
Headless UI Pattern: Separate logic from presentation. The headless component handles state, accessibility, and keyboard interactions, while consumers provide the styling:
// Headless component
function useSelect(options) {
const [isOpen, setIsOpen] = useState(false);
const [selected, setSelected] = useState(null);
// Handle keyboard navigation, focus management, ARIA attributes
const getTriggerProps = () => ({...});
const getMenuProps = () => ({...});
const getOptionProps = (option) => ({...});
return { isOpen, selected, getTriggerProps, getMenuProps, getOptionProps };
}
// Styled implementation
function CustomSelect({ options }) {
const { selected, getTriggerProps, getMenuProps, getOptionProps } = useSelect(options);
return (
{options.map(option => (
-
{option.label}
))}
);
}
Variant Management with CVA: Class Variance Authority provides type-safe variant management:
import { cva, type VariantProps } from 'class-variance-authority';
const button = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
ghost: 'hover:bg-gray-100 text-gray-700',
},
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',
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes,
VariantProps {}
function Button({ className, variant, size, ...props }: ButtonProps) {
return (
);
}
Documentation and Developer Experience
A design system is only as good as its documentation. Leading teams invest heavily in documentation that serves multiple audiences: designers, developers, and product managers.
Living Documentation with Storybook: Storybook serves as the component library's development environment and documentation site. Configure it for maximum utility:
// .storybook/preview.tsx
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
docs: {
source: { state: 'open' },
},
viewport: {
viewports: {
mobile: { name: 'Mobile', styles: { width: '375px', height: '667px' } },
tablet: { name: 'Tablet', styles: { width: '768px', height: '1024px' } },
desktop: { name: 'Desktop', styles: { width: '1440px', height: '900px' } },
},
},
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#ffffff' },
{ name: 'dark', value: '#0a0a0a' },
],
},
};
Documentation Structure: Organize documentation in four levels:
- Getting Started: Installation, setup, and first component usage
- Foundations: Colors, typography, spacing, icons—design tokens and usage guidelines
- Components: API reference, usage examples, and best practices for each component
- Patterns: Common UI patterns composed of multiple components (forms, navigation, data tables)
Design-Dev Handoff: Use tools like Figma's Dev Mode, Zeroheight, or Supernova to bridge the gap between design and code. These tools inspect design files and generate code snippets, design tokens, and specifications.
Governance and Contribution Models
As design systems grow, governance becomes critical. Without clear processes, systems become inconsistent and difficult to maintain.
Steering Committee: Establish a cross-functional team (designers, developers, product managers, accessibility experts) that makes decisions about:
- Component additions and deprecations
- Breaking changes and versioning strategy
- Design principle updates
- Technical architecture decisions
Contribution Workflow: Define clear paths for contributing to the system:
- Proposal: Submit a proposal describing the need, design rationale, and API
- Review: Steering committee reviews for alignment with system principles
- Development: Build the component following system standards (accessibility, testing, documentation)
- QA: Design and accessibility review, cross-browser testing
- Release: Merge, version, and announce
Versioning Strategy: Use semantic versioning for the design system package. Communicate breaking changes clearly through:
- Migration guides with codemods for automated updates
- Deprecation warnings in minor versions before removal in major versions
- Long-term support (LTS) versions for enterprise customers
Performance Optimization
Design systems can become bloated if not carefully managed. Performance optimization ensures fast load times across all applications.
Tree Shaking: Structure your package to support tree shaking. Use ES modules with proper sideEffects marking:
// package.json
{
"sideEffects": false,
"module": "dist/index.esm.js",
"main": "dist/index.cjs.js",
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js"
},
"./button": {
"import": "./dist/button.esm.js",
"require": "./dist/button.cjs.js"
}
}
}
CSS-in-JS Performance: If using CSS-in-JS, consider the runtime cost. Libraries like Stitches or Vanilla Extract offer zero-runtime CSS generation. Alternatively, use CSS Modules or Tailwind CSS for build-time CSS generation.
Icon Systems: Icon libraries can bloat bundles. Use SVG sprites or icon fonts for large icon sets. Implement tree-shaking for icon imports:
// Instead of importing entire icon library
import * as Icons from 'design-system/icons'; // Bad
// Import specific icons
import { CheckIcon, CloseIcon } from 'design-system/icons'; // Good
// Or use subpath imports
import CheckIcon from 'design-system/icons/check'; // Best for tree-shaking
Accessibility as a Core Feature
Accessibility cannot be an afterthought in a design system. It must be built into every component from the ground up.
WCAG Compliance: Target WCAG 2.1 Level AA compliance minimum. This includes:
- Keyboard navigation and focus management
- Screen reader support with proper ARIA attributes
- Color contrast ratios (4.5:1 for normal text, 3:1 for large text)
- Focus indicators and skip links
- Reduced motion support for animations
Automated Testing: Integrate accessibility testing into CI/CD:
// jest.config.js
module.exports = {
setupFilesAfterEnv: ['@testing-library/jest-dom'],
};
// Component test
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('Button should have no accessibility violations', async () => {
const { container } = render();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Manual Testing: Automated tests catch only 30% of accessibility issues. Conduct regular manual testing with screen readers (NVDA, VoiceOver, JAWS) and keyboard-only navigation.
Multi-Platform Strategy
Modern design systems support multiple platforms: web, iOS, Android, and sometimes desktop applications.
Cross-Platform Tokens: Use design tokens as the source of truth. Transform them for each platform:
- Web: CSS custom properties, Sass variables, or JavaScript
- iOS: Swift constants and UIColor extensions
- Android: XML resources and Kotlin constants
Shared Components: For React Native projects, share components between iOS and Android. For web, consider responsive design over separate mobile components unless the UX differs significantly.
Platform-Specific Adaptations: Allow platform conventions while maintaining brand consistency. iOS and Android have different navigation patterns, button placements, and gesture behaviors. Respect these platform conventions rather than forcing identical UIs.
Measuring Design System Success
Track metrics to ensure your design system delivers value and identify areas for improvement.
Adoption Metrics:
- Percentage of UI built with design system components vs. custom code
- Number of teams actively using the system
- Component usage analytics (which components are used most/least)
Efficiency Metrics:
- Time to implement new features (before vs. after design system)
- Reduction in design QA cycles
- Decrease in accessibility bugs
Quality Metrics:
- Visual consistency scores across products
- Accessibility audit results
- Performance budgets (bundle size, render times)
Case Studies: Lessons from Industry Leaders
Figma's Design System: Figma's own design system prioritizes flexibility. They use a "token-first" approach where all visual properties are tokens, enabling deep customization while maintaining consistency. Their component API design emphasizes composition—components accept children and render props rather than configuration objects.
Vercel's Geist UI: Vercel's design system focuses on performance and developer experience. They use CSS variables for theming, enabling instant theme switching without JavaScript. Components are built with Server Components in mind, minimizing client-side JavaScript.
Linear's Design System: Linear's system is notable for its attention to detail in micro-interactions. Every component has carefully crafted animations that respect user preferences (prefers-reduced-motion). Their design system documentation includes motion specifications alongside visual design specs.
Common Pitfalls and How to Avoid Them
Over-Engineering: Start simple. Don't build for hypothetical future requirements. Figma started with basic components and evolved the system as needs emerged.
Ignoring Maintenance Burden: Every component added is a component that must be maintained, tested, and documented. Be selective about what belongs in the system versus what should be application-specific.
Lack of Governance: Without clear ownership and processes, design systems become inconsistent dumping grounds. Establish the steering committee early and enforce contribution guidelines.
Designer-Developer Disconnect: Ensure designers and developers collaborate closely. Tools like Figma's Dev Mode and regular design-dev pair sessions prevent divergence between design files and code.
Conclusion
Building a design system that scales requires equal attention to technical architecture, documentation, governance, and culture. Start with solid foundations in design tokens, choose component patterns that balance flexibility with consistency, and invest in documentation and developer experience.
Remember that a design system is a product serving internal customers—your designers and developers. Gather feedback continuously, measure adoption and satisfaction, and evolve the system based on real usage patterns. The best design systems grow organically with the products they support, becoming indispensable infrastructure for delivering high-quality user experiences at scale.
Enjoyed this article?
If you found this helpful, consider sharing it with your network. It helps us grow.