Why Build Your Own Kit?
- Consistency: Define styles once, use everywhere
- Speed: No more hunting for the perfect shade or spacing
- Maintainability: Update one place, change site-wide
- Team alignment: Everyone uses the same components
Setting Up Your Foundation
First, extend Tailwind's default theme with your design tokens:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: {
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
},
},
},
}
Creating Base Component Styles
Use the @layer directive to create reusable classes:
@layer components {
.btn {
@apply inline-flex items-center justify-center px-4 py-2
font-medium rounded-lg transition-all duration-200
focus:outline-none focus:ring-2;
}
.btn-primary {
@apply btn bg-primary-600 text-white hover:bg-primary-700;
}
.input {
@apply w-full px-4 py-2 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-primary-500;
}
}
Building React Components
For better flexibility, wrap your styles in components:
const Button = ({
variant = 'primary',
size = 'md',
loading = false,
children,
...props
}) => {
const variants = {
primary: 'btn-primary',
secondary: 'btn-secondary',
outline: 'btn-outline',
};
const sizes = {
sm: 'text-sm px-3 py-1.5',
md: 'px-4 py-2',
lg: 'text-lg px-6 py-3',
};
return (
<button
className={`btn ${variants[variant]} ${sizes[size]}`}
disabled={loading}
{...props}
>
{loading ? 'Loading...' : children}
</button>
);
};
Usage:
<Button variant="primary" size="lg">Save Changes</Button>
<Button variant="outline" loading>Processing...</Button>
Adding Dark Mode
Tailwind's dark mode support makes this easy:
@layer components {
.btn-primary {
@apply btn bg-primary-600 hover:bg-primary-700
dark:bg-primary-500 dark:hover:bg-primary-600;
}
.card {
@apply bg-white dark:bg-gray-800
border-gray-200 dark:border-gray-700;
}
}
Enable it in your config:
module.exports = {
darkMode: 'class',
// ... rest of config
}
Key Components to Start With
Focus on these first:
- Button: Variants (primary, secondary, outline), sizes, loading state
- Input: Label, error state, helper text
- Card: Header, body, footer sections
- Alert: Success, error, warning types
- Modal: Backdrop, close button, scroll lock
Best Practices I've Learned
Start minimal. Don't build 50 components upfront. Create what you need, when you need it.
Composition over configuration. Build smaller pieces that work together:
<Card>
<CardHeader>User Profile</CardHeader>
<CardBody>
<p>Content here</p>
</CardBody>
</Card>
Keep accessibility in mind. Use semantic HTML, add proper ARIA labels, and test with keyboard navigation.
Document as you go. A simple README with props and examples saves time later.
Common Pitfalls
- Over-abstracting early: Don't create a component until you've used the pattern 3+ times
- Hardcoding colors: Always use theme colors from your config
- Ignoring edge cases: Test long text, disabled states, and mobile views
-
Forgetting responsive design: Use Tailwind's responsive prefixes (
md:,lg:)
Example: Complete Form Component
Here's a form input that handles everything:
const Input = ({
label,
error,
hint,
required,
...props
}) => {
return (
<div className="space-y-1">
{label && (
<label className="block text-sm font-medium text-gray-700">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>
)}
<input
className={error ? 'input-error' : 'input'}
{...props}
/>
{hint && !error && (
<p className="text-sm text-gray-500">{hint}</p>
)}
{error && (
<p className="text-sm text-red-600">{error}</p>
)}
</div>
);
};
The Result
After setting this up, my development speed increased significantly. No more decision fatigue about spacing or colors. Everything is consistent, and changes are easy.
Want to Learn More?
This is just scratching the surface. I wrote a comprehensive guide that covers:
- Advanced component patterns
- Form systems with validation
- Managing component complexity
- Storybook integration
- Real-world examples and code
👉 Read the full article: Building a Custom UI Kit with Tailwind CSS
The complete guide includes production-ready code examples, architectural decisions, and lessons learned from building multiple UI kits.
Have you built your own UI kit? What challenges did you face? Let me know in the comments!
Top comments (1)
Thanks for sharing, I’ll give it a try!