Skip to content

Brand colors & fonts

Every theme is built on a small set of design tokens defined in src/styles/global.css. Change one variable, the whole theme reskins.

src/styles/global.css (excerpt)
@theme {
/* Surfaces (tinted toward brand hue) */
--color-bg: oklch(99.5% 0.003 263);
--color-surface: oklch(98% 0.005 263);
--color-elevated: oklch(96% 0.007 263);
--color-line: oklch(92% 0.01 263);
--color-line-soft: oklch(95% 0.008 263);
/* Text */
--color-muted: oklch(62% 0.02 263);
--color-soft: oklch(45% 0.025 263);
--color-text: oklch(22% 0.02 263);
--color-strong: oklch(15% 0.015 263);
/* Brand */
--color-brand: oklch(58% 0.234 263);
--color-brand-hover: oklch(52% 0.225 263);
--color-brand-active: oklch(46% 0.215 263);
--color-brand-soft: oklch(95% 0.04 263);
--color-brand-softer: oklch(97% 0.025 263);
--color-brand-fg: oklch(99% 0 0);
/* Type */
--font-sans: "Geist Variable", ui-sans-serif, system-ui, -apple-system, sans-serif;
--font-mono: "Geist Mono Variable", ui-monospace, "SF Mono", Menlo, monospace;
--font-display: "Geist Variable", ui-sans-serif, system-ui, sans-serif;
}

These are CSS custom properties registered with Tailwind v4 via @theme. Tailwind generates utilities like bg-brand, text-strong, border-line-soft automatically — change the variable, every utility that references it updates.

The third value in oklch(L C H) is the hue (in degrees, 0–360). Change just that:

HueColoroklch(58% 0.234 <h>)
263Default bluethe shipped value
25Redwarm red
145Greenemerald
200Tealaqua
290Violetrich purple
340Pinkhot magenta
50Orangewarm orange

Update every --color-brand-* and every --color-line* / --color-bg* / --color-surface* to the same hue — that’s what gives the theme its tinted neutral feel.

:root {
--color-brand: oklch(58% 0.234 290); /* violet */
--color-brand-hover: oklch(52% 0.225 290);
--color-brand-active: oklch(46% 0.215 290);
--color-brand-soft: oklch(95% 0.04 290);
--color-brand-softer: oklch(97% 0.025 290);
/* And the surfaces, all sharing the same hue: */
--color-bg: oklch(99.5% 0.003 290);
--color-surface: oklch(98% 0.005 290);
--color-elevated: oklch(96% 0.007 290);
--color-line: oklch(92% 0.01 290);
--color-line-soft: oklch(95% 0.008 290);
/* …text colors too if you want a perfectly tinted neutral */
}

Every theme uses three font families:

TokenDefaultUse
--font-sansGeist VariableBody text, UI
--font-displayGeist VariableHeadings (usually same as sans)
--font-monoGeist Mono VariableCode blocks, KBD, anything monospaced
  1. Pick a font on fontsource.org — they bundle variable web fonts as npm packages.

  2. Install it: npm install @fontsource-variable/inter

  3. Update the import at the top of global.css:

    @import "tailwindcss";
    @import "@fontsource-variable/inter";
    @import "@fontsource-variable/geist-mono";
  4. Update the variable:

    --font-sans: "Inter Variable", ui-sans-serif, system-ui, sans-serif;
    --font-display: "Inter Variable", ui-sans-serif, system-ui, sans-serif;

That’s it. Every component already references --font-sans and --font-display — they’ll all pick up the new font on the next save.

Common pattern — serif headings, sans body:

@import "@fontsource-variable/playfair-display";
@import "@fontsource-variable/inter";
:root {
--font-sans: "Inter Variable", system-ui, sans-serif;
--font-display: "Playfair Display Variable", Georgia, serif;
}

Some themes (like Forge) ship sharp border-radius: 0 corners; others (like Aurora) use 8px. There’s no --radius token by default — each component sets its own — but if you find yourself adjusting often, define one:

@theme {
--radius-sm: 6px;
--radius: 10px;
--radius-lg: 16px;
--radius-xl: 20px;
}

Then in components, swap style="border-radius: 8px" for the Tailwind utility rounded-[var(--radius)]. All your radii sync to one variable.

Animations across the theme reference four easing variables:

--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);

Most components use --ease-out-quint for hover and entrance animations. Change it once and every transition picks it up.

For the most dramatic change, swap the brand hue and the body font at the same time:

:root {
/* From blue + Geist to warm orange + Inter */
--color-brand: oklch(58% 0.234 50);
--font-sans: "Inter Variable", system-ui, sans-serif;
--font-display: "Inter Variable", system-ui, sans-serif;
}

…and update the four --color-brand-* variants + --color-bg/--color-surface/--color-line hue values to 50. Save, refresh — the entire theme reads as a different brand.

  • Section data — change every section’s copy, lists, and CTAs.
  • Pages — add a new page using the same brand tokens.