StorybookGitHub

Theming

Apply your brand accents after installing the design system styles, including multi-brand palettes.

Installed components read semantic CSS tokens such as primary, ring, and sidebar-primary. After you install styles, you brand the product by changing those tokens — not by editing component source.

Theme files

globals.css pulls in the default tokens and optional accent palettes:

@import "./theme.css";
@import "./theme-secondary.css";
@import "./theme-tertiary.css";
FileWhen it applies
theme.cssDefault palette on :root and .dark
theme-secondary.cssWhen data-theme="theme-secondary"
theme-tertiary.cssWhen data-theme="theme-tertiary"

With no data-theme attribute, the app stays on the default tokens in theme.css. Accent files only override the brand-facing tokens (--primary, --ring, --sidebar-primary, --selection, and --chart-*). Surfaces such as --background and --foreground stay on the default palette.

Light and dark mode still use the .dark class. Each accent file includes a .dark[data-theme="…"] block so the brand color can shift in dark mode.

Use a shipped accent

Set data-theme on the root element in your layout so the palette is present on first paint:

<html lang="en" data-theme="theme-secondary">

theme-secondary is amber and theme-tertiary is violet. To let users switch palettes, install Theme Toggle.

Use your own brand color

Pick one of these approaches.

Edit the default tokens

For a single-brand app that does not need named palettes, change the accent tokens in src/styles/theme.css under :root and .dark:

:root {
  --primary: oklch(0.55 0.19 250);
  --primary-foreground: oklch(0.985 0 0);
  --ring: oklch(0.55 0.19 250);
  --sidebar-primary: oklch(0.55 0.19 250);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-ring: oklch(0.55 0.19 250);
  --selection: oklch(0.55 0.19 250);
  --selection-foreground: oklch(0.985 0 0);
}

Leave data-theme unset. Buttons, focus rings, and other primary styles pick up the new values.

Add a named palette

To keep the default palette and add your brand as a selectable accent, create a file next to the shipped themes — for example src/styles/theme-brand.css:

[data-theme="theme-brand"] {
  --primary: oklch(0.55 0.19 250);
  --primary-foreground: oklch(0.985 0 0);
  --ring: oklch(0.55 0.19 250);
  --sidebar-primary: oklch(0.55 0.19 250);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-ring: oklch(0.55 0.19 250);
  --selection: oklch(0.55 0.19 250);
  --selection-foreground: oklch(0.985 0 0);
  --chart-1: oklch(0.55 0.19 250);
  --chart-2: oklch(0.6 0.16 220);
  --chart-3: oklch(0.65 0.12 190);
  --chart-4: oklch(0.5 0.2 270);
  --chart-5: oklch(0.7 0.1 230);
}

.dark[data-theme="theme-brand"] {
  --primary: oklch(0.72 0.16 248);
  --primary-foreground: oklch(0.145 0 0);
  --ring: oklch(0.72 0.16 248);
  --sidebar-primary: oklch(0.72 0.16 248);
  --sidebar-primary-foreground: oklch(0.145 0 0);
  --sidebar-ring: oklch(0.72 0.16 248);
  --selection: oklch(0.72 0.16 248);
  --selection-foreground: oklch(0.145 0 0);
  --chart-1: oklch(0.72 0.16 248);
  --chart-2: oklch(0.75 0.14 218);
  --chart-3: oklch(0.78 0.11 188);
  --chart-4: oklch(0.65 0.18 268);
  --chart-5: oklch(0.82 0.09 228);
}

Import it from globals.css with the other theme files:

@import "./theme.css";
@import "./theme-secondary.css";
@import "./theme-tertiary.css";
@import "./theme-brand.css";

Then set the matching attribute:

<html lang="en" data-theme="theme-brand">

You can also replace the values in theme-secondary.css or theme-tertiary.css instead of adding a new file. The selector name (theme-secondary) and the data-theme value must match.

Multi-brand teams

Named accent palettes are for teams that ship more than one product, partner, or white-label from the same component set. Each brand is a CSS file that only overrides accent tokens; buttons, focus rings, and charts restyle without forking components or duplicating layouts.

That is useful when:

  • Several products share density, type, and surfaces (--background, --foreground, --muted) but need distinct --primary colors.
  • A platform app switches brand per tenant, environment, or route by setting data-theme on <html> at render time.
  • Designers and engineers preview every brand on one docs or Storybook surface, using Theme Toggle or a data-theme attribute on a subtree. Because the selectors are [data-theme="…"] rather than :root only, a nested region can use a different accent than the rest of the page.

Keep one palette file per brand (theme-acme.css, theme-partner.css) and import them all from globals.css. The shared component library stays the source of truth; brand teams own only their token file.

Accent tokens

Override this set when you brand an accent. Copy the same keys into both the light and .dark blocks.

TokenTypical use
--primaryButtons, key actions, brand fills
--primary-foregroundText and icons on --primary
--ringFocus rings
--sidebar-primarySidebar brand and active states
--sidebar-primary-foregroundText on --sidebar-primary
--sidebar-ringSidebar focus rings
--selectionText selection background
--selection-foregroundText selection foreground
--chart-1--chart-5Chart series colors