Theme Variable Reference
Every @theme namespace in Tailwind v4 — the utilities each generates, the full default scales, and how to extend, override, disable and read them.
Theme variables are CSS custom properties declared inside @theme. A variable's namespace — the part before the last segment — decides which utilities it generates. Everything on this page is a real default in Tailwind v4.
Namespace summary#
| Namespace | Generates | Example |
|---|---|---|
--color-* | Every colour utility | bg-mint-500, text-mint-500, ring-mint-500 |
--font-* | Font family | font-display |
--text-* | Font size, plus paired line height, letter spacing and weight | text-hero |
--font-weight-* | Font weight | font-book |
--tracking-* | Letter spacing | tracking-caps |
--leading-* | Line height | leading-prose |
--tab-size-* | Tab size v4.3 | tab-github |
--breakpoint-* | Responsive variants | 3xl:flex |
--container-* | Container query variants and max-width | @3xl:grid, max-w-3xl |
--spacing-* | Named spacing values | p-gutter |
--radius-* | Border radius | rounded-card |
--shadow-* | Box shadow | shadow-card |
--inset-shadow-* | Inset box shadow | inset-shadow-well |
--drop-shadow-* | Drop shadow filter | drop-shadow-glow |
--text-shadow-* | Text shadow v4.1 | text-shadow-emboss |
--blur-* | Blur and backdrop blur | blur-frost, backdrop-blur-frost |
--perspective-* | 3D perspective | perspective-stage |
--zoom-* | Zoom v4.3 | zoom-compact |
--aspect-* | Aspect ratio | aspect-poster |
--ease-* | Transition timing function | ease-snappy |
--animate-* | Animation shorthand | animate-wiggle |
Two variables are not namespaces but scalars in their own right:
| Variable | Effect |
|---|---|
--spacing | The multiplier behind every numeric spacing and sizing utility |
--default-* | Base values the preflight and some utilities read — see Default variables |
--color-*#
The default palette has 22 families, each with eleven steps — 50 100 200 300 400 500 600 700 800 900 950:
| Group | Families |
|---|---|
| Warm | red orange amber yellow |
| Green | lime green emerald teal |
| Blue | cyan sky blue indigo |
| Purple/pink | violet purple fuchsia pink rose |
| Neutral | slate gray zinc neutral stone |
| Muted v4.2 | mauve olive mist taupe |
Plus --color-black: #000 and --color-white: #fff. The keywords transparent, current and inherit are built in and are not theme variables.
Every family is defined in oklch(), which is why the shade ramps stay perceptually even and why bg-sky-500/40 mixes predictably.
@theme {
--color-brand-50: oklch(0.97 0.02 264);
--color-brand-500: oklch(0.62 0.19 264);
--color-brand-900: oklch(0.35 0.14 264);
}One --color-* entry generates bg-*, text-*, border-*, outline-*, ring-*, inset-ring-*, shadow-*, inset-shadow-*, drop-shadow-*, text-shadow-*, divide-*, decoration-*, accent-*, caret-*, fill-*, stroke-*, from-*, via-*, to-*, placeholder-*, selection-*, marker-* and the scrollbar-thumb-* / scrollbar-track-* pair.
--font-*#
| Variable | Default |
|---|---|
--font-sans | -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' |
--font-serif | ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif |
--font-mono | ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace |
--font-* accepts two sub-properties:
@theme {
--font-display: "Satoshi", ui-sans-serif, sans-serif;
--font-display--font-feature-settings: "cv02", "cv03";
--font-display--font-variation-settings: "opsz" 32;
}font-display then sets all three declarations at once.
--text-*#
Each size ships with a paired line height.
| Variable | Font size | --line-height |
|---|---|---|
--text-xs | 0.75rem | calc(1 / 0.75) |
--text-sm | 0.875rem | calc(1.25 / 0.875) |
--text-base | 1rem | calc(1.5 / 1) |
--text-lg | 1.125rem | calc(1.75 / 1.125) |
--text-xl | 1.25rem | calc(1.75 / 1.25) |
--text-2xl | 1.5rem | calc(2 / 1.5) |
--text-3xl | 1.875rem | calc(2.25 / 1.875) |
--text-4xl | 2.25rem | calc(2.5 / 2.25) |
--text-5xl | 3rem | 1 |
--text-6xl | 3.75rem | 1 |
--text-7xl | 4.5rem | 1 |
--text-8xl | 6rem | 1 |
--text-9xl | 8rem | 1 |
Sub-property syntax#
A second -- suffix attaches a companion declaration to a font-size token:
@theme {
--text-hero: 4rem;
--text-hero--line-height: 1.05;
--text-hero--letter-spacing: -0.02em;
--text-hero--font-weight: 700;
}<h1 class="text-hero">Sets size, leading, tracking and weight.</h1>Override any of them inline — text-hero leading-tight wins, because the explicit utility comes later in the cascade. The text-<size>/<number> shorthand does the same for line height: text-hero/none, text-sm/6.
--font-weight-*#
| Variable | Default | Utility |
|---|---|---|
--font-weight-thin | 100 | font-thin |
--font-weight-extralight | 200 | font-extralight |
--font-weight-light | 300 | font-light |
--font-weight-normal | 400 | font-normal |
--font-weight-medium | 500 | font-medium |
--font-weight-semibold | 600 | font-semibold |
--font-weight-bold | 700 | font-bold |
--font-weight-extrabold | 800 | font-extrabold |
--font-weight-black | 900 | font-black |
--tracking-*#
| Variable | Default |
|---|---|
--tracking-tighter | -0.05em |
--tracking-tight | -0.025em |
--tracking-normal | 0em |
--tracking-wide | 0.025em |
--tracking-wider | 0.05em |
--tracking-widest | 0.1em |
--leading-*#
| Variable | Default |
|---|---|
--leading-tight | 1.25 |
--leading-snug | 1.375 |
--leading-normal | 1.5 |
--leading-relaxed | 1.625 |
--leading-loose | 2 |
leading-none is a static utility for line-height: 1 and has no theme variable. Numeric leading-<number> utilities are derived from --spacing: leading-7 is calc(var(--spacing) * 7).
--spacing#
@theme {
--spacing: 0.25rem;
}One variable drives p-*, m-*, gap-*, space-*, w-*, h-*, size-*, inset-*, translate-*, leading-<number>, indent-* and more. p-4 compiles to calc(var(--spacing) * 4), so changing --spacing rescales the whole system proportionally.
Values are generated on demand — p-13, mt-96 and gap-7 all work without configuration.
Named spacing tokens live in the --spacing-* namespace:
@theme {
--spacing-gutter: 1.5rem;
}<div class="px-gutter gap-gutter">…</div>--breakpoint-*#
| Variable | Default | Variant |
|---|---|---|
--breakpoint-sm | 40rem | sm: and max-sm: |
--breakpoint-md | 48rem | md: and max-md: |
--breakpoint-lg | 64rem | lg: and max-lg: |
--breakpoint-xl | 80rem | xl: and max-xl: |
--breakpoint-2xl | 96rem | 2xl: and max-2xl: |
Adding --breakpoint-3xl: 120rem creates 3xl: and max-3xl: at once. The container utility also reads this namespace.
--container-*#
| Variable | Default | Variable | Default |
|---|---|---|---|
--container-3xs | 16rem | --container-2xl | 42rem |
--container-2xs | 18rem | --container-3xl | 48rem |
--container-xs | 20rem | --container-4xl | 56rem |
--container-sm | 24rem | --container-5xl | 64rem |
--container-md | 28rem | --container-6xl | 72rem |
--container-lg | 32rem | --container-7xl | 80rem |
--container-xl | 36rem |
This namespace does double duty: it sizes max-w-* and it defines the container query variants @3xs: through @7xl: and their @max-* counterparts.
--radius-*#
| Variable | Default | Pixels |
|---|---|---|
--radius-xs | 0.125rem | 2px |
--radius-sm | 0.25rem | 4px |
--radius-md | 0.375rem | 6px |
--radius-lg | 0.5rem | 8px |
--radius-xl | 0.75rem | 12px |
--radius-2xl | 1rem | 16px |
--radius-3xl | 1.5rem | 24px |
--radius-4xl | 2rem | 32px |
rounded-none (0) and rounded-full (calc(infinity * 1px)) are static utilities with no variable.
--shadow-* and friends#
| Variable | Default |
|---|---|
--shadow-2xs | 0 1px rgb(0 0 0 / 0.05) |
--shadow-xs | 0 1px 2px 0 rgb(0 0 0 / 0.05) |
--shadow-sm | 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) |
--shadow-md | 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1) |
--shadow-lg | 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1) |
--shadow-xl | 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1) |
--shadow-2xl | 0 25px 50px -12px rgb(0 0 0 / 0.25) |
| Variable | Default |
|---|---|
--inset-shadow-2xs | inset 0 1px rgb(0 0 0 / 0.05) |
--inset-shadow-xs | inset 0 1px 1px rgb(0 0 0 / 0.05) |
--inset-shadow-sm | inset 0 2px 4px rgb(0 0 0 / 0.05) |
| Variable | Default |
|---|---|
--drop-shadow-xs | 0 1px 1px rgb(0 0 0 / 0.05) |
--drop-shadow-sm | 0 1px 2px rgb(0 0 0 / 0.15) |
--drop-shadow-md | 0 3px 3px rgb(0 0 0 / 0.12) |
--drop-shadow-lg | 0 4px 4px rgb(0 0 0 / 0.15) |
--drop-shadow-xl | 0 9px 7px rgb(0 0 0 / 0.1) |
--drop-shadow-2xl | 0 25px 25px rgb(0 0 0 / 0.15) |
| Variable v4.1 | Default |
|---|---|
--text-shadow-2xs | 0px 1px 0px rgb(0 0 0 / 0.15) |
--text-shadow-xs | 0px 1px 1px rgb(0 0 0 / 0.2) |
--text-shadow-sm | 0px 1px 0px rgb(0 0 0 / 0.075), 0px 1px 1px rgb(0 0 0 / 0.075), 0px 2px 2px rgb(0 0 0 / 0.075) |
--text-shadow-md | 0px 1px 1px rgb(0 0 0 / 0.1), 0px 1px 2px rgb(0 0 0 / 0.1), 0px 2px 4px rgb(0 0 0 / 0.1) |
--text-shadow-lg | 0px 1px 2px rgb(0 0 0 / 0.1), 0px 3px 2px rgb(0 0 0 / 0.1), 0px 4px 8px rgb(0 0 0 / 0.1) |
shadow-none, inset-shadow-none and text-shadow-none are static resets.
--blur-*#
| Variable | Default |
|---|---|
--blur-xs | 4px |
--blur-sm | 8px |
--blur-md | 12px |
--blur-lg | 16px |
--blur-xl | 24px |
--blur-2xl | 40px |
--blur-3xl | 64px |
One entry generates two utilities: blur-* and backdrop-blur-*.
--perspective-*#
| Variable | Default |
|---|---|
--perspective-dramatic | 100px |
--perspective-near | 300px |
--perspective-normal | 500px |
--perspective-midrange | 800px |
--perspective-distant | 1200px |
Smaller values sit the camera closer and exaggerate the effect. perspective-none is static.
--aspect-*#
| Variable | Default |
|---|---|
--aspect-video | 16 / 9 |
aspect-square (1 / 1) and aspect-auto are static, and any ratio works inline: aspect-3/2, aspect-21/9.
--ease-*#
| Variable | Default |
|---|---|
--ease-in | cubic-bezier(0.4, 0, 1, 1) |
--ease-out | cubic-bezier(0, 0, 0.2, 1) |
--ease-in-out | cubic-bezier(0.4, 0, 0.2, 1) |
ease-linear and ease-initial are static utilities.
--animate-*#
| Variable | Default |
|---|---|
--animate-spin | spin 1s linear infinite |
--animate-ping | ping 1s cubic-bezier(0, 0, 0.2, 1) infinite |
--animate-pulse | pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite |
--animate-bounce | bounce 1s infinite |
Define keyframes inside @theme so they travel with the token:
@theme {
--animate-wiggle: wiggle 1s ease-in-out infinite;
@keyframes wiggle {
0%, 100% { transform: rotate(-3deg); }
50% { transform: rotate(3deg); }
}
}--tab-size-* and --zoom-*#
v4.3 Both namespaces ship empty. tab-<number> and zoom-<number> work out of the box from bare values; the namespaces exist so you can name the ones you use repeatedly.
@theme {
--tab-size-github: 8;
--zoom-compact: 0.85;
}<pre class="tab-github">…</pre>
<div class="zoom-compact">…</div>Default variables#
These are not namespaces. They set base values the preflight and a few utilities read.
| Variable | Default |
|---|---|
--default-transition-duration | 150ms |
--default-transition-timing-function | cubic-bezier(0.4, 0, 0.2, 1) |
--default-font-family | --theme(--font-sans, initial) |
--default-font-feature-settings | --theme(--font-sans--font-feature-settings, initial) |
--default-font-variation-settings | --theme(--font-sans--font-variation-settings, initial) |
--default-mono-font-family | --theme(--font-mono, initial) |
--default-mono-font-feature-settings | --theme(--font-mono--font-feature-settings, initial) |
--default-mono-font-variation-settings | --theme(--font-mono--font-variation-settings, initial) |
Change --default-transition-duration to retune every bare transition in the project at once.
Extending and overriding#
Add a token#
@import "tailwindcss";
@theme {
--font-poppins: Poppins, sans-serif;
}New utility, defaults untouched.
Override one default#
@theme {
--breakpoint-sm: 30rem;
}Remove one default#
@theme {
--breakpoint-2xl: initial;
}2xl: and max-2xl: stop existing.
Replace an entire namespace#
@theme {
--color-*: initial;
--color-white: #fff;
--color-purple: #3f3cbb;
--color-midnight: #121063;
--color-tahiti: #3ab7bf;
--color-bermuda: #78dcca;
}bg-red-500 no longer compiles. This is the cleanest way to enforce a closed palette — a typo becomes a build-visible missing class rather than a plausible-looking wrong colour.
Start from nothing#
@theme {
--*: initial;
--spacing: 4px;
--font-body: Inter, sans-serif;
--color-lagoon: oklch(0.72 0.11 221.19);
--color-coral: oklch(0.74 0.17 40.24);
}@theme inline#
By default a utility references the variable: .font-sans { font-family: var(--font-sans) }. If --font-sans itself holds var(--font-inter), that indirection is captured where @theme was declared, so a value set later on a wrapper element never reaches it.
inline resolves the reference at build time and emits the inner variable directly:
@theme inline {
--font-sans: var(--font-inter);
}.font-sans {
font-family: var(--font-inter);
}Use it for any token whose value is a runtime variable — Next.js font loading and semantic colour aliases that flip between light and dark are the two common cases.
@theme static#
Tailwind only emits the CSS variables that some utility actually uses. static emits every variable in the block regardless:
@theme static {
--color-chart-1: oklch(0.7 0.15 200);
--color-chart-2: oklch(0.7 0.15 30);
}Reach for it when JavaScript reads a token no class references — chart palettes, canvas rendering, animation libraries.
Referencing tokens outside utilities#
In custom CSS#
@layer components {
.prose-callout {
border-left: 3px solid var(--color-brand-500);
padding-inline: --spacing(4);
font-size: var(--text-sm);
color: --alpha(var(--color-gray-950) / 10%);
}
}--spacing(4) expands to calc(var(--spacing) * 4); --alpha() compiles to a color-mix().
In arbitrary values#
<div class="rounded-[calc(var(--radius-xl)-1px)]">…</div>With the shorthand syntax#
<div class="bg-(--color-brand-500) p-(--gutter)">…</div>bg-(--x) is exactly bg-[var(--x)].
In inline styles#
<div style="background-color: var(--color-brand-500)">…</div>In JavaScript#
const styles = getComputedStyle(document.documentElement)
const shadow = styles.getPropertyValue('--shadow-xl')<motion.div animate={{ backgroundColor: 'var(--color-brand-500)' }} />Passing the var() string is preferable to reading the computed value — it keeps the animation live across a theme change.
Common mistakes#
- Declaring tokens in
:rootinstead of@theme. A plain custom property produces a variable but no utility. - Inventing a namespace.
--brand-color,--size-*and--space-*generate nothing. Only the namespaces in the table above are recognised. - Putting
@themebefore@import "tailwindcss". The import has to come first. - Forgetting
inlinefor runtime variables. The classic symptom is a webfont that never applies. - Expecting
getPropertyValueto find an unused token. Wrap it in@theme static, or reference it from at least one class. - Using
theme()in new code.theme(spacing.12)still works but is deprecated — usevar(--spacing)and--spacing().
Related topics#
- Theme & Design Tokens — the same system with worked examples
- Colors — the palette in practice
- Custom Utilities — turning namespaces into new utilities with
@utility - Functions & Directives —
--alpha(),--spacing(),--theme(),@config - Cheat Sheet — the utilities these tokens generate