Creating a Unified Design System from Scratch
How we built Blossom's design system to ensure consistency across web, mobile, and emerging platforms while maintaining flexibility.
Creating a Unified Design System from Scratch
Building a design system is like creating a new language - it needs to be expressive enough to handle complex ideas, yet simple enough for everyone to understand. Here’s how we built Blossom’s design system from the ground up.
The Problem We Were Solving
Before our design system, we faced common challenges:
- Inconsistent UI: Buttons looked different across pages
- Slow development: Developers recreated components from scratch
- Brand dilution: Each team interpreted brand guidelines differently
- Accessibility gaps: Inconsistent implementation of WCAG standards
Foundation: Design Tokens
We started with design tokens - the atomic values that power our entire system:
{
"color": {
"primary": {
"50": "#FFF0F5",
"100": "#FFE4EC",
"500": "#FF69B4",
"900": "#8B0037"
},
"semantic": {
"error": "$color.red.500",
"success": "$color.green.500",
"warning": "$color.amber.500"
}
},
"spacing": {
"xs": "4px",
"sm": "8px",
"md": "16px",
"lg": "24px",
"xl": "32px"
},
"typography": {
"fontFamily": {
"sans": "Pretendard, -apple-system, sans-serif",
"mono": "JetBrains Mono, monospace"
}
}
}
Component Architecture
Atomic Design Principles
We structured our components following atomic design:
// Atom: Button
export const Button = ({ variant, size, children, ...props }) => {
return (
<button
className={cn(
"blossom-button",
"blossom-button--" + variant,
"blossom-button--" + size
)}
{...props}
>
{children}
</button>
);
};
// Molecule: Card
export const Card = ({ title, description, actions }) => {
return (
<div className="blossom-card">
<h3 className="blossom-card__title">{title}</h3>
<p className="blossom-card__description">{description}</p>
<div className="blossom-card__actions">
{actions}
</div>
</div>
);
};
// Organism: ProductCard
export const ProductCard = ({ product }) => {
return (
<Card
title={product.name}
description={product.description}
actions={
<Button variant="primary" size="medium">
View Details
</Button>
}
/>
);
};
Documentation Strategy
Interactive Documentation
We built our documentation to be interactive and developer-friendly:
// Storybook story example
export default {
title: 'Components/Button',
component: Button,
argTypes: {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary', 'ghost', 'danger']
},
size: {
control: { type: 'radio' },
options: ['small', 'medium', 'large']
}
}
};
export const Playground = {
args: {
children: 'Click me',
variant: 'primary',
size: 'medium'
}
};
Accessibility First
Every component is built with accessibility in mind:
export const Modal = ({ isOpen, onClose, title, children }) => {
const modalRef = useRef(null);
// Trap focus within modal
useFocusTrap(modalRef, isOpen);
// Close on escape key
useEffect(() => {
const handleEscape = (e) => {
if (e.key === 'Escape') onClose();
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
// Announce to screen readers
announceToScreenReader(title + ' dialog opened');
}
return () => document.removeEventListener('keydown', handleEscape);
}, [isOpen, onClose, title]);
return (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
className={cn('blossom-modal', { 'is-open': isOpen })}
>
<h2 id="modal-title">{title}</h2>
{children}
</div>
);
};
Design-Dev Handoff
Figma to Code Bridge
We automated the design-to-development workflow:
- Design tokens sync: Automatically sync from Figma to code
- Component specs: Generate specs from Figma components
- Visual regression testing: Catch unintended changes
// Token sync script
const syncTokens = async () => {
const figmaTokens = await fetchFigmaTokens(FIGMA_FILE_ID);
const transformed = transformTokens(figmaTokens);
await fs.writeFile(
'./tokens/design-tokens.json',
JSON.stringify(transformed, null, 2)
);
// Generate CSS variables
generateCSSVariables(transformed);
// Generate TypeScript types
generateTypeDefinitions(transformed);
};
Versioning and Migration
Semantic Versioning
We follow semantic versioning for our design system:
- Major (1.0.0): Breaking changes
- Minor (1.1.0): New components or features
- Patch (1.1.1): Bug fixes
Migration Guides
Each major version includes detailed migration guides:
## Migrating from v1 to v2
### Breaking Changes
1. **Button component**
- Old: <Button type="primary">
- New: <Button variant="primary">
2. **Color tokens**
- Renamed: color-brand-* → color-primary-*
### Automated Migration
Run our codemod to automatically update most changes:
```bash
npx @blossom/design-system-codemods v1-to-v2
```
Performance Optimization
Tree-Shaking Support
Our design system is built for optimal bundle sizes:
// Bad: Imports entire library
import { Button } from '@blossom/design-system';
// Good: Imports only what you need
import Button from '@blossom/design-system/button';
CSS-in-JS Performance
We optimized our styled-components for performance:
/* Use CSS variables for dynamic values */
.blossom-button {
background-color: var(--button-bg, #ff69b4);
}
.blossom-button:hover {
background-color: var(--button-hover-bg, #ff1493);
}
Then update variables dynamically in JavaScript:
// This allows us to update colors without re-rendering
button.style.setProperty('--button-bg', newColor);
Multi-Platform Support
Our design system works across platforms:
Web Components
class BlossomButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML =
'<style>/* Button styles */</style>' +
'<button class="blossom-button">' +
'<slot></slot>' +
'</button>';
}
}
customElements.define('blossom-button', BlossomButton);
React Native
export const Button = ({ variant, children, onPress }) => {
return (
<TouchableOpacity
style={[styles.button, styles[variant]]}
onPress={onPress}
>
<Text style={styles.buttonText}>{children}</Text>
</TouchableOpacity>
);
};
Measuring Success
Key metrics we track:
- Adoption rate: 95% of new features use design system
- Development speed: 40% faster component development
- Consistency score: 98% UI consistency across products
- Accessibility compliance: 100% WCAG AA compliant
Lessons Learned
- Start small: Begin with the most used components
- Involve everyone: Include developers, designers, and PMs early
- Document everything: Good documentation drives adoption
- Automate repetitive tasks: Use tools to enforce consistency
- Plan for change: Design systems evolve - plan for it
What’s Next?
- AI-powered design suggestions: Using ML to suggest component usage
- Motion design system: Standardizing animations and transitions
- Voice UI components: Extending to voice interfaces
- AR/VR components: Preparing for spatial computing
Conclusion
A design system is never “done” - it’s a living, breathing entity that grows with your product. The key is to start with solid foundations, maintain flexibility, and always keep your users (both designers and developers) at the center of your decisions.
Want to explore our design system? Check out our Storybook or contribute on GitHub!