Capítulo 13 de 29
@theme defines design tokens as special CSS custom properties that Tailwind reads to generate matching utility classes and variants — unlike plain :root variables, a theme variable is both a normal CSS variable and an instruction to create a utility/variant with the same name, which is why it needs its own directive and must live at the top level (never nested).
@theme vs :root: use @theme when a value should generate a utility class; use :root for a variable that's just a variable with no corresponding utility.| Namespace | Drives |
|---|---|
--color-* | Color utilities (bg-red-500, text-sky-300, ...) |
--font-* | Font family (font-sans) |
--text-* | Font size (text-xl) |
--font-weight-* | Font weight (font-bold) |
--tracking-* | Letter spacing (tracking-wide) |
--leading-* | Line height (leading-tight) |
--tab-size-* | Tab size (tab-github) |
--breakpoint-* | Responsive variants (sm:*) |
--container-* | Container query variants (@sm:*) and size utilities (max-w-md) |
--spacing-* / --spacing | Spacing/sizing utilities (px-4, max-h-16, ...) |
--radius-* | Border radius (rounded-sm) |
--shadow-* / --inset-shadow-* | Box shadow / inset shadow |
--drop-shadow-* | Drop-shadow filter |
--blur-* | Blur filter |
--perspective-* | Perspective |
--zoom-* | Zoom |
--aspect-* | Aspect ratio |
--ease-* | Transition timing function |
--animate-* | Animation (paired with a @keyframes block) |
flex, object-cover) are hardcoded, not theme-driven — only namespaced tokens generate/remove utilities dynamically.@import "tailwindcss" pulls in theme.css (default palette, type scale, shadows, fonts) as the theme layer, alongside preflight.css (base) and utilities.css (utilities) — everything like bg-red-200/font-serif/shadow-sm exists because the default theme defines those tokens, not because they're built into the framework.--font-script: "Great Vibes", cursive;) to get a new utility (font-script) alongside the defaults.--breakpoint-sm: 30rem;) to change that one utility/variant's behavior.--color-*: initial; then define only your replacements — removes every default utility in that namespace, keeping only your custom ones.--*: initial; at the top of @theme wipes every default token across all namespaces — only tokens you define afterward produce utilities. Useful for a from-scratch design system.@keyframes inside @theme alongside the matching --animate-* token so the keyframes ship in the generated CSS; define them outside @theme instead if they should always be included regardless of whether the --animate-* utility is used.@theme inline: required when a token's value references another CSS variable (--font-sans: var(--font-inter);). Without inline, the utility emits var(--font-sans) which resolves where --font-sans is defined (often the document root) — if the variable it points to (--font-inter) is only set deeper in the tree (e.g. by a font-loader script on a nested element), the outer reference sees no value and silently falls back. inline bakes in the value at compile time instead of leaving a live reference, avoiding that scoping trap.@theme static: forces all theme variables to be emitted as CSS custom properties in the output, even ones no utility class currently uses — useful when you plan to reference var(--color-primary) directly (e.g. from JS or hand-written CSS) without ever using the matching utility class (which is what would normally trigger generation).@theme { --*: initial; ... } block can live in its own file/package and be pulled into multiple projects via @import "../brand/theme.css"; — works for monorepo packages or an npm-published theme file.--token variables directly in custom CSS (color: var(--color-gray-700);), in arbitrary values combined with calc() (rounded-[calc(var(--radius-xl)-1px)]), or in JS — either pass the CSS variable straight through to an animation library (Motion: animate={{ backgroundColor: "var(--color-blue-500)" }}), or resolve it at runtime with getComputedStyle(document.documentElement).getPropertyValue("--shadow-xl").@theme {
--color-mint-500: oklch(0.72 0.11 178); /* → bg-mint-500, text-mint-500, fill-mint-500, ... */
--breakpoint-3xl: 120rem; /* → 3xl:* variant */
}
/* fully custom theme, no defaults survive */
@theme {
--*: initial;
--spacing: 4px;
--font-body: Inter, sans-serif;
--color-lagoon: oklch(0.72 0.11 221.19);
}
-*) determines what kind of utility/variant it produces — naming a token correctly is what wires it up, there's no separate registration step.@theme inline whenever a token's value is var(...) pointing at something set elsewhere in the cascade — plain @theme can silently produce a broken fallback in that case.--*: initial (global) and --namespace-*: initial (single namespace) are the only two ways to remove default tokens — there's no per-token deny-list.--token CSS variables over hand-copying values (colors, shadows, spacing) into custom CSS or JS — it stays in sync automatically when the theme changes.--color-* namespace is the same mechanism, covered there with the full default palette.@theme alongside @utility/@custom-variant for the full custom-extension toolkit.--breakpoint-*/--container-* are theme namespaces that generate variants, not utilities.