Rebrand in 10 minutes

Take an app from the default palette to your brand by overriding a handful of custom properties.

The default theme is black and white on purpose — a default, not a brand. This walkthrough takes a whole app from there to your own identity by redefining custom properties in one stylesheet. No component is touched, no class is overridden, and the result is live at the bottom of this page.

All overrides go in the same place: your globals.css, after the theme you start from.

globals.css
@import "tailwindcss";
@import "@voila.dev/ui/styles/globals.css";
@import "@voila.dev/ui/styles/themes/default.css";
 
/* your overrides below */

If one of the shipped themes is already close — indigo, iris, sand, emerald, olive — start from that one instead and you will have fewer properties to set.

Step 1: the brand colour

--primary is every default button, progress fill and active state. --ring is the focus ring; set it to the same hue so keyboard focus reads as branded rather than grey. Always set the -foreground partner, it is the text that sits on the surface.

:root {
	--primary: oklch(0.55 0.21 145);
	--primary-foreground: oklch(0.99 0 0);
	--ring: oklch(0.55 0.21 145);
}

Values are OKLch, a perceptual colour space: lightness is the first number, so "the same colour but lighter" is a one-number change rather than a guess. Paste a hex value into oklch.com to convert your existing brand colour.

Step 2: the dark theme

A colour tuned for white backgrounds usually sinks on dark ones. Redefine the pair inside .dark, lighter and with the foreground flipped:

.dark {
	--primary: oklch(0.72 0.18 145);
	--primary-foreground: oklch(0.16 0.01 145);
}

If you skip this step the light values still apply in dark mode; they are merely worse. Two minutes here is the difference between a rebrand and a skin.

Step 3: status colours

Only if your brand has opinions about them. --destructive, --success and --warning are already sensible; most rebrands leave them alone. If yours clashes, for instance a red brand next to the default destructive red, shift the status hue, not the brand.

:root {
	--destructive: oklch(0.58 0.22 15);
	--destructive-foreground: oklch(0.99 0 0);
}

Step 4: radius

--radius is a single number the whole corner scale derives from (--radius-sm through --radius-xl are calculated off it), so one line takes every button, card and input from soft to sharp:

:root {
	--radius: 0.25rem; /* sharp */
}

Step 5: typography

The theme names two families, --font-sans for body copy and --font-heading for display text. Point them at yours and load the fonts however your framework prefers; the tokens only name them.

:root {
	--font-sans: "Your Sans", ui-sans-serif, system-ui, sans-serif;
	--font-heading: "Your Display", var(--font-sans);
}

Step 6: check contrast

The default palette targets WCAG AA. Your overrides are your responsibility: run each -foreground against its surface through a contrast checker (4.5:1 for body text, 3:1 for large text and UI). OKLch makes the fix obvious, since lightness is the first number in the value.

Before and after

The two panels below are the same markup. The second sits inside a wrapper that redefines four properties inline, exactly what the stylesheet above does app-wide:

const rebrand = {
	"--primary": "oklch(0.55 0.21 145)",
	"--primary-foreground": "oklch(0.99 0 0)",
	"--ring": "oklch(0.55 0.21 145)",
	"--radius": "0.25rem",
} as CSSProperties;
 
<div style={rebrand}>{/* every component inside follows */}</div>;

Same markup, four properties apart

Default palette

Create your accountFree plan

Four properties later

Create your accountFree plan

Because custom properties cascade, this wrapper trick is also how you scope a sub-brand to one route, preview a client's colours in a settings page, or white-label per tenant with a single inline style.

Own it

Two files tell the whole story: @voila.dev/ui/src/styles/tokens/colors.css for every role that exists, and @voila.dev/ui/src/styles/themes/default.css for its light and dark values. Copy the theme into your repo if you would rather edit values than override them — the sibling themes are exactly that, a copy that overrides a dozen properties.

Quick start · Theming and tokens