Design
Overview
Stop fighting Tailwind's utility classes. Learn how we compose tokens, variants, and component abstractions into a coherent design system.
Priya Shah
"Tailwind is just utility classes" is wrong. Properly configured, Tailwind becomes a complete design token system with component APIs that rival any CSS-in-JS solution.
Tailwind design tokens dashboard
Before any components, define your design tokens in tailwind.config.js:
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
100: '#dbeafe',
// ... up to 900
DEFAULT: '#3b82f6',
},
semantic: {
success: 'var(--success)',
error: 'var(--error)',
warning: 'var(--warning)',
}
},
spacing: {
18: '4.5rem',
88: '22rem',
},
fontSize: {
'title-xl': ['2.5rem', { lineHeight: '1.2', fontWeight: '700' }],
}
}
}
}
Your spacing should follow a geometric progression (4, 8, 12, 16, 24, 32, 48, 64, 96, 128). Every number divisible by 4 maintains harmony.
Never write the same utility combination twice. Create reusable components:
// Before — Don't do this
<button className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
Click me
</button>
// After — Create a Button component
<Button variant="primary" size="md">
Click me
</Button>
Component composition architecture
Use clsx or class-variance-authority (CVA) for variants:
import { cva } from 'class-variance-authority'
const button = cva(['rounded-lg font-medium transition-colors'], {
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50',
},
size: {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
},
}
})
Tailwind's dark mode with class strategy:
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
This works everywhere
</div>
Pro tip: Use CSS variables for complex dark mode transitions:
:root { --bg-primary: #ffffff; --text-primary: #111827; }
.dark { --bg-primary: #111827; --text-primary: #f9fafb; }
.bg-primary { background-color: var(--bg-primary); }
Define breakpoints as a team:
sm: 640px (mobile landscape)md: 768px (tablet)lg: 1024px (desktop)xl: 1280px (wide)2xl: 1536px (ultrawide)Use the mobile-first approach:
<div className="text-sm md:text-base lg:text-lg">
Scales beautifully across devices
</div>
Responsive breakpoints visualization
Custom plugins for repeated patterns:
// tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addUtilities, theme }) {
addUtilities({
'.text-shadow': {
textShadow: '2px 2px 4px rgba(0,0,0,0.1)',
},
})
})
]
}
❌ Inline utilities everywhere (creates inconsistency) ❌ Deep nesting with arbitrary classes (hard to maintain) ❌ Ignoring the config file (misses design system benefits) ❌ Manual dark mode toggles (use class strategy)
After implementing this system:
The key insight: Tailwind isn't a CSS framework — it's a design token distribution system. Treat it like one, and your UI will scale beautifully.
Written by
Priya Shah
Keep Reading
Handoff between design and engineering breaks at scale. Here's the component naming, token structure, and review process we use to keep it seamless.
Most scroll animations feel cheap. Here's the exact easing, timing, and trigger logic we use to make motion feel premium and intentional.
Let's work together