Table of Contents
Introduction
Tailwind CSS is a utility-first CSS framework that provides low-level utility classes to build custom designs directly in your markup. Unlike traditional CSS frameworks that offer pre-designed components, Tailwind gives you the building blocks to create unique interfaces without writing custom CSS.
What Makes Tailwind Different
Utility-First Philosophy: Instead of semantic class names like .button or .card, Tailwind provides atomic utility classes like bg-blue-500, p-4, and rounded-lg that do one thing well.
Design Constraints: Built-in design system with carefully crafted spacing scales, color palettes, and typography that promote consistency while remaining highly customizable.
Build-Time Processing: Tailwind uses PostCSS to scan your files and generate only the CSS you actually use, resulting in minimal production file sizes.
Core Concepts
Utility Classes
Utility classes are single-purpose CSS classes that apply one specific style property. Each class maps directly to CSS properties:
m-4 β margin: 1remtext-center β text-align: centerbg-blue-500 β background-color: #3b82f6
Design Tokens
Tailwind provides a comprehensive design system through tokens:
Spacing Scale: Uses a consistent scale where each unit represents 0.25rem (4px)
p-1 = 0.25rem (4px)p-4 = 1rem (16px)p-8 = 2rem (32px)
Color Palette: Organized in shades from 50 (lightest) to 950 (darkest)
bg-gray-50 through bg-gray-950text-blue-500, border-red-600
Typography Scale: Predefined font sizes with corresponding line heights
text-xs = 0.75rem / 1rem line-heighttext-base = 1rem / 1.5rem line-heighttext-2xl = 1.5rem / 2rem line-height
JIT Mode (Just-In-Time)
Since Tailwind v3, JIT is the default engine that generates styles on-demand as you author content. Benefits include:
- Instant Build Times: Only generates CSS for classes you use
- Arbitrary Values: Write custom values like
w-[137px] or top-[-113px] - All Variants Enabled: Every variant is available without configuration
- Smaller Development Builds: No massive CSS file during development
Terminology Tables
Table 1: Lifecycle Phases & Terminology
| Phase/Stage | Tailwind Term | Alternative Terms | Description |
|---|
| Setup | Installation | Initialization, Bootstrap | Installing Tailwind via npm/yarn and creating config files |
| Configuration | tailwind.config.js | Config File, Setup File | Defining content paths, theme customization, and plugins |
| Development | JIT Compilation | On-Demand Generation, Live Building | Real-time CSS generation as you write classes |
| Scanning | Content Detection | File Scanning, Class Extraction | PostCSS scans specified files for class names |
| Generation | CSS Building | Style Compilation, Output Generation | Creating the final CSS file with used utilities |
| Optimization | Purging/Tree-shaking | Dead Code Elimination, Unused CSS Removal | Removing unused styles for production |
| Output | Production Build | Final Build, Optimized CSS | Minified, production-ready stylesheet |
| Integration | Framework Setup | Platform Integration, Tooling Configuration | Configuring with React, Vue, Next.js, etc. |
| Customization | Theme Extension | Design Token Customization, Style Override | Modifying default theme values |
| Composition | Component Extraction | Style Abstraction, Class Grouping | Creating reusable patterns with @apply or components |
Table 2: Hierarchical Jargon Differentiation
| Level | Category | Terms | Scope | Usage Context |
|---|
| 1. Framework Level | Architecture | Utility-First, Atomic CSS, Functional CSS | Entire framework philosophy | Describing Tailwindβs approach vs traditional CSS |
| 2. Configuration Level | Setup | Config, Theme, Presets, Plugins | Project configuration | tailwind.config.js customization |
| 3. Build Level | Processing | JIT Engine, PostCSS, Compilation, Purging | Build system | Development and production workflows |
| 4. Design System Level | Tokens | Spacing Scale, Color Palette, Typography Scale | Design constraints | Theme customization and consistency |
| 5. Class Level | Utilities | Base Utilities, Variants, Modifiers, Arbitrary Values | Individual classes | Writing actual markup |
| 6. Responsive Level | Breakpoints | sm, md, lg, xl, 2xl, Mobile-First | Screen sizes | Responsive design implementation |
| 7. State Level | Variants | Hover, Focus, Active, Group, Peer, State Modifiers | Interactive states | User interaction styling |
| 8. Composition Level | Patterns | @apply, @layer, Component Classes, Extracting Components | Code organization | Managing complexity and reusability |
| 9. Optimization Level | Production | Content Configuration, Tree-shaking, Minification, PurgeCSS | Performance | Production build optimization |
| 10. Extension Level | Customization | Plugins, Custom Utilities, Theme Extension, Directives | Extending functionality | Adding custom features |
Installation & Configuration
Basic Installation
Via npm/yarn:
1
2
3
4
5
| # Install Tailwind and dependencies
npm install -D tailwindcss postcss autoprefixer
# Initialize configuration
npx tailwindcss init
|
Via CDN (Development Only):
1
| <script src="https://cdn.tailwindcss.com"></script>
|
Note: CDN is not recommended for production as it includes the entire framework and doesnβt support customization or optimization.
Configuration File Structure
tailwind.config.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| /** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,js,jsx,ts,tsx}",
"./pages/**/*.{html,js,jsx,ts,tsx}",
"./components/**/*.{html,js,jsx,ts,tsx}",
],
theme: {
extend: {
colors: {
primary: '#3b82f6',
secondary: '#8b5cf6',
},
spacing: {
'128': '32rem',
'144': '36rem',
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
},
},
plugins: [],
}
|
Key Configuration Sections:
- content: Array of file paths to scan for class names
- theme: Design system customization (colors, spacing, fonts, etc.)
- theme.extend: Adds to default theme without replacing it
- plugins: Third-party or custom plugins to extend functionality
- darkMode: Strategy for dark mode (βmediaβ or βclassβ)
- prefix: Add prefix to all utility classes
- important: Make utilities important by default
PostCSS Configuration
postcss.config.js:
1
2
3
4
5
6
| module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
|
CSS Entry Point
styles.css:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| @tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
h1 {
@apply text-4xl font-bold;
}
}
@layer components {
.btn-primary {
@apply py-2 px-4 bg-blue-500 text-white rounded hover:bg-blue-600;
}
}
@layer utilities {
.scroll-snap-none {
scroll-snap-type: none;
}
}
|
Directives Explained:
@tailwind base: Injects Normalize.css and base styles@tailwind components: Injects component classes@tailwind utilities: Injects utility classes@layer: Organizes custom styles into Tailwindβs layer system
Utility-First Fundamentals
Basic Utilities
Layout:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <!-- Flexbox -->
<div class="flex items-center justify-between">
<div>Item 1</div>
<div>Item 2</div>
</div>
<!-- Grid -->
<div class="grid grid-cols-3 gap-4">
<div>Column 1</div>
<div>Column 2</div>
<div>Column 3</div>
</div>
<!-- Positioning -->
<div class="relative">
<div class="absolute top-0 right-0">Positioned</div>
</div>
|
Spacing:
1
2
3
4
5
6
7
8
| <!-- Margin -->
<div class="m-4">All sides 1rem</div>
<div class="mx-auto">Horizontal centering</div>
<div class="mt-8 mb-4">Top 2rem, bottom 1rem</div>
<!-- Padding -->
<div class="p-6">All sides 1.5rem</div>
<div class="px-4 py-2">X-axis 1rem, Y-axis 0.5rem</div>
|
Typography:
1
2
3
4
5
6
7
8
9
| <!-- Font styling -->
<h1 class="text-4xl font-bold text-gray-900">Heading</h1>
<p class="text-base font-normal text-gray-600 leading-relaxed">
Paragraph text with relaxed line height.
</p>
<!-- Text alignment -->
<div class="text-center">Centered text</div>
<div class="text-left md:text-right">Responsive alignment</div>
|
Colors:
1
2
3
4
5
6
7
8
9
| <!-- Background -->
<div class="bg-blue-500">Blue background</div>
<div class="bg-gradient-to-r from-purple-500 to-pink-500">Gradient</div>
<!-- Text -->
<p class="text-red-600">Red text</p>
<!-- Border -->
<div class="border-2 border-gray-300">Border</div>
|
Arbitrary Values
JIT mode allows custom values inline:
1
2
3
4
5
6
7
8
9
10
11
| <!-- Custom dimensions -->
<div class="w-[137px] h-[342px]"></div>
<!-- Custom colors -->
<div class="bg-[#1da1f2] text-[#f8f9fa]"></div>
<!-- Custom spacing -->
<div class="top-[-113px] left-[50%]"></div>
<!-- CSS variables -->
<div class="bg-[var(--primary-color)]"></div>
|
Syntax: Use square brackets with any valid CSS value.
Responsive Design
Mobile-First Breakpoints
Tailwind uses a mobile-first approach where unprefixed utilities apply to all screen sizes, and prefixed utilities apply at specified breakpoints and above:
| Breakpoint | Min Width | CSS |
|---|
sm | 640px | @media (min-width: 640px) |
md | 768px | @media (min-width: 768px) |
lg | 1024px | @media (min-width: 1024px) |
xl | 1280px | @media (min-width: 1280px) |
2xl | 1536px | @media (min-width: 1536px) |
Responsive Patterns
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <!-- Stack on mobile, row on desktop -->
<div class="flex flex-col md:flex-row gap-4">
<div class="w-full md:w-1/2">Column 1</div>
<div class="w-full md:w-1/2">Column 2</div>
</div>
<!-- Responsive grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- Items -->
</div>
<!-- Responsive text -->
<h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl">
Responsive Heading
</h1>
<!-- Hide/show based on screen size -->
<div class="block md:hidden">Mobile only</div>
<div class="hidden md:block">Desktop only</div>
|
Custom Breakpoints
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // tailwind.config.js
module.exports = {
theme: {
screens: {
'xs': '475px',
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
'3xl': '1920px',
},
},
}
|
State Variants
Pseudo-Classes
Hover and Focus:
1
2
3
4
5
6
7
8
9
10
11
12
| <!-- Hover states -->
<button class="bg-blue-500 hover:bg-blue-600">
Hover me
</button>
<!-- Focus states -->
<input class="border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200">
<!-- Combined states -->
<a class="text-blue-600 hover:text-blue-800 focus:outline-none focus:ring-2">
Link
</a>
|
Active and Visited:
1
2
3
4
5
6
7
8
9
| <!-- Active state -->
<button class="bg-blue-500 active:bg-blue-700">
Press me
</button>
<!-- Visited links -->
<a class="text-blue-600 visited:text-purple-600">
Visited link
</a>
|
Group Modifiers
Style child elements based on parent state:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <!-- Group hover -->
<div class="group">
<img class="group-hover:scale-110 transition">
<h3 class="group-hover:text-blue-600">Title</h3>
</div>
<!-- Multiple groups -->
<div class="group/card">
<div class="group/item">
<span class="group-hover/card:text-blue-600 group-hover/item:underline">
Text
</span>
</div>
</div>
|
Peer Modifiers
Style elements based on sibling state:
1
2
3
4
5
6
7
8
9
10
11
| <!-- Peer validation -->
<input type="checkbox" class="peer" />
<label class="peer-checked:text-blue-600 peer-checked:font-bold">
Checked label
</label>
<!-- Form validation -->
<input type="email" required class="peer" />
<p class="hidden peer-invalid:block text-red-600">
Invalid email
</p>
|
Advanced Variants
First, Last, Odd, Even:
1
2
3
4
5
| <ul>
<li class="first:font-bold">First item</li>
<li class="odd:bg-gray-100 even:bg-white">Item</li>
<li class="last:border-b-0">Last item</li>
</ul>
|
Has and Not:
1
2
3
4
5
6
7
8
9
| <!-- Has variant (experimental) -->
<div class="has-[img]:p-4">
<img src="..." />
</div>
<!-- Not variant -->
<button class="not-disabled:hover:bg-blue-600" disabled>
Button
</button>
|
Data Attributes:
1
2
3
| <div data-state="active" class="data-[state=active]:bg-blue-500">
Tab
</div>
|
Customization & Theming
Extending the Theme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| // tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
900: '#1e3a8a',
},
},
spacing: {
'128': '32rem',
'144': '36rem',
},
borderRadius: {
'4xl': '2rem',
},
fontSize: {
'2xs': ['0.625rem', { lineHeight: '0.75rem' }],
},
fontFamily: {
display: ['Oswald', 'sans-serif'],
body: ['Open Sans', 'sans-serif'],
},
boxShadow: {
'brutal': '4px 4px 0px 0px rgba(0,0,0,1)',
},
animation: {
'spin-slow': 'spin 3s linear infinite',
},
keyframes: {
wiggle: {
'0%, 100%': { transform: 'rotate(-3deg)' },
'50%': { transform: 'rotate(3deg)' },
},
},
},
},
}
|
Replacing Default Theme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| module.exports = {
theme: {
// Replaces default colors entirely
colors: {
primary: '#3b82f6',
secondary: '#8b5cf6',
white: '#ffffff',
black: '#000000',
},
// Keep defaults, add custom
extend: {
colors: {
brand: '#3b82f6',
},
},
},
}
|
CSS Variables Integration
1
2
3
4
5
6
7
8
9
10
11
12
13
| /* globals.css */
@layer base {
:root {
--color-primary: 59 130 246;
--color-secondary: 139 92 246;
--radius: 0.5rem;
}
.dark {
--color-primary: 96 165 250;
--color-secondary: 167 139 250;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: 'rgb(var(--color-primary) / <alpha-value>)',
secondary: 'rgb(var(--color-secondary) / <alpha-value>)',
},
borderRadius: {
DEFAULT: 'var(--radius)',
},
},
},
}
|
Component Patterns
Extract components when:
- A pattern repeats 3+ times across your project
- The pattern is complex (10+ utility classes)
- You need dynamic behavior or logic
- Team consistency requires standardization
React Component Pattern
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| // Button.jsx
const Button = ({
variant = 'primary',
size = 'md',
children,
...props
}) => {
const baseClasses = 'font-semibold rounded transition-colors duration-200';
const variants = {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50',
};
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
};
return (
<button
className={`${baseClasses} ${variants[variant]} ${sizes[size]}`}
{...props}
>
{children}
</button>
);
};
|
Vue Component Pattern
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| <!-- Button.vue -->
<template>
<button
:class="[baseClasses, variantClasses, sizeClasses]"
>
<slot />
</button>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
variant: { type: String, default: 'primary' },
size: { type: String, default: 'md' },
});
const baseClasses = 'font-semibold rounded transition-colors duration-200';
const variantClasses = computed(() => {
const variants = {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
};
return variants[props.variant];
});
const sizeClasses = computed(() => {
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
};
return sizes[props.size];
});
</script>
|
Using @apply Directive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| @layer components {
.btn {
@apply font-semibold py-2 px-4 rounded transition-colors duration-200;
}
.btn-primary {
@apply bg-blue-500 text-white hover:bg-blue-600;
}
.btn-secondary {
@apply bg-gray-200 text-gray-900 hover:bg-gray-300;
}
.card {
@apply bg-white rounded-lg shadow-md overflow-hidden;
}
.card-header {
@apply px-6 py-4 border-b border-gray-200;
}
.card-body {
@apply px-6 py-4;
}
}
|
@apply Best Practices:
- Use only for repeated component patterns
- Avoid @apply for simple utilities (defeats purpose)
- Prefer component frameworks over excessive @apply
- Keep extracted components in @layer components
- Donβt @apply responsive or state variants
Content Configuration
Optimize build performance by specifying precise content paths:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // tailwind.config.js
module.exports = {
content: [
// Include all relevant files
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./app/**/*.{js,ts,jsx,tsx}',
'./src/**/*.{js,ts,jsx,tsx}',
// Include node_modules if using UI libraries
'./node_modules/@my-company/ui/**/*.{js,ts,jsx,tsx}',
// Exclude files that don't contain classes
'!./node_modules/**',
'!./.git/**',
],
}
|
Production Build Optimization
Automatic in Tailwind v3+:
- JIT engine generates only used classes
- Built-in minification and compression
- Dead code elimination
Additional Optimizations:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // tailwind.config.js
module.exports = {
content: ['./src/**/*.{html,js,jsx}'],
// Safelist classes that are generated dynamically
safelist: [
'bg-red-500',
'bg-green-500',
{
pattern: /bg-(red|green|blue)-(100|500|900)/,
},
],
// Blocklist to never generate certain utilities
blocklist: [
'container',
'text-15xl',
],
}
|
Dynamic Class Names
β Avoid (wonβt be detected):
1
2
3
4
5
| // String concatenation
<div className={`text-${color}-500`}></div>
// Template literals with variables
<div className={`text-${isActive ? 'blue' : 'gray'}-500`}></div>
|
β
Correct Approach:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // Complete class names
const colors = {
blue: 'text-blue-500',
gray: 'text-gray-500',
red: 'text-red-500',
};
<div className={colors[color]}></div>
// Or use safelist in config
// tailwind.config.js
safelist: [
'text-blue-500',
'text-gray-500',
'text-red-500',
]
|
Bundle Size Analysis
1
2
3
4
5
| # Analyze CSS output
npx tailwindcss -i ./src/input.css -o ./dist/output.css --minify
# Check file size
du -h ./dist/output.css
|
Typical Production Sizes:
- Small project: 5-10 KB
- Medium project: 10-30 KB
- Large project: 30-50 KB
Dark Mode Implementation
Configuration
1
2
3
4
5
6
| // tailwind.config.js
module.exports = {
darkMode: 'class', // or 'media'
// 'class': Toggle via class on html/body
// 'media': Automatic based on OS preference
}
|
Class Strategy
1
2
3
4
5
6
7
8
9
10
| <!-- Add dark class to html element -->
<html class="dark">
<body>
<!-- Dark mode utilities -->
<div class="bg-white dark:bg-gray-900">
<h1 class="text-gray-900 dark:text-white">Heading</h1>
<p class="text-gray-600 dark:text-gray-300">Paragraph</p>
</div>
</body>
</html>
|
Toggle Implementation
React Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import { useState, useEffect } from 'react';
const DarkModeToggle = () => {
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
// Check localStorage and system preference
const isDark = localStorage.theme === 'dark' ||
(!('theme' in localStorage) &&
window.matchMedia('(prefers-color-scheme: dark)').matches);
setDarkMode(isDark);
document.documentElement.classList.toggle('dark', isDark);
}, []);
const toggleDarkMode = () => {
const newMode = !darkMode;
setDarkMode(newMode);
localStorage.theme = newMode ? 'dark' : 'light';
document.documentElement.classList.toggle('dark', newMode);
};
return (
<button
onClick={toggleDarkMode}
className="p-2 rounded-lg bg-gray-200 dark:bg-gray-700"
>
{darkMode ? 'π' : 'βοΈ'}
</button>
);
};
|
Dark Mode Design Patterns
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| <!-- Color inversion -->
<div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-white">
Content
</div>
<!-- Subtle background changes -->
<div class="bg-gray-50 dark:bg-gray-800">
Card background
</div>
<!-- Border adjustments -->
<div class="border border-gray-200 dark:border-gray-700">
Bordered content
</div>
<!-- Shadow adjustments -->
<div class="shadow-lg dark:shadow-2xl dark:shadow-gray-900/50">
Card with shadow
</div>
<!-- Image overlays for dark mode -->
<img
class="dark:brightness-75 dark:contrast-125"
src="image.jpg"
/>
|
Custom Dark Mode Colors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Define colors with dark variants
primary: {
light: '#3b82f6',
dark: '#60a5fa',
},
},
},
},
}
|
Advanced Techniques
Custom Plugins
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| // tailwind.config.js
const plugin = require('tailwindcss/plugin');
module.exports = {
plugins: [
plugin(function({ addUtilities, addComponents, theme }) {
// Add custom utilities
addUtilities({
'.scrollbar-hide': {
'-ms-overflow-style': 'none',
'scrollbar-width': 'none',
'&::-webkit-scrollbar': {
display: 'none',
},
},
'.text-shadow': {
'text-shadow': '2px 2px 4px rgba(0,0,0,0.1)',
},
});
// Add custom components
addComponents({
'.btn-glow': {
padding: theme('spacing.4'),
borderRadius: theme('borderRadius.lg'),
background: 'linear-gradient(45deg, #f093fb 0%, #f5576c 100%)',
boxShadow: '0 4px 15px rgba(240, 147, 251, 0.75)',
transition: 'all 0.3s ease',
'&:hover': {
transform: 'translateY(-2px)',
boxShadow: '0 6px 20px rgba(240, 147, 251, 0.4)',
},
},
});
}),
],
}
|
Creating Custom Variants
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| const plugin = require('tailwindcss/plugin');
module.exports = {
plugins: [
plugin(function({ addVariant }) {
// Custom children variant
addVariant('children', '& > *');
// Custom nth-child variants
addVariant('third', '&:nth-child(3)');
// Custom optional variant (when input doesn't have required attribute)
addVariant('optional', '&:optional');
// Custom supports variant
addVariant('supports-grid', '@supports (display: grid)');
}),
],
}
|
Usage:
1
2
3
4
5
6
7
8
| <div class="children:p-4 children:border">
<div>Child 1</div>
<div>Child 2</div>
</div>
<ul>
<li class="third:font-bold">Item 3 will be bold</li>
</ul>
|
Layer Customization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| /* Custom base styles */
@layer base {
h1 {
@apply text-4xl font-bold tracking-tight;
}
a {
@apply text-blue-600 hover:text-blue-800 transition-colors;
}
/* Custom focus ring */
*:focus-visible {
@apply outline-none ring-2 ring-blue-500 ring-offset-2;
}
}
/* Custom component patterns */
@layer components {
.input-field {
@apply block w-full rounded-md border-gray-300 shadow-sm;
@apply focus:border-blue-500 focus:ring-blue-500;
}
.badge {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
}
.badge-primary {
@apply bg-blue-100 text-blue-800;
}
}
/* Custom utilities */
@layer utilities {
.animation-pause {
animation-play-state: paused;
}
.text-balance {
text-wrap: balance;
}
.scrollbar-thin {
scrollbar-width: thin;
}
}
|
Container Queries
1
2
3
4
5
6
| // tailwind.config.js
module.exports = {
plugins: [
require('@tailwindcss/container-queries'),
],
}
|
1
2
3
4
5
| <div class="@container">
<div class="@md:grid @md:grid-cols-2 @lg:grid-cols-3">
<!-- Responds to container size, not viewport -->
</div>
</div>
|
Typography Plugin
1
2
3
4
5
6
| // tailwind.config.js
module.exports = {
plugins: [
require('@tailwindcss/typography'),
],
}
|
1
2
3
4
| <article class="prose lg:prose-xl dark:prose-invert">
<h1>Article Title</h1>
<p>Beautifully styled typography with sensible defaults...</p>
</article>
|
Best Practices
1. Maintain Consistency
Use Design Tokens:
1
2
3
4
5
| <!-- β Avoid arbitrary values everywhere -->
<div class="p-[13px] text-[#1a73e8]"></div>
<!-- β
Use theme values -->
<div class="p-4 text-blue-600"></div>
|
Create a Style Guide:
1
2
3
4
5
6
7
8
9
10
11
12
13
| // tailwind.config.js - Define your design system
module.exports = {
theme: {
extend: {
spacing: {
'sm': '0.5rem',
'md': '1rem',
'lg': '1.5rem',
'xl': '2rem',
},
},
},
}
|
2. Component Organization
File Structure:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| src/
βββ components/
β βββ ui/ # Reusable UI components
β β βββ Button.jsx
β β βββ Card.jsx
β β βββ Input.jsx
β βββ layouts/ # Layout components
β β βββ Header.jsx
β β βββ Footer.jsx
β βββ features/ # Feature-specific components
β βββ UserProfile.jsx
βββ styles/
β βββ globals.css # @tailwind directives
β βββ components.css # Component-specific styles
βββ lib/
βββ utils.js # Utility functions (cn helper)
|
Class Name Helper (cn):
1
2
3
4
5
6
7
| // lib/utils.js
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs) {
return twMerge(clsx(inputs));
}
|
Usage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import { cn } from '@/lib/utils';
const Button = ({ className, variant, ...props }) => {
return (
<button
className={cn(
"px-4 py-2 rounded font-medium",
variant === "primary" && "bg-blue-500 text-white",
variant === "secondary" && "bg-gray-200 text-gray-900",
className
)}
{...props}
/>
);
};
|
3. Accessibility First
Semantic HTML:
1
2
3
4
5
6
7
| <!-- β
Use semantic elements -->
<nav class="flex gap-4">
<a href="#" class="hover:text-blue-600">Link</a>
</nav>
<!-- β Avoid generic divs for interactive elements -->
<div class="cursor-pointer" onclick="...">Click me</div>
|
Focus States:
1
2
3
4
5
6
7
8
9
| <!-- Always include focus styles -->
<button class="
bg-blue-500 text-white px-4 py-2 rounded
hover:bg-blue-600
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
disabled:opacity-50 disabled:cursor-not-allowed
">
Button
</button>
|
Screen Reader Support:
1
2
3
4
5
6
| <button class="sr-only focus:not-sr-only">
Skip to main content
</button>
<span class="sr-only">Loading...</span>
<div class="animate-spin" aria-hidden="true">β³</div>
|
Avoid Over-Extraction:
1
2
3
4
5
6
7
8
| /* β Don't extract single-use utilities */
@layer components {
.my-special-div {
@apply p-4;
}
}
/* β
Use utilities directly for simple cases */
|
Optimize Content Paths:
1
2
3
4
5
| // β Too broad
content: ['./src/**/*']
// β
Specific file types
content: ['./src/**/*.{js,jsx,ts,tsx,html}']
|
Lazy Load Heavy Components:
1
2
3
4
5
6
7
8
9
10
11
| import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div class="animate-pulse">Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
|
5. Naming Conventions
Component Props:
1
2
3
4
5
| // Use clear prop names
<Button size="lg" variant="primary" fullWidth />
// Not vague abbreviations
<Button sz="l" var="p" fw />
|
Custom Classes:
1
2
3
4
5
6
7
8
9
10
11
| @layer components {
/* Use BEM-like naming for complex components */
.card { }
.card__header { }
.card__body { }
.card__footer { }
/* Use modifiers with prefixes */
.card--featured { }
.card--compact { }
}
|
6. Responsive Design Patterns
Mobile-First Approach:
1
2
3
4
5
6
7
8
9
| <!-- β
Start with mobile, enhance for larger screens -->
<div class="p-4 md:p-6 lg:p-8">
<h1 class="text-2xl md:text-3xl lg:text-4xl">Title</h1>
</div>
<!-- β Desktop-first (requires more overrides) -->
<div class="p-8 md:p-6 sm:p-4">
<h1 class="text-4xl md:text-3xl sm:text-2xl">Title</h1>
</div>
|
Container Usage:
1
2
3
4
5
6
| <!-- Use container for consistent max-widths -->
<div class="container mx-auto px-4">
<main class="max-w-7xl mx-auto">
<!-- Content -->
</main>
</div>
|
7. Team Collaboration
Document Patterns:
1
2
3
4
5
6
7
8
9
| /**
* Primary button component
*
* @example
* <Button variant="primary" size="md">Click me</Button>
*/
const Button = ({ variant = 'primary', size = 'md', children }) => {
// Implementation
};
|
ESLint + Prettier Setup:
1
2
3
4
5
| // .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindConfig": "./tailwind.config.js"
}
|
Class Ordering: The Prettier plugin automatically orders classes:
- Layout (display, position)
- Box model (width, height, margin, padding)
- Typography (font, text)
- Visual (background, border)
- Misc (cursor, transition)
Common Pitfalls
1. Specificity Issues
Problem:
1
2
3
4
5
6
7
8
| <!-- External CSS overriding Tailwind -->
<button class="bg-blue-500">Button</button>
<style>
button {
background: red !important;
}
</style>
|
Solution:
1
2
3
4
5
6
| // tailwind.config.js
module.exports = {
important: true, // Makes all utilities !important
// Or use a selector
important: '#app',
}
|
2. Missing Purge Configuration
Problem:
1
2
| // β Classes in dynamic templates not detected
const btnClass = `bg-${color}-500`; // Won't work
|
Solution:
1
2
3
4
5
6
7
8
9
| // β
Use complete class names
const colorMap = {
blue: 'bg-blue-500',
red: 'bg-red-500',
};
// Or add to safelist
// tailwind.config.js
safelist: ['bg-blue-500', 'bg-red-500']
|
3. Inline Styles Mixed with Tailwind
Problem:
1
2
3
4
5
6
7
| // β Mixing paradigms
<div
className="p-4 bg-white"
style=
>
Content
</div>
|
Solution:
1
2
3
4
5
6
7
8
9
| // β
Use Tailwind consistently
<div className="mt-5 p-4 bg-white text-gray-800">
Content
</div>
// Or use arbitrary values for one-offs
<div className="mt-[20px] text-[#333]">
Content
</div>
|
4. Overusing @apply
Problem:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| /* β Extracting everything defeats the purpose */
@layer components {
.header {
@apply flex items-center justify-between;
}
.header-logo {
@apply w-32 h-8;
}
.header-nav {
@apply flex gap-4;
}
}
|
Solution:
1
2
3
4
5
6
7
| // β
Use utilities directly or create proper components
<header className="flex items-center justify-between">
<img className="w-32 h-8" />
<nav className="flex gap-4">
{/* Links */}
</nav>
</header>
|
5. Not Testing Responsive Behavior
Problem:
- Designing only for desktop
- Not checking mobile breakpoints
- Overflow issues on small screens
Solution:
1
2
3
4
5
6
7
8
9
10
11
| <!-- Test at all breakpoints -->
<div class="
grid grid-cols-1
sm:grid-cols-2
md:grid-cols-3
lg:grid-cols-4
gap-4
">
<!-- Use browser dev tools responsive mode -->
<!-- Test actual devices when possible -->
</div>
|
6. Ignoring Dark Mode
Problem:
1
2
3
4
| <!-- β Only light mode colors -->
<div class="bg-white text-gray-900">
Content
</div>
|
Solution:
1
2
3
4
| <!-- β
Include dark mode variants -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
Content
</div>
|
7. Not Leveraging Variants
Problem:
1
2
3
4
5
6
7
8
9
10
| // β Manual state management
const [isHovered, setIsHovered] = useState(false);
<button
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={isHovered ? 'bg-blue-600' : 'bg-blue-500'}
>
Button
</button>
|
Solution:
1
2
3
4
| // β
Use hover variant
<button className="bg-blue-500 hover:bg-blue-600">
Button
</button>
|
References
- Official Tailwind CSS Documentation
- Tailwind CSS Configuration Guide
- Responsive Design - Tailwind CSS
- Hover, Focus, and Other States
- Dark Mode - Tailwind CSS
- Adding Custom Styles
- Plugins - Tailwind CSS
- Optimizing for Production
- Tailwind CSS GitHub Repository
- Editor Setup - Tailwind CSS
- Installation - Tailwind CSS
- Utility-First Fundamentals
Last Updated: November 29, 2025