Since 2023 Tailwind CSS has become the de-facto utility-first framework on the frontend. You can build production-ready UI just by adding class names, no CSS file needed. This guide walks through every fundamental, from installation to custom theming.
Installation
# Next.js / React / Vite project
npm install -D tailwindcss@latest postcss autoprefixer
npx tailwindcss init -p
# Standalone (no build tool)
npm install -D @tailwindcss/cli
npx @tailwindcss/cli -i input.css -o output.css --watch
# CDN (for prototypes)
<script src="https://cdn.tailwindcss.com"></script>
/* src/styles.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Utility-First Philosophy
Tailwind's core idea: keep class names short and compose UIs inline. Instead of padding: 1rem write p-4; instead of background-color: #3b82f6 write bg-blue-500.
<!-- Classic CSS
<style>
.card { padding: 1rem; background: white;
border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.card-title { font-size: 1.25rem; font-weight: 600; margin-bottom: 0.5rem; }
</style>
<div class="card">
<h2 class="card-title">Title</h2>
<p>Content</p>
</div>
-->
<!-- Tailwind -->
<div class="p-4 bg-white rounded-lg shadow">
<h2 class="text-xl font-semibold mb-2">Title</h2>
<p>Content</p>
</div>
Responsive Design
Tailwind's breakpoint prefixes are mobile-first: no prefix is the default (mobile), then sm: (640px+), md: (768px+), lg: (1024px+), xl: (1280px+), 2xl: (1536px+).
<!-- Single column on mobile, 2 on tablet, 3 on desktop -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="p-4 bg-gray-100">Card 1</div>
<div class="p-4 bg-gray-100">Card 2</div>
<div class="p-4 bg-gray-100">Card 3</div>
</div>
<!-- Small heading on mobile, larger on desktop -->
<h1 class="text-2xl md:text-4xl lg:text-5xl font-bold">Big Heading</h1>
Dark Mode
// tailwind.config.js
module.exports = {
darkMode: 'class', // or 'media'
content: ['./src/**/*.{html,js,jsx,tsx}']
};
<html class="dark">
<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded">
Automatic dark mode
</div>
</body>
// Dark mode toggle
const toggle = document.getElementById('themeToggle');
toggle.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
localStorage.setItem('theme',
document.documentElement.classList.contains('dark') ? 'dark' : 'light');
});
// On page load
if (localStorage.theme === 'dark') {
document.documentElement.classList.add('dark');
}
Custom Theme
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{html,js,jsx,tsx}'],
theme: {
extend: {
colors: {
brand: {
50: '#fef3ee',
500: '#ea580c', // keydal orange
700: '#9a3412'
}
},
fontFamily: {
serif: ['Instrument Serif', 'Georgia', 'serif'],
sans: ['DM Sans', 'system-ui', 'sans-serif']
},
animation: {
'fade-in': 'fadeIn 0.3s ease-out'
},
keyframes: {
fadeIn: {
'0%': { opacity: 0, transform: 'translateY(10px)' },
'100%': { opacity: 1, transform: 'translateY(0)' }
}
}
}
}
};
<button class="bg-brand-500 hover:bg-brand-700 text-white px-4 py-2 rounded font-sans">
Button using brand colors
</button>
<div class="animate-fade-in">Fade-in animation</div>
Component Extraction
Repeating the same class list? Extract it into a component — in HTML/JSX, not in CSS.
// React component
function Button({ children, variant = 'primary' }) {
const variants = {
primary: 'bg-blue-500 hover:bg-blue-600 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
danger: 'bg-red-500 hover:bg-red-600 text-white'
};
return (
<button className={`px-4 py-2 rounded font-medium ${variants[variant]}`}>
{children}
</button>
);
}
// Usage
<Button variant="primary">Save</Button>
<Button variant="danger">Delete</Button>
CSS Abstraction with @apply
Sometimes it is useful to keep class names in CSS. The @apply directive inlines utilities into CSS rules — handy when you are not using a component framework.
@layer components {
.btn {
@apply px-4 py-2 rounded font-medium transition-colors;
}
.btn-primary {
@apply btn bg-blue-500 hover:bg-blue-600 text-white;
}
}
Arbitrary Values
<!-- Custom color, size, etc. -->
<div class="bg-[#ea580c] w-[calc(100%-2rem)] text-[15px]">
Values not in the config
</div>
<div class="grid grid-cols-[200px_1fr_100px] gap-[14px]">
Custom grid template
</div>
Production Build
Tailwind ships only the classes you actually use in the final CSS (built-in PurgeCSS-like behaviour). A full 3MB Tailwind file typically shrinks to 5-30KB in production.
# Build
NODE_ENV=production npx tailwindcss -i input.css -o output.css --minify
Conclusion
After a two-hour learning curve, Tailwind CSS becomes the fastest tool for writing UI. You skip CSS files, you skip naming, and you get responsive design and dark mode for free — an ideal foundation for production-quality interfaces.
Tailwind CSS setup and design-system work inside your React/Vue project Write to us