A brand colour in, a semantic theme out
Primitive ramps first, then roles that reference them — with light and dark generated from the same primitives rather than one inverted into the other.
A brand colour in, a semantic theme out
Primitive ramps first, then roles that reference them. Light and dark generated from the same primitives with different mappings — not one inverted into the other.
The component gallery
Every role in use at once, in the mode you selected. The role table underneath carries the same information without needing to look at it.
Validation
Every role
Export
How the theme is built
Primitives first. Brand, neutral and status ramps are generated in OKLCH. Nothing in the theme is a loose colour; every role points at a step.
Roles are chosen by measurement. Rather than fixing text at 900 and primary at 600 and hoping, the ramp is walked from the end nearest the canvas until a step actually reaches the target. That is why an unusual brand colour still produces a theme that passes rather than one that looks right for blue.
Hover and active are OKLCH lightness deltas, not RGB darkenings. A 10% RGB darken is invisible on a light colour and turns a dark one into a different colour. These step along the ramp until the lightness has moved far enough to be seen, and the validation says so when the ramp runs out.
Dark is not an inversion. It uses the same primitives with different semantic mappings: the canvas is a dark neutral rather than a flipped light one, and the primary moves lighter rather than darker. Inverting a light theme produces glaring surfaces and text that is too bright.
A preset is a parameter bundle — chroma range, neutral warmth, radius, shadow softness, density — not a branded clone of anything. What each one changes is listed above the gallery.
Semantic roles are the point
A theme made of --blue-600 everywhere is a palette with extra steps. What makes it a theme is that components reference --primary, and changing the brand colour moves everything coherently because the roles are references rather than copies.
That is also what makes a dark mode possible without editing every component: the roles keep their names and point at different steps.
Why dark mode is not an inversion
Inverting a light theme gives you white surfaces turned near-black and dark text turned near-white, and both are wrong. Pure white text on pure black is uncomfortable to read for long, and an inverted shadow becomes a glow.
Real dark themes use a slightly elevated surface above a darker canvas, reduce text to an off-white, and lighten the primary rather than darkening it. That is what the mapping here does.
What the validation cannot tell you
It checks contrast, state distinction, gamut and whether every role resolved. It cannot tell you whether the theme suits the product, whether the focus ring is visible against every surface it will actually land on, or whether the whole thing is usable — those need a person.