Configuration and theming
CSS-first theming in v4, the v3 JavaScript config for comparison, and when a custom utility beats a pile of @apply rules.
Theming in v4
@import "tailwindcss";
@theme {
--color-brand-500: oklch(0.62 0.19 262);
--color-brand-600: oklch(0.55 0.19 262);
--font-display: "Satoshi", ui-sans-serif, system-ui;
--spacing: 0.25rem; /* every numeric spacing step derives from this */
--breakpoint-3xl: 120rem;
--radius-card: 0.875rem;
}- Theme variables are namespaced:
--color-*generates colour utilities,--font-*font families,--breakpoint-*variants,--spacingthe whole numeric scale. - They are real CSS custom properties, so
var(--color-brand-500)also works in hand-written CSS. - Declaring a key that already exists replaces it everywhere; there is no separate extend step as there is in v3.
- Utilities for a custom value are only emitted when a matching class appears in the scanned source.
The v3 configuration file
// tailwind.config.js - v3, and loadable in v4 with @config "./tailwind.config.js"
module.exports = {
content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
theme: {
extend: {
colors: { brand: { 500: '#4f46e5', 600: '#4338ca' } },
fontFamily: { display: ['Satoshi', 'sans-serif'] },
screens: { '3xl': '120rem' }
}
},
plugins: [require('@tailwindcss/forms')]
};| Concern | v3 | v4 |
|---|---|---|
| Entry CSS | @tailwind base/components/utilities | @import "tailwindcss" |
| Content scanning | explicit content globs | automatic, adjusted with @source |
| Theme values | theme.extend in JavaScript | @theme in CSS |
| Custom variants | addVariant plugin | @custom-variant |
| Config file | required | optional, loaded with @config |
| Browser support | wider | Safari 16.4, Chrome 111, Firefox 128+ |
Custom utilities and component classes
@utility content-auto {
content-visibility: auto;
}
/* a semantic class for markup you do not control */
@layer components {
.btn-primary {
@apply inline-flex items-center rounded-lg bg-brand-500 px-4 py-2 text-white;
}
}💡
Reach for
@utility when you are adding a new building block, and keep @apply for third-party markup or a genuinely semantic name. Wrapping every group of utilities in @apply rebuilds the CSS-file architecture that utilities were meant to replace.FAQ
Are my custom theme values always included in the CSS?
No. A theme token produces a utility only when the corresponding class appears in the scanned source, unless another rule references the variable. That is what keeps the output small.
Should I use @apply?
Sparingly. It is right for a class handed to markup you cannot edit, such as a CMS field or a third-party widget, and wrong as the default way to style components you own.
Related
Responsive and state variants CSS: getting started
Last refreshed 2026-09-18.