Color palettes are supposed to be simple. Pick a brand hue, generate some tints and shades, call it done. In practice, most 10-stop scales look terrible: the steps are uneven, the mid-tones are muddy, and the 900 is so dark it reads black on every monitor except the designer's calibrated display. We broke this cycle by switching to OKLCH — and then spent three months figuring out why it wasn't enough on its own.
Why OKLCH?
OKLCH is a perceptually uniform color space. "Perceptually uniform" means that equal numerical steps produce equal-looking differences to a human eye — something neither HSL nor hex can claim. In HSL, moving from 50% to 60% lightness can produce a dramatic shift or barely visible change depending on the hue. In OKLCH, the math and the eye agree.
More importantly, OKLCH separates lightness, chroma, and hue into independent axes. This means we can move along the lightness axis to generate stops without accidentally drifting the hue — a problem notorious in HSL-based generators that produce greenish yellows at high lightness and brownish oranges at low.
Equal steps in the formula should feel equal to the eye. OKLCH gets us there. HSL doesn't even try.— Internal color review, Feb 2026
The anchor point
Every scale in UISqueezy starts from a single anchor: the 500 stop. This is the color the brand team picks — the one that appears in the logo, the hero, the primary button. Everything else is derived. The anchor is never moved by an algorithm; it is a human decision, reviewed by a human, set in the token catalog as a literal hex.
From the anchor, we convert to OKLCH coordinates. For the portica scale, our brand yellow, the anchor sits at approximately L 0.88, C 0.18, H 97. These three numbers become the origin of the entire scale. The hue (H) is locked for all stops. Lightness and chroma are the only moving parts.
Chroma curves
Here is where the subtlety lives. If you hold chroma constant while moving lightness up and down, the pale stops look washed out and the dark stops look desaturated in a way that reads gray. The eye expects chroma to peak somewhere in the mid-range and fall off toward both extremes.
We model this with a quadratic curve: chroma is maximum at the anchor (500), falls to roughly 40% at stop 50, and drops to roughly 30% at stop 900. The exact curve coefficients depend on the hue — warm yellows can hold more chroma at high lightness than cool blues, which clip into P3 gamut territory almost immediately.
- Stop 50: L 0.98, C × 0.40 — near-white, just a breath of hue
- Stop 100: L 0.95, C × 0.55
- Stop 200: L 0.91, C × 0.72
- Stop 300: L 0.88, C × 0.88
- Stop 400: L 0.82, C × 0.96
- Stop 500: L 0.76, C × 1.00 — anchor
- Stop 600: L 0.64, C × 0.92
- Stop 700: L 0.52, C × 0.78
- Stop 800: L 0.38, C × 0.60
- Stop 900: L 0.22, C × 0.32 — near-black with a trace of hue
Lightness steps
Lightness distribution is not linear. A linear distribution produces steps that feel tight at the light end and spread at the dark end, because human vision is more sensitive to lightness differences in the shadows. We use a slight ease-in curve: steps compress toward 50–100 range and open up toward 700–900.
This sounds like a lot of manual tuning. In UISqueezy it isn't — the curve coefficients are stored as token metadata on the scale root. When a team changes their brand anchor, the entire scale regenerates in under 50ms, running the same curve logic against the new OKLCH coordinates.
Testing in the wild
We validate every generated scale against three checks before it lands in the catalog. First, WCAG contrast ratios: 500 must pass AA against white (4.5:1) and 700 must pass AAA. Second, the visual difference between adjacent stops must score above a threshold in the CIEDE2000 delta. Third, every stop is rendered on a calibrated display and eyeballed by at least one designer who did not write the algorithm.
The last check is not optional. Algorithms can pass every automated test and still produce a scale that a senior designer immediately flags as "something is wrong with the 300." Trust your eye. Build the tools that let your eye be the final authority.