# Tailwind CSS Configuration
Complete configuration reference covering both Tailwind v3 (JS config) and v4 (CSS-first config).
## Tailwind v3 Configuration (tailwind.config.js)
### Minimal Config
```js
// tailwind.config.js (v3)
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{html,js,jsx,ts,tsx,vue,astro}',
'./public/index.html',
],
theme: {
extend: {},
},
plugins: [],
}
```
### theme.extend vs theme Override
```js
module.exports = {
theme: {
// OVERRIDE: replaces ALL default colors (only these 3 exist)
colors: {
primary: '#3b82f6',
secondary: '#6b7280',
white: '#ffffff',
},
// EXTEND: adds to defaults (all defaults + these custom values)
extend: {
colors: {
brand: '#3b82f6', // Adds brand color, keeps slate/gray/red/etc.
primary: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
900: '#1e3a5f',
},
},
spacing: {
'18': '4.5rem',
'88': '22rem',
'128': '32rem',
},
borderRadius: {
'4xl': '2rem',
},
},
},
}
```
**Rule**: Almost always use `theme.extend`. Only use direct `theme` override when you want to eliminate defaults entirely.
### Screens (Breakpoints)
```js
module.exports = {
theme: {
// Override ALL breakpoints
screens: {
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
},
// Or extend with additional breakpoints
extend: {
screens: {
'3xl': '1920px',
'tall': { 'raw': '(min-height: 800px)' }, // Height-based
},
},
},
}
```
## Tailwind v4 Configuration (@theme in CSS)
### Basic @theme Block
```css
@import "tailwindcss";
@theme {
/* Colors: generate bg-*, text-*, border-*, ring-*, etc. */
--color-brand: #3b82f6;
--color-brand-50: #eff6ff;
--color-brand-100: #dbeafe;
--color-brand-500: #3b82f6;
--color-brand-600: #2563eb;
--color-brand-700: #1d4ed8;
--color-brand-900: #1e3a5f;
/* Semantic colors */
--color-surface: #ffffff;
--color-surface-dark: #1f2937;
--color-on-surface: #111827;
--color-on-surface-dark: #f9fafb;
/* Fonts */
--font-sans: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
/* Spacing */
--spacing-18: 4.5rem;
--spacing-128: 32rem;
/* Breakpoints */
--breakpoint-3xl: 1920px;
/* Border radius */
--radius-4xl: 2rem;
/* Animations */
--animate-fade-in: fade-in 0.3s ease-out forwards;
}
```
### Clearing Default Values
```css
@theme {
/* Clear all default colors, only keep what you define */
--color-*: initial;
--color-white: #ffffff;
--color-black: #000000;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-blue-700: #1d4ed8;
}
```
## Custom Colors
### Color Palette Definition (v3)
```js
// tailwind.config.js
const colors = require('tailwindcss/colors')
module.exports = {
theme: {
extend: {
colors: {
// Reference built-in palette
gray: colors.slate,
primary: colors.blue,
success: colors.green,
warning: colors.amber,
danger: colors.red,
// Custom palette with full scale
brand: {
50: '#faf5ff',
100: '#f3e8ff',
200: '#e9d5ff',
300: '#d8b4fe',
400: '#c084fc',
500: '#a855f7',
600: '#9333ea',
700: '#7e22ce',
800: '#6b21a8',
900: '#581c87',
950: '#3b0764',
},
},
},
},
}
```
### Color Palette Definition (v4)
```css
@theme {
--color-brand-50: #faf5ff;
--color-brand-100: #f3e8ff;
--color-brand-200: #e9d5ff;
--color-brand-300: #d8b4fe;
--color-brand-400: #c084fc;
--color-brand-500: #a855f7;
--color-brand-600: #9333ea;
--color-brand-700: #7e22ce;
--color-brand-800: #6b21a8;
--color-brand-900: #581c87;
--color-brand-950: #3b0764;
}
```
### Opacity Variants
```html
75% opacity
50% opacity
25% opacity
90% text opacity
50% border opacity
```
### Semantic Colors (Design Tokens)
```css
/* v4: Define semantic tokens that reference palette values */
@theme {
--color-primary: var(--color-brand-600);
--color-primary-hover: var(--color-brand-700);
--color-secondary: var(--color-gray-600);
--color-secondary-hover: var(--color-gray-700);
--color-accent: var(--color-amber-500);
--color-surface: var(--color-white);
--color-surface-elevated: var(--color-gray-50);
--color-on-surface: var(--color-gray-900);
--color-on-surface-muted: var(--color-gray-500);
--color-destructive: var(--color-red-600);
--color-destructive-hover: var(--color-red-700);
}
```
```html
Card on surface
```
## Custom Spacing
### Extending the Spacing Scale
```js
// v3: tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: {
'13': '3.25rem', // 52px
'15': '3.75rem', // 60px
'18': '4.5rem', // 72px
'88': '22rem', // 352px
'128': '32rem', // 512px
'header': '64px', // Named spacing
'sidebar': '280px',
},
},
},
}
```
```css
/* v4: @theme */
@theme {
--spacing-13: 3.25rem;
--spacing-15: 3.75rem;
--spacing-18: 4.5rem;
--spacing-88: 22rem;
--spacing-128: 32rem;
--spacing-header: 64px;
--spacing-sidebar: 280px;
}
```
### Arbitrary Spacing Values
```html
Arbitrary padding
Fluid margin
Calculated width
```
## Typography
### Font Families
```js
// v3: tailwind.config.js
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
display: ['Poppins', 'system-ui', 'sans-serif'],
body: ['Source Sans Pro', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
},
},
},
}
```
```css
/* v4: @theme */
@theme {
--font-sans: "Inter", system-ui, sans-serif;
--font-display: "Poppins", system-ui, sans-serif;
--font-body: "Source Sans Pro", system-ui, sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", monospace;
}
```
```html
Display Heading
Body text with Source Sans Pro
Code block
```
### Font Sizes
```js
// v3: Custom font sizes with line-height
module.exports = {
theme: {
extend: {
fontSize: {
'xs': ['0.75rem', { lineHeight: '1rem' }],
'tiny': ['0.625rem', { lineHeight: '0.875rem' }],
'hero': ['4rem', { lineHeight: '1.1', letterSpacing: '-0.02em', fontWeight: '800' }],
},
},
},
}
```
```css
/* v4: Font size with associated properties */
@theme {
--text-tiny: 0.625rem;
--text-tiny--line-height: 0.875rem;
--text-hero: 4rem;
--text-hero--line-height: 1.1;
--text-hero--letter-spacing: -0.02em;
--text-hero--font-weight: 800;
}
```
```html
Hero Heading
Label
```
### Line Heights
```html
1.0 line-height
1.25 line-height
1.375 line-height
1.5 line-height (default)
1.625 line-height
2.0 line-height
1rem (16px)
1.5rem (24px)
2rem (32px)
```
### @tailwindcss/typography Plugin (Prose Classes)
```bash
npm install @tailwindcss/typography
```
```js
// v3
module.exports = {
plugins: [require('@tailwindcss/typography')],
}
```
```css
/* v4 */
@plugin "@tailwindcss/typography";
```
```html
Article Title
Rendered markdown with beautiful typography defaults.
Styled blockquotes.
Styled code blocks
```
**Prose modifiers**: `prose-sm`, `prose-base`, `prose-lg`, `prose-xl`, `prose-2xl`, `prose-invert` (dark mode), `prose-gray`, `prose-slate`, `prose-zinc`.
## Plugins
### Writing Plugins (v3)
```js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
// addUtilities: generate utility classes
plugin(function ({ addUtilities }) {
addUtilities({
'.text-balance': { 'text-wrap': 'balance' },
'.text-pretty': { 'text-wrap': 'pretty' },
'.content-auto': { 'content-visibility': 'auto' },
})
}),
// addComponents: generate component classes
plugin(function ({ addComponents, theme }) {
addComponents({
'.btn': {
padding: `${theme('spacing.2')} ${theme('spacing.4')}`,
borderRadius: theme('borderRadius.lg'),
fontWeight: theme('fontWeight.medium'),
fontSize: theme('fontSize.sm'),
lineHeight: theme('lineHeight.5'),
},
})
}),
// matchUtilities: generate dynamic utilities with values
plugin(function ({ matchUtilities, theme }) {
matchUtilities(
{
'grid-area': (value) => ({ gridArea: value }),
},
{ values: { header: 'header', main: 'main', sidebar: 'sidebar', footer: 'footer' } }
)
}),
],
}
```
### Popular Plugins
| Plugin | Purpose | Install |
|--------|---------|---------|
| `@tailwindcss/typography` | Prose classes for rich content | `npm i @tailwindcss/typography` |
| `@tailwindcss/forms` | Better default form styles | `npm i @tailwindcss/forms` |
| `@tailwindcss/container-queries` | Container queries (v3) | `npm i @tailwindcss/container-queries` |
| `tailwindcss-animate` | Animation utilities (shadcn) | `npm i tailwindcss-animate` |
| `@tailwindcss/aspect-ratio` | Aspect ratio (pre-native) | `npm i @tailwindcss/aspect-ratio` |
```js
// v3: Using plugins
module.exports = {
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/container-queries'),
require('tailwindcss-animate'),
],
}
```
```css
/* v4: Using plugins */
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
/* container-queries not needed in v4 (native) */
@plugin "tailwindcss-animate";
```
## Content Configuration
### Template Paths (v3)
```js
// v3: Tell Tailwind where to find class usage
module.exports = {
content: [
'./src/**/*.{html,js,jsx,ts,tsx,vue,svelte,astro}',
'./public/index.html',
'./content/**/*.md',
// Include component libraries
'./node_modules/@acme/ui/dist/**/*.js',
],
}
```
### Automatic Detection (v4)
v4 automatically scans your project for template files. Override with `@source` if needed:
```css
/* v4: Explicit source paths (usually not needed) */
@source "../content/**/*.md";
@source "../node_modules/@acme/ui/dist/**/*.js";
```
### Safelisting
```js
// v3: Safelist classes that can't be detected
module.exports = {
safelist: [
'bg-red-500',
'bg-green-500',
'bg-blue-500',
// Pattern-based
{ pattern: /bg-(red|green|blue)-(100|500|900)/ },
// With variants
{ pattern: /text-(red|green|blue)-500/, variants: ['hover', 'dark'] },
],
}
```
```css
/* v4: Safelist via CSS comment */
@source "safelist:bg-red-500,bg-green-500,bg-blue-500";
```
## @layer Directive
### Layer Hierarchy
```css
@import "tailwindcss";
/* BASE: Reset, HTML element defaults, @font-face */
@layer base {
html {
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
}
body {
font-family: var(--font-body);
color: var(--color-on-surface);
background-color: var(--color-surface);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
font-weight: 700;
}
a {
color: var(--color-primary);
text-decoration-line: underline;
text-underline-offset: 2px;
}
}
/* COMPONENTS: Reusable multi-property classes */
@layer components {
.card {
@apply rounded-lg bg-white p-6 shadow-md dark:bg-gray-800;
}
.btn {
@apply inline-flex items-center justify-center rounded-lg px-4 py-2
text-sm font-medium transition-colors
focus-visible:outline-2 focus-visible:outline-offset-2;
}
.btn-primary {
@apply btn bg-blue-600 text-white hover:bg-blue-700
focus-visible:outline-blue-600;
}
.input {
@apply w-full rounded-lg border border-gray-300 bg-white px-3 py-2
text-gray-900 placeholder:text-gray-400
focus:border-transparent focus:ring-2 focus:ring-blue-500
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100;
}
}
/* UTILITIES: Single-property overrides */
@layer utilities {
.text-balance {
text-wrap: balance;
}
.content-auto {
content-visibility: auto;
}
.scrollbar-hidden {
scrollbar-width: none;
&::-webkit-scrollbar { display: none; }
}
}
```
### When to Use Each Layer
| Layer | Use For | Example |
|-------|---------|---------|
| `base` | HTML element defaults, resets, `@font-face` | Body font, link colors, heading styles |
| `components` | Multi-property reusable patterns | `.card`, `.btn`, `.input`, `.badge` |
| `utilities` | Single-purpose utility classes | `.text-balance`, `.content-auto` |
## @apply
### When @apply is Appropriate
```css
/* GOOD: Component libraries where utility classes aren't available */
@layer components {
.prose-custom h2 {
@apply text-2xl font-bold text-gray-900 dark:text-white mt-8 mb-4;
}
}
/* GOOD: Markdown content styling */
@layer base {
.markdown-content h1 { @apply text-3xl font-bold mb-4; }
.markdown-content p { @apply text-gray-600 dark:text-gray-400 mb-4; }
.markdown-content a { @apply text-blue-600 hover:underline; }
}
/* GOOD: Repeated pattern across many elements */
@layer components {
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-colors
focus-visible:outline-2 focus-visible:outline-offset-2;
}
}
```
### When NOT to Use @apply
```html
```
**Rule of thumb**: Use inline utilities by default. Only reach for `@apply` when:
1. You need to style elements you don't control (CMS content, markdown)
2. You're building a component library with `.btn`, `.card` classes
3. A combination of 5+ utilities is repeated identically in 5+ places
## Custom Variants
### addVariant (v3)
```js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function ({ addVariant }) {
// Simple variant
addVariant('hocus', ['&:hover', '&:focus'])
addVariant('supports-grid', '@supports (display: grid)')
addVariant('optional', '&:optional')
// Parent state
addVariant('group-sidebar', ':merge(.group-sidebar):hover &')
}),
],
}
```
### @custom-variant (v4)
```css
/* v4: Define custom variants in CSS */
@custom-variant hocus (&:hover, &:focus);
@custom-variant optional (&:optional);
@custom-variant supports-grid (@supports (display: grid));
/* Dark mode with custom selector */
@custom-variant dark (&:where(.dark, .dark *));
/* Dark mode with data attribute */
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
```
```html
```
### Data Attribute Variants
```html
Responds to data-state attribute
Responds to data-size
Loading state
```
## Prefix Configuration
### v3: Avoiding Conflicts
```js
// v3: Add prefix to all Tailwind classes
module.exports = {
prefix: 'tw-',
}
```
```html
```
### v4: Prefix
```css
/* v4: Prefix via @import option */
@import "tailwindcss" prefix(tw);
```
## Important Configuration
### v3: Important Selector Strategy
```js
// v3: Make all utilities important
module.exports = {
// Option 1: All utilities get !important
important: true,
// Option 2: Selector strategy (recommended)
important: '#app',
}
```
### Per-Utility Important
```html
This text is red regardless of other styles
Background is red
```
## Dark Mode Configuration
### Class Strategy (v3)
```js
// v3: tailwind.config.js
module.exports = {
darkMode: 'class', // Toggle via class="dark" on
}
```
```html
```
### Media Strategy (v3 and v4 Default)
```js
// v3: Uses system preference (prefers-color-scheme)
module.exports = {
darkMode: 'media', // Default in v4
}
```
### Selector Strategy (v4)
```css
/* v4: Class-based dark mode */
@custom-variant dark (&:where(.dark, .dark *));
/* v4: Data attribute dark mode */
@custom-variant dark (&:where([data-mode="dark"], [data-mode="dark"] *));
```
### Dark Mode Toggle Script
```html
```
## Container Queries
### Setup (v3)
```bash
npm install @tailwindcss/container-queries
```
```js
// v3: tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/container-queries')],
}
```
### Setup (v4)
Container queries are native in v4. No plugin needed.
### Usage
```html
Responds to parent width
Not viewport width
```
### Container Query Breakpoints
| Variant | Min Width |
|---------|-----------|
| `@xs:` | 320px (20rem) |
| `@sm:` | 384px (24rem) |
| `@md:` | 448px (28rem) |
| `@lg:` | 512px (32rem) |
| `@xl:` | 576px (36rem) |
| `@2xl:` | 672px (42rem) |
| `@3xl:` | 768px (48rem) |
| `@4xl:` | 896px (56rem) |
| `@5xl:` | 1024px (64rem) |
### Container Query Units
```html
50% of container width, 30% of container height
```
| Unit | Description |
|------|-------------|
| `cqw` | 1% of container width |
| `cqh` | 1% of container height |
| `cqi` | 1% of container inline size |
| `cqb` | 1% of container block size |
| `cqmin` | Smaller of `cqi` / `cqb` |
| `cqmax` | Larger of `cqi` / `cqb` |
### Nested Containers
```html
```