HSL is the colour model most designers learned first. Three values: hue, saturation, lightness. The lightness part doesn't work the way you'd expect. A yellow at 50% lightness looks far brighter than a blue at 50% lightness — same number, different result.
Build a scale in HSL, try to pair a step-400 blue with a step-400 yellow, and something feels off even though the values match. You end up fixing it by eye, every time.
OKLCH's L axis is calibrated to human vision, not math. L 0.58 in green and L 0.58 in purple will look the same brightness to your eye. Actually the same, not approximately.
If your rule is "interactive elements use step 500", every colour in your palette at step 500 carries the same visual weight. That's not a guarantee you can make in HSL.
0 is black, 1 is white. Each increment feels like an equal jump in brightness because it is one. Step 50 sits at L ≈ 0.995, step 900 at L ≈ 0.10.
How vivid the colour is. 0 is grey. Unlike HSL saturation, chroma is absolute — C 0.18 means the same vividness at any hue or lightness.
The angle around the colour wheel, 0°–360°. Equal angular steps here map to equal perceptual steps. Complementary colours actually look complementary.
Set H, C, and L with the sliders, or type a hex. When you type hex, the tool converts it to OKLCH, pulls out the hue and chroma, then builds a 10-step scale by walking L from near-white (0.995) down to near-black (0.10). Chroma tapers near the extremes — without that, step 50 would look like a vivid tint rather than near-white.
Secondary colours rotate H by a fixed angle (30°, 120°, or 180°) while keeping C and the same L stops. Status colours use fixed canonical hues that shift slightly when your primary is close — so danger always reads as danger.
Any two colours at the same step number will be genuinely comparable in brightness. That's the thing you can't reliably do in HSL.
oklch() works in Chrome 111+, Firefox 113+, Safari 15.4+. The CSS export includes hex fallbacks on every line for anything older. Tailwind and Figma exports use hex throughout.
Most designers think of their brand colour as "the primary colour." In the 60-30-10 rule, it's actually the 10%.
The rule splits screen space into three buckets. 60% is the dominant colour — backgrounds, surfaces, cards. 30% is structure — text, navigation, borders, secondary UI. 10% is the accent — buttons, links, active states, anything that needs to catch the eye. That 10% is where your brand colour lives.
The accent works because it's rare. If your brand colour is everywhere, nothing stands out. The 10% is what gives a button its weight — the eye notices it because almost nothing else on screen is that colour.
A mostly neutral interface with one vivid colour tends to feel more deliberate than a colourful one. Less is doing more.
Backgrounds, surfaces, cards, dividers. Steps 50–200 in light mode. Steps 700–900 in dark mode. Most of what the user sees.
Steps 400–700 of your primary scale. Text hierarchy, navigation, structural elements. Enough presence to carry content without competing with the accent.
Steps 500–600. Buttons, links, active states, focus rings. Use these sparingly — that's what makes them work.
For most apps, you don't need it. The three buckets above cover the vast majority of UI decisions.
A second brand hue makes sense in specific situations: charts with multiple data series, multi-product platforms where each product has its own colour identity, or marketing surfaces where expression matters more than functional clarity. If none of those apply to what you're building, start without it. Add it when you have a real reason, not just because the option is there.
Choose a format. All exports include every scale — primary, secondary, neutral, and status colours.
--color-background, --color-text, --color-brand, status pairs, etc. Ready to use directly in a UI without referencing raw scale steps.