In v4 the palette lives in CSS: one --color-* variable per color

Tailwind v4 @theme colors from a palette

In Tailwind CSS v4, colors are defined in CSS: every --color-* variable inside @theme becomes a color for the utilities, so --color-primary: #00ac84; gives bg-primary, text-primary and border-primary. Recolor (recolor.page) exports a palette as such a block. Tailwind's own default colors are written in OKLCH, and --color-*: initial removes them all.

Last updated

From the Tailwind CSS docs (theme variables, colors) and the theme file of Tailwind 4.3.3, checked October 8, 2026
You writeInsideYou get
--color-primary: #00ac84;@themebg-primary, text-primary, fill-primary and the rest
--color-1: #00ac84;@themebg-1, text-1
--color-mint-500: oklch(0.72 0.11 178);@themebg-mint-500 (the example in Tailwind's docs)
--color-*: initial;@themeNo default colors, only yours
--color-surface: var(--surface);@theme inlineA utility that follows a variable you redefine, for dark mode

A palette as an @theme block

Tailwind v4 reads its design tokens from CSS. Any variable in the --color-* namespace inside @theme becomes a color for every color utility, and stays available as a normal CSS variable too. This is the block Recolor exports for the Evergreen palette:

CSS
@theme {
  --color-1: #00ac84;
  --color-2: #1d6a59;
  --color-3: #031f17;
  --color-4: #88d8c0;
  --color-5: #c6ede1;
  --color-primary: #00ac84;
  --color-secondary: #1d6a59;
  --color-accent: #031f17;
}

Put it after @import "tailwindcss"; in your main stylesheet. The numbered names follow the palette's order; the three role names give bg-primary, text-secondary and ring-accent. They hold the hex value itself, so the block needs no @theme inline. Export formats shows the other formats of the same palette.

Naming: roles or scales

Tailwind's default colors are scales, red-50 to red-950, and many design systems copy that. A palette from a generator is usually a handful of roles instead: a primary, a secondary, an accent, a dark and a light. Both work in @theme; pick one per project. Roles keep templates honest when the palette changes, since bg-primary stays the brand however the brand looks. If you need shades of a role, color-mix() can derive them in CSS, as the CSS variables guide shows.

OKLCH in the theme

Tailwind 4.3.3 writes 286 of its default colors in OKLCH, for example:

CSS
--color-red-500: oklch(63.7% 0.237 25.331);
--color-blue-500: oklch(62.3% 0.214 259.815);

Black and white are the exceptions, written as hex. Your own colors can be hex, rgb() or oklch(); Tailwind does not convert them. OKLCH is worth it when you adjust a palette by hand, because its lightness number means what it says; What OKLCH is explains why.

Removing the default colors

Defining your own colors adds them next to Tailwind's. To leave only yours, reset the namespace first:

CSS
@theme {
  --color-*: initial;
  --color-primary: #00ac84;
}

Every default color utility, bg-red-500 and text-white included, then disappears, so add white and black back if you use them.

Dark mode with @theme inline

Theme variables have to sit at the top level, not inside a selector or a media query. For colors that change with a theme, point the theme variable at an ordinary variable with @theme inline, and redefine that one per theme:

CSS
@theme inline {
  --color-surface: var(--surface);
  --color-text: var(--text);
}

:root {
  --surface: #ffffff;
  --text: #031f17;
}

.dark {
  --surface: #031f17;
  --text: #c6ede1;
}

bg-surface then follows whichever value applies. Recolor itself shows a site in the theme it has, so to see a palette on a dark version of your site, load that version of the page; Loading a page covers the options.

Common mistakes when moving colors to v4

  • Defining colors outside @theme. A variable on :root is just a variable; only --color-* inside @theme creates utilities.
  • Nesting @theme in a selector or a media query. Theme variables must be top-level; use @theme inline with a plain variable for anything that changes by theme.
  • Keeping the v3 config. A colors object in tailwind.config.js is the old way; in a v4 project with a CSS entry point, the @theme block is where colors go.
  • Forgetting the defaults are still there. Your colors are added next to Tailwind's until you reset the namespace with --color-*: initial, which also removes white and black.
  • Naming a color after its hue. --color-teal breaks the day the brand is no longer teal; a role such as --color-primary survives the next palette, which is why Recolor's export adds role names.

Common questions

Do I still need tailwind.config.js for colors in Tailwind v4?

No. In v4 colors are --color-* variables in an @theme block in your CSS.

Can I use hex colors in @theme instead of OKLCH?

Yes. Any CSS color works; Tailwind's own defaults happen to be OKLCH, apart from black and white.

Does --color-*: initial also remove white and black?

Yes. It removes every color in the namespace, --color-white and --color-black included.

Can I use opacity modifiers such as bg-primary/50 with my own colors?

Yes. The /50 modifier sets the alpha of any theme color, your own included.

Can theme variables be defined inside .dark or a media query?

No. Theme variables must be top-level; use @theme inline pointing at a variable that you redefine under .dark.

Can Recolor export a palette as a Tailwind v4 block?

Yes. Its Export panel has a Tailwind tab that writes the @theme block shown above for whatever palette is on screen.

Sources