Tailwind CSS Intermediate: Theme, Variants and Reuse
Customising the Theme
In this lesson you will
- Add custom colours, fonts and breakpoints with @theme
- Explain how theme variables create utilities and when they are emitted
- Replace or extend the default palette
Introduction
Out of the box Tailwind looks like Tailwind. To look like your brand, you customise the theme. In Tailwind v4 this is done in CSS, in the same file where you import Tailwind. There is no JavaScript configuration file by default. Everything below was compiled and checked with Tailwind 4.3.3.
Theme variables
A theme variable is a CSS custom property with a special prefix, defined inside @theme. The prefix decides which utilities it creates.
CSS
@import "tailwindcss";
@theme {
--color-brand: #5b4bf5;
--font-display: "Plus Jakarta Sans", sans-serif;
--breakpoint-3xl: 120rem;
}Now these classes exist:
bg-brand,text-brand,border-brandandhover:bg-brand/20(opacity works too)font-display3xl:flex(a breakpoint variant that starts at 120rem)
When I compiled bg-brand, the result was background-color: var(--color-brand), and --color-brand: #5b4bf5 was added to the output. For 3xl:flex, the output contained @media (width >= 120rem) and the class name is escaped because it starts with a digit.
Namespaces
The prefix of the variable name controls what it generates:
| Variable prefix | Creates utilities for |
|---|---|
--color-* | bg-*, text-*, border-*, fill-*, ring and more |
--font-* | font-* (font families) |
--text-* | text-* (font sizes) |
--font-weight-* | font-* weights |
--breakpoint-* | responsive variants like 3xl: |
--spacing | the base for all spacing and sizing utilities |
--radius-* | rounded-* |
--shadow-* | shadow-* |
--ease-* | ease-* |
--animate-* | animate-* |
--container-* | max-w-* sizes and container query variants |
Quick quiz
What does defining --color-brand inside @theme create?
Animations and easing
You can define keyframes inside @theme too:
CSS
@theme {
--ease-snappy: cubic-bezier(0.2, 0, 0, 1);
--animate-wiggle: wiggle 1s ease-in-out infinite;
@keyframes wiggle {
0%, 100% { transform: rotate(-3deg); }
50% { transform: rotate(3deg); }
}
}This gives you ease-snappy and animate-wiggle. In the compiled output, animate-wiggle became animation: var(--animate-wiggle) and the @keyframes wiggle rule was included.
Changing the base spacing
--spacing is the base unit. The default is 0.25rem. Changing it rescales every spacing utility:
CSS
@theme {
--spacing: 0.3rem;
}Now p-4 is 1.2rem. Do this only when you really want a different rhythm.
Replacing the palette
By default all the standard colours exist. To remove them and use only your own, reset the namespace with initial:
CSS
@theme {
--color-*: initial;
--color-brand: #5b4bf5;
--color-ink: #0b1020;
--color-paper: #ffffff;
}I checked that after this reset bg-brand still generated and bg-blue-500 generated nothing. This keeps a design system strict, so developers cannot use off-brand colours by accident. You can also reset only part of the theme, such as --font-*: initial.
Only used variables are emitted
Tailwind v4 includes a theme variable in the CSS output only if a utility uses it. I defined --color-brand and --color-unused, used only bg-brand, and the output contained --color-brand but not --color-unused. This keeps CSS small.
If you want a variable always available (for use in your own CSS or JavaScript), use @theme static:
CSS
@theme static {
--color-brand: #5b4bf5;
}With static, I confirmed unused variables were emitted.
Referencing other variables with inline
If a theme value points to another variable, use @theme inline so the utility uses the final value:
CSS
:root {
--brand: #5b4bf5;
}
@theme inline {
--color-primary: var(--brand);
}I compiled bg-primary this way and got background-color: var(--brand), referencing your variable directly. Without inline, it would reference var(--color-primary), which can break when the variable is overridden in a different scope such as a dark theme. This project's own styles use @theme inline.
Using theme values in your CSS
All theme variables are normal CSS variables, so you can use them anywhere:
CSS
.hero {
color: var(--color-brand);
margin: --spacing(4);
}The --spacing(4) function is replaced at build time with calc(var(--spacing) * 4). (I checked it produces exactly that.) In older Tailwind versions you would use theme(), which is now discouraged in favour of CSS variables.
Try it yourself
Create a theme with --color-brand, --font-display and a custom --breakpoint-3xl. Build a heading with font-display text-brand 3xl:text-6xl. Then reset the colour namespace and confirm a default colour like bg-blue-500 no longer works.
Summary
- Customise Tailwind with theme variables inside
@theme, and the variable's prefix decides which utilities it creates. - Reset namespaces with
--color-*: initialto build a strict palette, and use@theme staticto always emit variables. - Use
@theme inlinewhen a theme value references another variable, and read theme values with normal CSS variables.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading