Overview
@theme is the v4 replacement for the JavaScript config object. Every declaration becomes a CSS custom property at :root and a generated utility class. A single source of truth drives both Tailwind utilities and bespoke CSS. The parent reference for Tailwind v4 conventions is tailwind; for the CSS custom property substrate see css-custom-properties.
Declare tokens in @theme, not in raw @layer base
Place design tokens inside the @theme block at the top of your entry CSS file. Tailwind registers them as utilities automatically.
@import "tailwindcss";
@theme {
--color-brand: oklch(0.72 0.18 264);
--color-ink: oklch(0.2 0 0);
--color-surface: oklch(0.98 0 0);
--font-display: "Schibsted Grotesk", system-ui, sans-serif;
--radius-card: 0.75rem;
--shadow-card: 0 1px 3px rgb(0 0 0 / 0.1);
}This produces bg-brand, text-ink, bg-surface, font-display, rounded-card, and shadow-card without any extra configuration. Putting the same properties in @layer base skips the utility generation step and is a common migration mistake from v3.
Use oklch for color tokens
Define color tokens with oklch(lightness chroma hue) instead of hex or rgb. The oklch color space is perceptually uniform: stepping lightness by 10% looks like the same visual change across every hue. Use css-custom-properties patterns for fallback values if you need to support older browsers, though evergreen browsers have shipped oklch since 2023.
@theme {
--color-brand-100: oklch(0.95 0.05 264);
--color-brand-500: oklch(0.72 0.18 264);
--color-brand-900: oklch(0.35 0.14 264);
}The scale becomes bg-brand-100, bg-brand-500, and bg-brand-900. Keep scales short. A full 50 to 950 scale for every hue multiplies your token count and your build output.
Reference tokens from bespoke CSS without duplication
Because @theme writes every token to :root as a CSS custom property, bespoke CSS reads them directly.
.prose blockquote {
border-left: 4px solid var(--color-brand-500);
padding-inline-start: 1rem;
color: var(--color-ink);
}This means third-party markup you cannot annotate with classes, like a CMS article body, still participates in the design system. There is no secondary token definition file.
Keep the token set small and intentional
Every token in @theme generates a utility. Fifty color tokens times five utility families (bg, text, border, ring, outline) is 250 utility classes in the output. Remove unused tokens to keep the build small; the scanner only prunes utilities that never appear in source, not tokens that appear in @theme but only via var() in bespoke CSS.
Rules of thumb for a tight token set:
- Colors: brand scale (3 to 5 steps), neutral scale (3 to 5 steps), semantic names (danger, success, warning).
- Spacing: override the default scale only if the design uses a non-standard base unit.
- Typography: one display font if needed; body font only if the default stack is not right.
- Radius: card, button, input. Three values cover most UIs.
Support runtime theme switching with CSS custom property overrides
@theme writes to :root. Override the same property on a scoped selector or in a media query to switch themes at runtime without a build step.
@theme {
--color-surface: oklch(0.98 0 0);
--color-ink: oklch(0.2 0 0);
}
[data-theme="dark"] {
--color-surface: oklch(0.12 0 0);
--color-ink: oklch(0.92 0 0);
}A JavaScript one-liner toggles data-theme="dark" on the root element. All Tailwind utilities that use the token (bg-surface, text-ink) update immediately. No class rewriting or style recalculation beyond what the browser’s cascade already does. For the full dark mode strategy, see tailwind-dark-mode.
Bridge the design system from Figma token exports
Design tools like Figma export tokens as JSON or CSS variables. Map the export to @theme in a single translation file rather than scattering raw values through components.
/* tokens.css — generated from Figma, committed to version control */
@theme {
--color-primary: oklch(0.72 0.18 264);
--color-secondary: oklch(0.68 0.15 190);
--space-unit: 0.25rem;
--radius-base: 0.375rem;
}Import this file before your component CSS. When the design system updates, only tokens.css changes; no component files need touching.
Avoid token drift with a quarterly audit
Token drift is the most common long-term failure mode: two tokens for the same color, a spacing token that nothing uses, a font token that duplicates the default. Run a search for -- in your entry CSS file quarterly and delete anything with no non-@theme references. The build will not catch this for you.