Theming
Five independent axes, set as attributes. Change any of them on <html> or on a single section.
The five axes
| Axis | Attribute | Values |
|---|---|---|
| Theme | class | light · dark |
| Material | data-material | solid · glass |
| Accent | data-accent | lime · ocean · ember · rose · graphite (or your own) |
| Density | data-density | comfortable · compact |
| Font | data-font | editorial · grotesk · corporate · system |
Every demo below is the same markup. Only the attribute on the wrapper changes.
Theme
class="light"Q4 plan
class="dark"Q4 plan
Material
Glass makes surfaces translucent with a backdrop blur. At most three blurred surfaces are on screen at once; nested layers stay opaque.
data-material="solid"Q4 plan
data-material="glass"Q4 plan
Accent
One source colour in OKLCH. Soft fills, readable text and the focus ring are derived from it.
data-accent="lime"Q4 plan
data-accent="ocean"Q4 plan
data-accent="ember"Q4 plan
data-accent="rose"Q4 plan
data-accent="graphite"Q4 plan
Density
Changes --radius, --control-h and --gap. Compact suits data-heavy screens.
data-density="comfortable"Q4 plan
data-density="compact"Q4 plan
Font
| Preset | UI | Headings | Numbers |
|---|---|---|---|
| editorial | Onest | Instrument Serif | JetBrains Mono |
| grotesk | Hanken Grotesk | Bricolage Grotesque | Geist Mono |
| corporate | IBM Plex Sans | IBM Plex Sans | IBM Plex Mono |
| system | System UI | System UI | System mono |
Heading weight, tracking, size scale and the style of <em> change with the preset.
data-font="editorial"Q4 plan
data-font="grotesk"Q4 plan
data-font="corporate"Q4 plan
data-font="system"Q4 plan
Token reference
Components read only these semantic tokens. Each is available as a Tailwind colour, e.g. bg-surface, text-fg-muted, border-line.
| Token | Used for |
|---|---|
scene | Page background behind every surface |
fg / fg-muted / fg-subtle | Text: primary, secondary, tertiary |
surface / surface-2 / surface-border | Card surface, nested tile, card border |
line | Hairlines and dividers |
field / field-border / field-border-hover | Inputs and other controls |
hover / selected | Interactive row and item states |
skeleton / track | Loading placeholders, slider and progress tracks |
popover / popover-border / overlay | Floating layers and the dialog backdrop |
nav / nav-fg / nav-fg-strong / nav-hover / nav-active / nav-line / nav-sub | Sidebar (dark in both themes) |
nav-popover / nav-popover-border | Sidebar flyouts |
accent / accent-fg / accent-soft / accent-ink | Accent fill, text on accent, tinted background, readable accent text |
ring | Focus ring |
success / warning / danger / info | Semantic status colours |
chart-1 … chart-6 | Chart series |
Custom accent
Skip the presets and set the four source values. Everything else is derived.
:root {
--accent-base: oklch(0.72 0.16 160); /* fill */
--accent-fg: oklch(0.18 0.02 160); /* text on the fill */
--accent-ink-light: oklch(0.48 0.12 160); /* readable accent text, light theme */
--accent-ink-dark: oklch(0.82 0.13 160); /* readable accent text, dark theme */
}Per-section theming
Axes are inherited, so a section can differ from the page. A dark hero on a light page:
<section className="dark" data-accent="ember">
<h1>Dinner from 18:00</h1>
<Button variant="primary">Book a table</Button>
</section>The one rule
dark:bg-zinc-900 breaks every other axis and every section that sets its own theme.