Themes & multiple brands

Build light/dark and multi-brand themes with tokens, CSS variables and live reconfiguration.

1 min readv1

There are three ways to theme, from most static to most dynamic.

1. Tokens + dark:#

Fine for a single brand with a dark mode. Pairs live side by side in recipes:

TypeScript
components: {
  surface: "bgc=$color.surface; cl=$color.ink; dark:bgc=$color.surface-dark; dark:cl=#e2e8f0",
}

2. Tokens pointing at CSS variables#

One set of markup, any number of themes — switch by changing variables:

TypeScript
tokens: {
  color: {
    brand: "var(--brand)",
    surface: "var(--surface)",
    ink: "var(--ink)",
  },
},
CSS
:root            { --brand: #4F52D9; --surface: #fff;    --ink: #0f172a; }
.dark            { --brand: #8A8DF0; --surface: #0b0f19; --ink: #e2e8f0; }
[data-brand=acme] { --brand: #e11d48; }
HTML
<div nib="--brand=#e11d48; d=flex; g=8px">
  <button nib="p=8px 14px; bdrd=8px; bd=none; cl=#fff; bgc=var(--brand)">Acme brand</button>
  <button nib="--brand=#16a34a; p=8px 14px; bdrd=8px; bd=none; cl=#fff; bgc=var(--brand)">Green brand</button>
</div>

No dark: rules are needed any more — the variables change instead.

3. Live reconfiguration#

The runtime can swap the whole config and re-style every element — for theme editors and white-label previews:

TypeScript
runtime.configure({
  ...config,
  tokens: { ...config.tokens, color: { ...config.tokens.color, brand: picker.value } },
});

Next steps#