tailwind.config
tailwind.config.js: content paths, theme.extend, plugins, and the choices that shape your design system.
Tailwind — config
EXAMPLE
// ===== The file =====
// tailwind.config.js (or .ts via type-stripping)
import typography from '@tailwindcss/typography';
import forms from '@tailwindcss/forms';
import { Config } from 'tailwindcss';
export default {
// 1. Where to scan for classes
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx,vue,svelte}',
],
// 2. Dark mode strategy: 'media' (default), 'class', or ['class', '.dark']
darkMode: 'class',
theme: {
// Replacing the default scale (rarely):
screens: { sm: '640px', md: '768px', lg: '1024px', xl: '1280px' },
// Extending (the common path):
extend: {
colors: {
brand: { 50: '#eff6ff', 500: '#2563eb', 700: '#1d4ed8' },
},
fontFamily: {
sans: ['Inter var', 'ui-sans-serif', 'system-ui'],
},
spacing: {
18: '4.5rem',
128: '32rem',
},
borderRadius: {
'4xl': '2rem',
},
animation: {
wiggle: 'wiggle 1s ease-in-out infinite',
},
keyframes: {
wiggle: {
'0%, 100%': { transform: 'rotate(-2deg)' },
'50%': { transform: 'rotate(2deg)' },
},
},
},
},
plugins: [typography, forms],
} satisfies Config;
// ===== Content paths matter =====
// The 'content' globs decide what classes are emitted.
// Miss a path -> classes 'silently' do nothing.
// Include node_modules of design systems if you consume their classes:
content: [
'./src/**/*.{ts,tsx}',
'./node_modules/@my-org/ui/**/*.js',
],
// ===== Safelist =====
// Force-include classes that won't be detected statically (e.g. dynamic names):
content: {
files: ['./src/**/*.{ts,tsx}'],
transform: {
// optional per-extension transforms
},
},
safelist: ['bg-red-500', { pattern: /bg-(red|green|blue)-(100|500|900)/ }],
// ===== Theme tokens drive consistency =====
// Once you extend the theme, classes follow:
// bg-brand-500, text-brand-700, font-sans, p-18 ...
// ===== Plugins =====
// Built-in features you toggle:
// @tailwindcss/typography -> prose-* for long content
// @tailwindcss/forms -> sane defaults for form controls
// @tailwindcss/container-queries
// @tailwindcss/aspect-ratio (built-in since 3.0)
// Custom plugin example:
import plugin from 'tailwindcss/plugin';
const myPlugin = plugin(function ({ addBase, addComponents, theme }) {
addBase({ 'h1': { fontSize: theme('fontSize.2xl') } });
addComponents({ '.card': { padding: theme('spacing.4'), borderRadius: theme('borderRadius.lg') } });
});
// ===== Important things to know =====
// - 'content' is NOT the same as 'purge' (which was a v2 concept)
// - JIT mode is default in 3+; no need to opt in
// - Arbitrary values text-[13px] are powerful — use sparingly
// - 'extend' beats 'replace'; replacing the scale resets everything
// ===== Patterns to internalise =====
// - Extend; rarely replace
// - Brand colours, fonts, spacing in config (one source of truth)
// - Plugins for cross-cutting helpers (prose, forms, container queries)
// - Restart the dev server after config changes
// ===== Pitfalls =====
// - content paths missing a folder -> classes do not appear
// - Replacing the colour palette with a small set -> losing utility coverage
// - Long arbitrary values in markup instead of token names
// - Mixing v2 and v3 patterns from different tutorials
Why it matters
tailwind.config.js is your design system. Extend the theme with brand tokens, register plugins for cross-cutting helpers, and watch the content paths like a hawk. The config file is small but load-bearing — every class your app emits passes through it.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// tailwind.config.js
export default {
content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
theme: { extend: { colors: { brand: '#04AA6D' } } },
plugins: [],
};
Try it Yourself »
Discussion
Loading…