Tailwind Theme Configuration
Tailwind CSS generates utility classes from a theme: your colors produce bg-brand-500 and text-brand-500, your spacing scale produces p-4 and gap-6, and your breakpoints produce md: and lg: variants. Customizing the theme is how Tailwind turns into your design system instead of a generic look.
Tailwind v4 moved configuration into CSS. Instead of a tailwind.config.js, you define theme variables in an @theme block, and they become both utility classes and real CSS custom properties available at runtime. That aligns Tailwind with design tokens and CSS custom properties, and removes a whole layer of JavaScript configuration.
TL;DR
- Import Tailwind with
@import "tailwindcss";and customize with@theme { … }in CSS. - Theme variables live in namespaces (
--color-*,--font-*,--spacing,--breakpoint-*,--radius-*,--shadow-*…), and each generates utilities. - Theme variables are emitted as CSS variables on
:root, usable in custom CSS and JavaScript. - Override a namespace entirely with
--color-*: initial;, or extend it by adding variables. - Add custom utilities with
@utility, and custom variants with@custom-variant. - Share themes as CSS files imported across projects, or keep JS config via
@configduring migration.
Quick Example
Core Concepts
CSS-First Configuration
Tailwind v4 uses CSS-native configuration:
@import "tailwindcss";loads the framework: theme, base styles (Preflight), and utilities.@themedefines theme variables.@sourceadds or excludes content paths when automatic detection needs help (it scans your project automatically by default).@utility,@variant/@custom-variant,@plugin, and@config(legacy JS config) extend it.
No content array is required: v4 detects template files automatically, respecting .gitignore.
Theme Namespaces
In v4, spacing is driven by a single --spacing value (default 0.25rem), so p-4 is calc(var(--spacing) * 4) and any integer works (p-13) without configuration.
Extending vs Overriding
- Adding
--color-brand-500extends the default palette. --color-*: initial;removes all default colors, so only your palette exists, which is good for strict design systems.--*: initial;resets the whole default theme.
Runtime CSS Variables
Every theme variable is emitted as a CSS variable, so you can use it anywhere:
and read or override it at runtime, which enables per-tenant theming and dark mode by redefining variables under a selector. Use @theme inline when a variable references another variable and should resolve at use time, and @theme static to always emit all variables even if unused.
Custom Utilities and Variants
@utility name { … }registers a utility that participates in variants and sorting. Functional utilities can take values (tab-4from@utility tab-* { tab-size: --value(integer); }).@custom-variant name (selector)defines variants, for example@custom-variant theme-midnight (&:where([data-theme=midnight] *));.@layer base/@layer componentsstill hold base element styles and component classes.
Plugins and Sharing
Official plugins (typography, forms) load with @plugin "@tailwindcss/typography";. To share a design system, publish a CSS file containing your @theme, utilities, and variants, which apps import after Tailwind. It's much simpler than sharing JS presets.
Best Practices
Define Semantic Tokens on Top of the Palette
Components should use names like bg-surface, text-ink, border-muted, and bg-accent, mapped to palette colors. Themes then change semantic tokens without touching markup. See design tokens.
Constrain the Design Space
Remove or limit defaults you don't want (extra color families, arbitrary shadows), so developers pick from the design system's scale instead of improvising. Constraints create consistency.
Prefer Theme Values Over Arbitrary Values
p-[13px] and text-[#6d28d9] bypass the system. Use them sparingly for one-offs; if a value recurs, add it to the theme.
Use OKLCH for Palettes
Tailwind v4's default palette uses OKLCH, which gives perceptually even steps and wider-gamut colors. Defining your palette in OKLCH keeps lightness consistent across hues.
Common Mistakes
Looking for tailwind.config.js in v4
v4 doesn't read a JavaScript config by default. Put customizations in @theme. For migration, @config "./tailwind.config.js"; still loads a legacy config. See Tailwind v4 migration.
Defining Tokens Outside @theme
Variables in a plain :root {} block don't generate utilities. Only @theme variables create classes (though you can reference plain variables in arbitrary values).
Dynamic Class Names
FAQ
Where did tailwind.config.js go in Tailwind v4?
Configuration moved to CSS: theme variables are defined in @theme blocks, and plugins, utilities, and variants are declared with CSS directives. JavaScript configs still work via @config for gradual migration, but the CSS-first approach is the default.
How do I add custom colors in Tailwind v4?
Add variables in the --color-* namespace inside @theme, for example --color-brand-500: oklch(0.6 0.22 293);. Tailwind generates bg-brand-500, text-brand-500, and every other color utility, and exposes var(--color-brand-500) at runtime.
How do I remove the default Tailwind colors?
Set --color-*: initial; inside @theme before defining your own colors. Only your palette remains, which keeps teams within the design system.
Can I use Tailwind theme values in regular CSS?
Yes. In v4 every theme variable is a CSS custom property, so var(--color-brand-500), var(--spacing), and var(--radius-card) work in any CSS, including third-party component styles and inline styles.
Related Topics
- Tailwind — The framework overview
- Tailwind Variants — Responsive, state, and custom variants
- Tailwind Dark Mode — Theming with variables
- Tailwind v4 Migration — Moving from JS config
- Design Tokens — Structuring design values
- CSS Custom Properties — The runtime layer underneath