Theming
Theming recipes
Because every token is a CSS variable, most theming problems are a few lines of CSS in the right scope. Here are the patterns that come up most — copy, paste, adjust.
The one idea behind all of these: a token set on an element cascades to every oks-ui component inside it. Put your override on :root and it's global; put it on a wrapper and it's scoped to that subtree. That's the whole mechanism. If you need the conceptual reference first, start with the overview.
1. Scope a theme to one section
Give one region a different accent without a provider or a re-render — set the ramp on a wrapper instead of :root. Everything inside re-tints; everything outside is untouched.
<section className="promo">
{/* Buttons, Chips, links here use the promo accent */}
<Button color="primary">Upgrade</Button>
</section>.promo {
--oks-color-primary-50: #ecfdf5;
--oks-color-primary-100: #d1fae5;
--oks-color-primary-500: #10b981;
--oks-color-primary-600: #059669;
--oks-color-primary-700: #047857;
}style object on the wrapper. This is exactly how the theme switcher on the home page works: it sets --oks-color-primary-* on a preview div, no library re-render.2. White-label / multi-brand
Ship one build, switch brands with a single attribute on <html> (or any ancestor). Each brand is a block of variable declarations; the runtime just toggles data-brand.
[data-brand="acme"] {
--oks-color-primary-500: #2563eb;
--oks-color-primary-600: #1d4ed8;
--oks-color-primary-700: #1e40af;
--oks-radius-md: 8px;
}
[data-brand="globex"] {
--oks-color-primary-500: #db2777;
--oks-color-primary-600: #be185d;
--oks-color-primary-700: #9d174d;
--oks-radius-md: 2px;
}// flip the whole app's brand at runtime
document.documentElement.setAttribute("data-brand", "globex");3. Recolour one component only
When you want a single element off-palette — a neutral-ink primary button, a warning-coloured input — use the component's own hooks instead of a global token. Nothing else changes.
{/* an ink button that ignores the primary role entirely */}
<Button
style={{
"--oks-button-tone": "var(--oks-palette-neutral-900)",
"--oks-button-solid-fg": "#fff",
} as React.CSSProperties}
>
Continue
</Button>
{/* or just reach a different palette by name */}
<Button color="teal" colorDepth={600}>Save</Button>4. Compact density preset
Dashboards and data-dense screens want tighter spacing and sharper corners. Because padding and radius are tokens, a "compact" mode is a scope with smaller values — no per-component prop churn.
.density-compact {
--oks-space-2: 0.3rem;
--oks-space-3: 0.45rem;
--oks-space-4: 0.6rem;
--oks-radius-md: 3px;
--oks-radius-lg: 5px;
}Component-specific sizing hooks stack on top when you need them, e.g. --oks-button-height and --oks-button-padding-x on a toolbar of buttons.
5. High-contrast variant
An accessibility-minded theme deepens text and borders and drops soft tints toward solid. Scope it behind a class or a media query.
@media (prefers-contrast: more) {
:root {
--oks-color-border: var(--oks-palette-neutral-500);
--oks-form-field-border: var(--oks-palette-neutral-600);
--oks-color-primary-700: #1e1b4b; /* darker soft-text */
}
}500 shade. Whatever brand ramp you drop in, make sure that 500 clears 4.5:1 against its label — pick a deeper shade if it doesn't.