Theme builder
Pick one brand color and this builder derives the rest of the
data-color accent set — the hover shade, the soft tint, and the
text-on-primary color — checks the WCAG contrast for you, and
generates ready-to-use exports.
Two modes:
- Accent — adds a new
[data-color="<name>"]axis (opt-in, sits alongside the built-in five). Just the accent changes. - Full theme — customises the default look: accent plus a
neutral ramp (gray / slate / zinc / neutral / stone, driving
surface / text / border / secondary), the control radius, and
typography (body / heading / mono
font-family), across both light and dark. The output is computed as the diff of the themed build against the stock build, so it’s exactly the variables that changed — nothing more.
It works by running the real token transformer
(@hypermedia-components/core/token-transform) on the real DTCG
sources right in your browser. The preview, the Apply to this page
button, and every export are produced by the same buildTokensCss the
library build uses — so a custom theme behaves exactly like a built-in
one (every affected component variable, subtree-safe), and the
“DTCG sources → generated CSS variables” model is never bypassed.
Semantic status colors (success / warning / error) stay fixed — the accent doesn't touch them.
Scoped to this box and rendered by the real token build. Toggle the site's light/dark switch to check both. Apply to this page themes the whole site.
Additive — keep the stock CSS and load this after it. No build.
Save as color.<name>.tokens.json and register it in build-tokens.mjs (Path B).
The complete token CSS (all built-in themes + yours). Drop-in replacement for the stock token stylesheet.
HTML email templates with this theme baked into inline styles — table layouts, 600px container, dark-mode <style> partial. See the HTML email guidefor usage. Regenerate after changing the theme (each file carries a manifest comment with its settings).
Preview shows the plain flavor with sample copy. Dark-mode overrides follow your OS/browser prefers-color-scheme, not the site's theme toggle.
How the derivation works
Section titled “How the derivation works”Everything is derived in OKLCH, using the same ladder the library’s own ramps are generated from — so a theme built here is put together exactly the way the five built-in accents are.
| Output | Derivation |
|---|---|
action-primary-bg / border | Your chosen brand color, as-is, re-expressed as oklch(). |
action-primary-hover-bg / border | The brand color stepped down in lightness by the Hover darkness amount, holding hue and chroma. The default 7 is the gap the built-in ramps use between 600 and 700. |
action-primary-fg | Auto applies the lightness threshold: white at or below 0.55, otherwise gray.900. Override it manually if you prefer. |
action-primary-soft-bg | color-mix(in oklab, <brand> N%, transparent) — one value that blends correctly on light and dark surfaces. |
focus-ring | Defaults to the brand color. |
The old builder multiplied sRGB channels to darken and ran a contrast search to pick the foreground. Both are gone. Darkening in sRGB dims warm hues faster than cool ones, so the same “12%” produced a different perceived step per brand; a lightness delta does not. And the foreground no longer needs a search, because at a fixed OKLCH lightness the contrast ratio barely moves with hue or chroma — the crossover is a constant, so it is a comparison.
Snap to ladder
Section titled “Snap to ladder”The In OKLCH read-out shows where your brand color sits, and how
far it is from step 600 of the ladder for its hue. Snap to ladder
replaces it with that exact step — the color a built-in axis would use
for the same hue, AA-safe on white text by construction.
Snapping is optional. Brand fidelity usually wins, and any color that clears the contrast read-out is fine. It is there for when you are picking a color rather than matching one — and Shuffle now rolls a random hue through the ladder, so a shuffled theme always clears AA.
The contrast read-out is the WCAG 2.x ratio between the brand
color and the resolved text color. Aim for 4.5 : 1 (AA) or
better; the badge turns red below that. If it fails, the brand color’s
lightness is in the band around 0.62 where neither white nor dark
text clears AA — snapping, or moving the lightness away from that band,
fixes it.
The three exports
Section titled “The three exports”The builder runs buildTokensCss with your color injected as a
synthetic color.<name> source, then hands you the output three ways:
-
Theme CSS block — just this theme’s
[data-color="<name>"]block (every affected component variable, resolved). Additive: keep the stock token CSS and load this after it, then setdata-color="<name>". No build. This is the correct Path A — overriding the seven semantic variables by hand would not work (see How the cascade actually works). -
DTCG token file —
color.<name>.tokens.json. Save it underpackages/core/src/tokens/and register it inbuild-tokens.mjsto ship the theme in the build (Path B). Also feeds external DTCG tooling (Style Dictionary, Figma). -
Full token CSS — the complete
hc.tokens.css(every built-in theme plus yours). A drop-in replacement for the stock token stylesheet when you’d rather swap one file than add a block.
A fourth tab, Email templates, bakes the same resolved tokens into table/inline-style HTML email fragments (Thymeleaf or plain-HTML flavor) with a live preview — see the HTML email guide.
Apply to this page injects the generated block onto the docs
site’s <html> so you can browse every component under your theme;
Reset clears it.
Related
Section titled “Related”- Color themes — the built-in five, the cascade explanation, and the full authoring guide.
- Density — orthogonal sizing axis.
- Theming overview — the four-layer model.