Tailwind Custom Config
Customizing tailwind.config.js to extend themes, add custom colors, fonts, spacing, and configure content paths
You are an expert in configuring and extending Tailwind CSS through its configuration file.
## Key Points
- **Always use `extend` unless you need to remove defaults.** Overriding `colors` at the top level removes all built-in colors, which is rarely desired.
- **Use semantic color names** (`primary`, `surface`, `danger`) rather than visual names (`blue`, `light-gray`) so themes are swappable.
- **Keep content paths precise.** Overly broad globs like `'./**/*.{js,html}'` slow down builds and may scan `node_modules`.
- **Use `tailwind.config.ts` in TypeScript projects** for type checking and autocompletion.
- **Share configuration across projects with presets** rather than copy-pasting config files.
- **Document custom values** with comments explaining design decisions (e.g., why a specific spacing value exists).
- **Forgetting to add new file paths to `content`.** New directories or file extensions not listed in `content` result in classes being purged in production.
- **Overriding instead of extending by accident.** Placing `colors` at the `theme` level instead of `theme.extend` silently removes all default colors.
- **Stale caches after config changes.** Restart the dev server after modifying `tailwind.config.js`; some bundlers do not pick up config changes via HMR.
- **Using deprecated color names.** Colors like `lightBlue`, `warmGray`, and `trueGray` were renamed in Tailwind v3 to `sky`, `stone`, and `neutral`.
- **Overly broad content paths.** Globs like `'./**/*.{js,html}'` scan `node_modules` and slow builds dramatically. Be precise about which directories contain template files and list only those.skilldb get tailwind-skills/Tailwind Custom ConfigFull skill: 319 linesCustomizing tailwind.config — Tailwind CSS
You are an expert in configuring and extending Tailwind CSS through its configuration file.
Overview
The tailwind.config.js (or tailwind.config.ts) file is the central place to customize Tailwind's default design system. You can extend or override colors, spacing, typography, breakpoints, and more. Understanding the difference between extend and top-level overrides is critical to maintaining a usable configuration.
Core Philosophy
The Tailwind configuration file is where a generic utility framework becomes your team's design system. Rather than accepting every default and sprinkling arbitrary values throughout the codebase, you invest upfront in defining your color palette, type scale, spacing rhythm, and breakpoints in one place. This configuration becomes a contract between design and engineering — every value in it is intentional, documented, and available as a utility class.
The distinction between extending and overriding is foundational to working with Tailwind's config. Extending adds your custom values alongside the defaults, giving you the full palette plus your brand colors. Overriding replaces the defaults entirely, which is powerful when you want to constrain the design system but dangerous when done accidentally. Understanding this boundary prevents the most common Tailwind configuration mistakes and keeps the utility set predictable.
Configuration should be treated as a shared artifact, not a dump of ad-hoc values. When someone adds spacing: { '13.5': '3.375rem' } without a clear design rationale, it signals a breakdown in the system. Every custom token should have a reason to exist, and presets provide the mechanism to share that reasoning across multiple projects in an organization.
Core Concepts
Configuration File Structure
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
// Top-level keys REPLACE the defaults entirely
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
},
extend: {
// Keys inside extend MERGE with defaults
colors: {
brand: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a5f',
},
},
},
},
plugins: [],
}
Content Paths
The content array tells Tailwind where to scan for class names so it can tree-shake unused CSS:
content: [
'./app/**/*.{js,ts,jsx,tsx}', // Next.js app directory
'./pages/**/*.{js,ts,jsx,tsx}', // Next.js pages
'./components/**/*.{js,ts,jsx,tsx}',
'./src/**/*.{html,js,svelte}', // Svelte project
'./index.html', // Vite entry point
]
Extending vs. Overriding
theme: {
// OVERRIDE: replaces ALL default colors with only these
colors: {
black: '#000',
white: '#fff',
primary: '#4f46e5',
},
extend: {
// EXTEND: adds to default colors, keeping gray, blue, red, etc.
colors: {
primary: '#4f46e5',
surface: '#f8fafc',
},
},
}
Always use extend unless you intentionally want to remove all defaults for that key.
Implementation Patterns
Custom Color Palette
// tailwind.config.js
const colors = require('tailwindcss/colors')
module.exports = {
theme: {
extend: {
colors: {
// Reference existing Tailwind palettes
primary: colors.indigo,
secondary: colors.slate,
accent: colors.amber,
// Custom brand colors with full shade scale
brand: {
50: '#faf5ff',
100: '#f3e8ff',
200: '#e9d5ff',
300: '#d8b4fe',
400: '#c084fc',
500: '#a855f7',
600: '#9333ea',
700: '#7e22ce',
800: '#6b21a8',
900: '#581c87',
950: '#3b0764',
},
},
},
},
}
Usage: bg-brand-500, text-brand-900, border-primary-300.
Custom Fonts
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'],
display: ['Cal Sans', 'Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
},
},
}
Usage: font-sans, font-display, font-mono.
Custom Spacing and Sizing
theme: {
extend: {
spacing: {
'4.5': '1.125rem', // 18px — between p-4 and p-5
'18': '4.5rem', // 72px
'88': '22rem', // 352px
},
maxWidth: {
'8xl': '88rem',
prose: '65ch',
},
minHeight: {
'screen-75': '75vh',
},
},
}
Custom Breakpoints
theme: {
screens: {
xs: '475px',
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
'3xl': '1920px',
},
}
Note: screens is typically overridden (not extended) so you control the full set and ordering.
Custom Animations
theme: {
extend: {
keyframes: {
'fade-in': {
'0%': { opacity: '0', transform: 'translateY(10px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
'slide-in-right': {
'0%': { transform: 'translateX(100%)' },
'100%': { transform: 'translateX(0)' },
},
},
animation: {
'fade-in': 'fade-in 0.3s ease-out',
'slide-in-right': 'slide-in-right 0.3s ease-out',
},
},
}
Using CSS Variables for Theming
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: 'hsl(var(--card))',
'card-foreground': 'hsl(var(--card-foreground))',
primary: 'hsl(var(--primary))',
'primary-foreground': 'hsl(var(--primary-foreground))',
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
},
}
This pattern (used by shadcn/ui) allows switching entire themes by changing CSS variable values.
TypeScript Configuration
// tailwind.config.ts
import type { Config } from 'tailwindcss'
const config: Config = {
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {
colors: {
brand: {
500: '#6366f1',
},
},
},
},
plugins: [],
}
export default config
Presets for Shared Configuration
// tailwind-preset.js — shared across multiple projects
module.exports = {
theme: {
extend: {
colors: {
brand: { /* ... */ },
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
},
},
plugins: [
require('@tailwindcss/typography'),
],
}
// tailwind.config.js — per-project
module.exports = {
presets: [require('./tailwind-preset')],
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
// Project-specific overrides merge on top of preset
},
},
}
Best Practices
- Always use
extendunless you need to remove defaults. Overridingcolorsat the top level removes all built-in colors, which is rarely desired. - Use semantic color names (
primary,surface,danger) rather than visual names (blue,light-gray) so themes are swappable. - Keep content paths precise. Overly broad globs like
'./**/*.{js,html}'slow down builds and may scannode_modules. - Use
tailwind.config.tsin TypeScript projects for type checking and autocompletion. - Share configuration across projects with presets rather than copy-pasting config files.
- Document custom values with comments explaining design decisions (e.g., why a specific spacing value exists).
Common Pitfalls
- Forgetting to add new file paths to
content. New directories or file extensions not listed incontentresult in classes being purged in production. - Overriding instead of extending by accident. Placing
colorsat thethemelevel instead oftheme.extendsilently removes all default colors. - Stale caches after config changes. Restart the dev server after modifying
tailwind.config.js; some bundlers do not pick up config changes via HMR. - Conflicting preset and local config. When a preset defines a key and the local config also defines it under
extend, they merge. If both define it at the top level, the local config wins entirely. - Using deprecated color names. Colors like
lightBlue,warmGray, andtrueGraywere renamed in Tailwind v3 tosky,stone, andneutral.
Anti-Patterns
-
The junk-drawer config. Adding arbitrary spacing values, one-off colors, and custom breakpoints without design justification turns the config into a grab bag. Every custom token should trace back to a design decision, not a developer's impulse to pixel-match a mockup.
-
Overriding when you meant to extend. Placing
colorsat thethemetop level instead of insidetheme.extendsilently removes every default color. This is the single most common config mistake and results inbg-gray-100suddenly producing no output. -
Duplicating config across projects. Copy-pasting
tailwind.config.jsbetween repositories leads to drift. Use Tailwind presets to share a canonical configuration and let individual projects extend it with project-specific additions. -
Overly broad content paths. Globs like
'./**/*.{js,html}'scannode_modulesand slow builds dramatically. Be precise about which directories contain template files and list only those. -
Ignoring the config after initial setup. The configuration should evolve with the design system. When designers introduce a new spacing value or deprecate a color, the config should be updated and the old tokens removed, not left to accumulate indefinitely.
Install this skill directly: skilldb add tailwind-skills
Related Skills
Tailwind Animations
Animation utilities, transitions, custom keyframes, and motion patterns with Tailwind CSS
Tailwind Component Patterns
Common UI component patterns including cards, navbars, forms, modals, and badges built with Tailwind CSS
Tailwind Dark Mode
Dark mode strategies including class-based toggling, media queries, and CSS variable theming with Tailwind CSS
Tailwind Fundamentals
Utility-first CSS fundamentals with Tailwind including class composition, spacing, typography, and layout primitives
Tailwind Plugins
Writing custom Tailwind CSS plugins to add utilities, components, base styles, and variants
Tailwind Responsive Design
Responsive breakpoints, mobile-first design patterns, and adaptive layouts with Tailwind CSS