@theme is how every v4 project brings in its own brand, and it replaces most of tailwind.config.js.
In v4 the design system lives in CSS. Theme variables are custom properties declared in an @theme block, and their namespace decides which utilities they create:
--color-brand-500createsbg-brand-500,text-brand-500,border-brand-500and so on,--font-displaycreatesfont-display,--text-*,--font-weight-*,--leading-*,--tracking-*for type,--breakpoint-*for responsive variants,--container-*for container sizes andmax-w-*,--radius-*,--shadow-*,--ease-*,--animate-*, and--spacingfor the whole spacing scale.
Every theme variable is also emitted as a normal CSS variable on :root (when used), so you can read it anywhere: var(--color-brand-500) in custom CSS, inline styles or JavaScript.
To extend, just add variables. To override a default, redeclare it with the same name. To remove a whole namespace, set it to initial (--color-*: initial;) and define only your own colours; --*: initial clears the entire default theme.
Use plain :root variables for values that shouldn't create utilities. Use @theme inline when a theme variable refers to another variable, so utilities use the final value rather than a reference that might resolve differently deeper in the tree.
To share a theme across projects, put the @theme block in its own CSS file and @import it.
Example
/* app.css */
@import "tailwindcss";
@theme {
--font-display: "Satoshi", ui-sans-serif, system-ui, sans-serif;
--color-brand-50: oklch(0.97 0.02 265);
--color-brand-500: oklch(0.60 0.20 265);
--color-brand-900: oklch(0.30 0.12 265);
--breakpoint-3xl: 120rem; /* enables 3xl: */
--radius-card: 1.25rem; /* enables rounded-card */
--ease-snappy: cubic-bezier(0.2, 0, 0, 1); /* enables ease-snappy */
}
/* Usage: <h1 class="font-display text-brand-900">...</h1>
<div class="rounded-card bg-brand-50 3xl:p-12">...</div>
Plain CSS can read the same tokens: */
.chart-line { stroke: var(--color-brand-500); }Remember
- @theme variables generate utilities based on their namespace
- They're also real CSS variables usable anywhere
- Redeclare to override; --color-*: initial removes a namespace
- Use :root for variables that shouldn't create utilities
Watch out for
- Putting brand colours in :root and wondering why bg-brand-500 doesn't exist.