# Tailwind CSS Best Practices: From Basics to Advanced
Master Tailwind CSS configuration, responsive design, custom components and performance
Tailwind CSS has revolutionized how we write CSS. This article explores best practices and advanced techniques.
Tailwind Core Concepts
Utility-First CSS
Tailwind Design Philosophy:
┌─────────────────────────────────────────────────────┐
│ │
│ Traditional CSS │
│ └── Write semantic class names → Define styles │
│ │
│ Tailwind CSS │
│ └── Compose utility classes → Build in HTML │
│ │
│ Benefits │
│ ├── No naming struggles │
│ ├── Styles as components │
│ ├── Consistent design system │
│ └── Smaller production bundle │
│ │
└─────────────────────────────────────────────────────┘
Basic Usage
<!-- Traditional approach -->
<div class="card">
<h2 class="card-title">Title</h2>
<p class="card-content">Content</p>
</div>
<!-- Tailwind approach -->
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-bold text-gray-900 mb-2">Title</h2>
<p class="text-gray-600">Content</p>
</div>
Configuration
tailwind.config.js
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx,astro}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
// Custom colors
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
brand: '#ff6b35',
},
// Custom fonts
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
// Custom spacing
spacing: {
'18': '4.5rem',
'88': '22rem',
'128': '32rem',
},
// Custom breakpoints
screens: {
'xs': '475px',
'3xl': '1920px',
},
// Custom animations
animation: {
'fade-in': 'fadeIn 0.5s ease-out',
'slide-up': 'slideUp 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
};
Responsive Design
Breakpoint Usage
<!-- Mobile-first design -->
<div class="
w-full /* Default: full width */
md:w-1/2 /* Medium screens: half */
lg:w-1/3 /* Large screens: third */
xl:w-1/4 /* Extra large: quarter */
">
Responsive Card
</div>
<!-- Responsive grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
<!-- Responsive navigation -->
<nav class="flex flex-col md:flex-row md:items-center md:justify-between">
<div class="text-xl font-bold">Logo</div>
<div class="hidden md:flex space-x-4">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</div>
</nav>
Container Queries
<!-- Using @container -->
<div class="@container">
<div class="@md:flex @md:items-center">
<img class="w-full @md:w-48" src="..." alt="" />
<div class="@md:ml-4">
<h3 class="text-lg @lg:text-xl">Title</h3>
</div>
</div>
</div>
State Variants
Interactive States
<!-- Hover, Focus, Active -->
<button class="
bg-blue-500
hover:bg-blue-600
focus:ring-2
focus:ring-blue-300
active:bg-blue-700
transition-colors
">
Button
</button>
<!-- Group Hover -->
<div class="group">
<div class="group-hover:bg-gray-100 p-4">
<h3 class="group-hover:text-blue-600">Title</h3>
<p class="group-hover:text-gray-700">Description</p>
</div>
</div>
<!-- Peer State -->
<input type="checkbox" class="peer" />
<label class="peer-checked:text-blue-600">
Turns blue when checked
</label>
Dark Mode
<!-- System preference -->
<div class="bg-white dark:bg-gray-900">
<h1 class="text-gray-900 dark:text-white">Title</h1>
<p class="text-gray-600 dark:text-gray-400">Content</p>
</div>
<!-- Manual toggle -->
<script>
// tailwind.config.js: darkMode: 'class'
function toggleDark() {
document.documentElement.classList.toggle('dark');
}
</script>
Component Abstraction
@apply Directive
/* styles/components.css */
@layer components {
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-colors;
}
.btn-primary {
@apply btn bg-blue-500 text-white hover:bg-blue-600;
}
.btn-secondary {
@apply btn bg-gray-200 text-gray-800 hover:bg-gray-300;
}
.input {
@apply w-full px-3 py-2 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-transparent;
}
.card {
@apply bg-white rounded-xl shadow-lg p-6;
}
}
React Components
// components/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-lg font-medium transition-colors',
{
variants: {
variant: {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
outline: 'border border-gray-300 hover:bg-gray-50',
ghost: 'hover:bg-gray-100',
},
size: {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
export function Button({ className, variant, size, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
);
}
// Usage
<Button variant="primary" size="lg">Primary</Button>
<Button variant="outline">Outline</Button>
cn Utility Function
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// Usage
cn('px-4 py-2', isActive && 'bg-blue-500', className);
// Automatically merges conflicting classes
Layout Patterns
Flexbox
<!-- Horizontal center -->
<div class="flex justify-center">
<div>Centered content</div>
</div>
<!-- Vertical center -->
<div class="flex items-center h-screen">
<div>Centered content</div>
</div>
<!-- Full center -->
<div class="flex items-center justify-center h-screen">
<div>Centered content</div>
</div>
<!-- Space Between -->
<div class="flex justify-between items-center">
<div>Left</div>
<div>Right</div>
</div>
Grid
<!-- Basic grid -->
<div class="grid grid-cols-3 gap-4">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<!-- Auto-fit grid -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
</div>
<!-- Complex layout -->
<div class="grid grid-cols-12 gap-4">
<div class="col-span-12 lg:col-span-8">Main</div>
<div class="col-span-12 lg:col-span-4">Sidebar</div>
</div>
Animations
<!-- Transitions -->
<button class="
bg-blue-500
hover:bg-blue-600
transition-all
duration-300
ease-in-out
hover:scale-105
">
Hover to scale
</button>
<!-- Custom animations -->
<div class="animate-fade-in">
Fade in element
</div>
<div class="animate-pulse">
Pulse effect
</div>
<div class="animate-spin">
Spinning loader
</div>
<!-- Conditional animation -->
<div class="
opacity-0
translate-y-4
transition-all
duration-500
data-[visible=true]:opacity-100
data-[visible=true]:translate-y-0
">
Scroll reveal
</div>
Performance
Production Build
// tailwind.config.js
module.exports = {
content: [
// Only scan needed files
'./src/**/*.{js,ts,jsx,tsx}',
],
// Automatic Tree Shaking in production
};
Minimizing Classes
<!-- Avoid repetition -->
<div class="p-4 m-4">
<!-- Use space instead of multiple margins -->
<div class="space-y-4">
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
<!-- Use divide instead of borders -->
<ul class="divide-y divide-gray-200">
<li class="py-4">Item 1</li>
<li class="py-4">Item 2</li>
</ul>
Best Practices Summary
Tailwind CSS Best Practices:
┌─────────────────────────────────────────────────────┐
│ Design System │
│ ├── Define design tokens in config │
│ ├── Use consistent spacing and colors │
│ ├── Create reusable components │
│ └── Use CVA for variants │
│ │
│ Code Organization │
│ ├── Group classes logically │
│ ├── Use @apply for common patterns │
│ ├── Use cn() for class merging │
│ └── Componentize long class lists │
│ │
│ Performance │
│ ├── Configure content paths correctly │
│ ├── Automatic purge in production │
│ ├── Avoid dynamic class concatenation │
│ └── Use JIT mode │
│ │
│ Responsive Design │
│ ├── Mobile-first approach │
│ ├── Use breakpoints wisely │
│ ├── Container queries │
│ └── Dark mode support │
└─────────────────────────────────────────────────────┘
| Scenario | Recommended Approach |
|---|---|
| Button components | CVA + cn() |
| Form styles | @tailwindcss/forms |
| Article typography | @tailwindcss/typography |
| Dark mode | dark: prefix |
Tailwind makes styles part of components. No more CSS naming struggles.