Skip to content
UT Studio

How to customise a Tailwind CSS template

Retheming a Tailwind v4 template through its token layer, and the three habits that keep it maintainable.

UT Studio7 min read

Tailwind v4 moved configuration into CSS. If your instinct is still to open `tailwind.config.js`, that instinct is the first thing to update — the file usually is not there, and the palette lives in a `@theme` block instead.

Find the token layer first

Before changing anything, read the theme block end to end. It is normally under a hundred lines and it is the entire design language of the template: colours, spacing scale, radii, fonts, shadows.

app/globals.css
@import "tailwindcss";

@theme {
  --color-canvas: oklch(0.99 0 0);
  --color-surface: oklch(1 0 0);
  --color-foreground: oklch(0.15 0.01 260);
  --color-foreground-muted: oklch(0.45 0.01 260);
  --color-primary: oklch(0.55 0.19 258);
  --radius-panel: 0.75rem;
}

Retheme by editing values, never names

Change what `--color-primary` is. Do not rename it to `--color-brand-orange`. The name is a contract every component depends on; the value is the thing you own.

Why OKLCH is worth keeping

Templates here define colour in OKLCH because lightness in that space matches perceived lightness. Practically: you can build a hover state by moving one number and get a predictable result, and two colours with the same lightness value genuinely look equally bright — which sRGB hex codes do not.

app/globals.css
--color-primary: oklch(0.55 0.19 258);
--color-primary-hover: oklch(0.49 0.19 258);  /* darker: L down */
--color-primary-subtle: oklch(0.96 0.03 258); /* tint: L up, C down */

Handle both themes at once

Define every token in the base, then redefine the ones that change. Two selectors, because an OS preference and an explicit toggle are different things and both have to win in the right order.

app/globals.css
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --color-surface: oklch(0.18 0.01 260); }
}

:root[data-theme="dark"] { --color-surface: oklch(0.18 0.01 260); }

Never let a token exist only in the dark block

It will be undefined in light mode and the component will fall back to whatever it inherits — usually black text on a dark panel, and usually noticed by a customer.

Three habits that keep it maintainable

  1. Semantic names only. `bg-surface`, never `bg-white`. The markup then does not need to know which theme is active.
  2. No `@apply` for shared treatments. If two elements look the same, make a component. `@apply` rebuilds the stylesheet coupling Tailwind exists to remove.
  3. Treat arbitrary values as a smell. One `w-[37ch]` is fine. A file full of them means a token is missing.

Changing the type scale

Fonts are tokens too. Swap the family and, if the new face has a different x-height, adjust the scale rather than nudging sizes in components.

app/globals.css
@theme {
  --font-sans: "Geist Variable", system-ui, sans-serif;
  --text-base: 1rem;
  --text-lg: 1.125rem;
}

Templates where the token layer is the product

Templates mentioned here

Everything above is written against real products. These are the ones this page draws on.

Read next