Making a Design System from Scratch for B2C Products
A practical guide to building a scalable design system for B2C products—from tokens and components to real-world frontend implementation using React and Tailwind CSS.
A design system is not a Figma file.
It's not a color palette.
It's not a fancy component library.
A design system is a decision system—and in B2C products, bad decisions scale fast.
Why B2C Products Need a Different Design System Mindset
B2C products move fast.
- Features ship weekly
- UI changes are frequent
- Experiments never stop
- Marketing will ask for "just one more variation"
If your design system is rigid, it breaks.
If it's too loose, it becomes chaos.
The goal isn't perfection.
The goal is controlled consistency.
The Biggest Mistake: Starting From Components
Most teams start here:
"Let's build buttons, cards, modals…"
That's backwards.
Components are the last layer.
A scalable design system starts with rules, not UI.
Step 1: Define Design Tokens (The Real Foundation)
Design tokens are the smallest, most reusable decisions.
Think:
- colors
- spacing
- typography
- radius
- shadows
These are non-negotiable rules, not suggestions.
Example: Color Tokens
export const colors = {
primary: {
DEFAULT: '#84cc16',
foreground: '#0f172a',
},
neutral: {
900: '#18181b',
700: '#3f3f46',
400: '#a1a1aa',
100: '#f4f4f5',
},
};If your brand color changes tomorrow, you update one file, not 200 components.
That's the power of tokens.
Step 2: Components Should Encode Decisions, Not Options
A common anti-pattern in B2C systems:
<Button bgColor='green' textColor='black' padding='12px' radius='8px' />Congrats—you just rebuilt inline styles.
✅ A Better Approach: Opinionated Components
type ButtonVariant = 'primary' | 'secondary' | 'ghost';
type ButtonSize = 'sm' | 'md' | 'lg';
type ButtonProps = {
variant?: ButtonVariant;
size?: ButtonSize;
};
export function Button({
variant = 'primary',
size = 'md',
children,
}: React.PropsWithChildren<ButtonProps>) {
return (
<button
className={cn(
'rounded-lg font-medium transition',
variant === 'primary' && 'bg-lime-500 text-zinc-900',
variant === 'secondary' && 'bg-zinc-800 text-white',
variant === 'ghost' && 'bg-transparent text-zinc-400',
size === 'sm' && 'px-3 py-1 text-sm',
size === 'md' && 'px-4 py-2',
size === 'lg' && 'px-6 py-3 text-lg'
)}
>
{children}
</button>
);
}Now:
- ✅ Design decisions are centralized
- ✅ UI stays consistent
- ✅ Developers can't accidentally break the system
This is what scaling looks like.
Step 3: B2C Needs Speed, Not Over-Engineering
Not every design system needs:
- versioned packages
- monorepos
- storybook on day one
For early to mid-stage B2C products:
- keep it close to the app
- iterate fast
- document just enough
A Simple Structure Works
components/
├─ ui/
│ ├─ button.tsx
│ ├─ badge.tsx
│ ├─ card.tsx
│ └─ input.tsx
├─ layout/
└─ feedback/
tokens/
styles/Design systems should enable shipping, not slow it down.
Step 4: Design for Change (Because Change Is Guaranteed)
In B2C, you will:
- rebrand
- tweak colors
- adjust spacing
- add variants
Your system must expect that.
Rule of thumb:
If a change requires touching many files, your abstraction is wrong.
Good design systems localize change.
Step 5: Frontend Engineers Own the System
Design systems are not static assets.
They are:
- maintained
- refactored
- evolved
Frontend engineers are best positioned to own them because:
- they see real constraints
- they feel the pain of bad abstractions
- they understand performance & DX
A design system that ignores implementation reality will collapse under production pressure.
Final Thoughts
A design system is not about being fancy.
It's about:
- clarity over cleverness
- consistency over freedom
- scalability over aesthetics
Especially in B2C products, where every UI decision directly touches users.
Build systems that help teams move fast—without breaking things along the way.
That's real frontend engineering.