Why this exists

Anyone who goes looking for a color palette finds two good answers already waiting. Radix Colors hands you twelve steps with a job written on each one — app background, subtle border, solid, low-contrast text — and for a great deal of interface work that is the end of the matter. Tailwind hands you eleven stops a hue, tuned by eye over years, and they are very hard to beat as accents. Both are better at what they set out to do than anything here.

This exists for the two things neither sets out to give you: a step that means a lightness, and a quiet version of a color that arrives with the loud one.

Most color palettes are built at the edge of what a screen can show. Measured across the seventeen chromatic palettes that ship with Tailwind v4, every stop sits between 0.96 and 1.10 of the highest chroma sRGB allows at that stop’s lightness and hue. The bell curve the raw chroma numbers appear to trace is not a designed curve at all — it is the shape of the sRGB gamut.

That is the right choice for an accent. It is the wrong one for most of an interface. Surfaces, cards, borders, dividers, muted text, disabled states: none of them want to be as saturated as the display can manage. Palettes made of nothing but candy colors leave you to invent the quiet nine tenths yourself.

The usual answer is a second, unrelated palette — slate, zinc, stone. Different hue, different lightness steps, no relationship to the brand color. They cannot stand in for one another, because they do not line up.

Radix goes further than that and pairs a tuned gray to every hue, which solves the half of the problem that is about neutrals. It leaves the other half: the quiet version of the color itself. A muted blue that is still blue is something you derive by hand, once per hue, and then maintain.

Componiq Colors pairs them instead. Every step carries three tones at the same lightness: vivid, a soft tone at half the chroma reachable there, and a muted one at under a fifth of it. All three behave identically against text and differ only in intensity, so any of them can take another’s place in a token set. Soft is the one for a surface that should read as coloured and still read as a surface — a tinted panel, a selected row; muted is for the parts that carry a hue only so they do not look grey beside the accent.

The gap that closes is measurable:

vivid + saturation ramp0 tonesdark and quiet
muted ramp alone4 tones#434460 #303145 #1d1e2a #0a0b0f
Dark and quiet means lightness under 35% at under 50% saturation — the background, card, and border a dark interface is built from. A vivid ramp paired with a saturation ramp contains none of them.

What a step means

Ask either system for step 9 and you get a color. Ask it for a lightness and it has no answer, because a step is not one.

Radix puts its ninth step where each hue is most itself — the solid, the color you would point at if asked to name it. That is a real decision and mostly the right one. It is also why iris lands at lightness 54.0% and yellow at 91.8%: thirty-eight points apart, on what is nominally the same step. Amber, lime, sky and yellow never reach lightness 35% at all, so their darkest color is still a mid-tone.

Tailwind is tuned finely enough that its stops read as a ladder, and it has the same property for the same reason. Its 500s run from 58.5% to 79.5%.

Radix Colors9.3 points37.8 at the solid step
Tailwind v410.0 points21.0 at the 500s
this ladder0 pointsa rung is a lightness
How far the same step drifts from hue to hue, in points of OKLCH lightness: the mean across all twelve Radix steps over its twenty-five chromatic scales, and across all eleven Tailwind stops over its seventeen. The third row is not an achievement, it is a definition — a rung here is a lightness before it is a color, so there is nothing left to drift.

The second thing a fixed ladder buys is room at the ends. Radix opens its gaps as it descends, gently at first and then not: the step from 11 to 12, low-contrast text to high-contrast text, is twenty points on its own — the largest gap in the scale, and the reason there is only ever one very dark tone to reach for.

Radix, steps 1 to 51.1 · 2.3 · 2.7 · 3.2each gap wider than the last
Radix, step 10 to 1110.3solid to text, in one move
Radix, step 11 to 1220.3the largest gap in the scale
this ladder, rungs 1 to 52 · 2 · 2 · 3four surfaces at one spacing
Mean gaps in points of OKLCH lightness. The light end is where an interface keeps its surfaces, and four of them two points apart are four you can actually tell apart and still call quiet. Fourteen rungs rather than twelve is what puts three of them below 35% instead of one.

None of this is free. A rung that is a fixed lightness cannot also be the place where a particular hue is most vivid — that is the trade Radix made, and it made it deliberately. So both answers are here rather than one: Componiq-14 holds the ladder and every hue shares it, Peak-on-8 lets each hue swell to its own brightest rung and gives up the shared lightness to get there. The difference is a tab, not a fork.

Chroma is not free

A very light or a very dark color cannot be very saturated. There is no dark, vivid yellow — dark and yellow exclude each other physically, not by convention. So the maximum chroma available depends on both lightness and hue. For orange at hue 60:

lightness 95%0.031
lightness 87%0.086
lightness 79%0.149
lightness 70%0.164widest
lightness 50%0.117
lightness 35%0.082
lightness 15%0.035
An arch, not a slope: toward white and toward black the available chroma falls to nothing, with a maximum in between. Hue moves it again — violet reaches 0.297, cyan only 0.150.

The chroma slider shows this directly. The hatched part of the track is not disabled, it is empty: dragging into it produces no more color, because none exists there. Showing the boundary beforehand beats letting you discover it by wondering why nothing changes.

That boundary belongs to the seed, so it is the same wherever you stand. The three controls on the left say what color you brought; nothing in the algorithm can widen what sRGB holds at that lightness, and so nothing in the algorithm redraws them. Where a ramp wants more color than its seed can carry, the ramp says so — Descending Chroma has a slope of its own for exactly that — and it says so in its own column.

What OKLCH is doing

Every stop is generated in OKLCH and then mapped into sRGB by reducing chroma while holding lightness and hue.

The alternative — clipping the red, green and blue channels back into range — moves both. A color asked to be more saturated than the display allows comes back as a different color, not merely a duller one. Reducing chroma keeps the ramp’s shape: every stop still sits at the lightness the algorithm chose, in the hue you picked.

The nine algorithms

The algorithm setting decides how a ramp grows out of your seed color. There are two families. The first five build every stop in OKLCH and then bring it back into sRGB by lowering chroma, as described above. The last four work in plain HSL, where every value is already on screen, so nothing needs mapping.

Tailwind CSS
Copies the shape of Tailwind v4's own palettes: how light each stop is, and how much color it carries, measured across all seventeen of them. The ramp also bends toward your hue. A yellow is lightest where yellow can be most vivid, a blue sits lower down. Pick this for a ramp that feels like the ones people already know.
Linear
Even lightness steps from near white to near black, with every stop as saturated as your seed allows. The simplest shape there is. Pick it when the ramp should read as a plain gradient, or as a starting point you will tune with the sliders.
Flat Chroma
Holds the chroma your seed actually has at every stop, rather than the same share of what the gamut allows there. The two differ because the gamut is not one width: a dark teal fills 98% of a narrow opening, and two thirds of the way to white that same share is nearly twice the color — which is how a petrol ramp arrives at a summer sky. Keeping the number keeps the color, and the ends run out on their own. Pick it when your seed sits where the gamut is narrow, which mostly means dark and saturated. On a hue's widest point, with a seed already full, it is Linear exactly.
Descending Chroma
The same steps again, and the opposite promise: each stop carries color in proportion to how far it is from white, scaled so the stop at your seed’s lightness carries your seed’s chroma. Flat Chroma keeps the color and tints the pale end doing it; this fades the pale end to something you can put text on and lets the dark stops run into the gamut, which is where a palette wants its depth. Pick it when the light half should read as surfaces rather than as the color. A seed already near white has no distance to fade across, so there it is Linear again. It is the one algorithm with a control of its own: Chroma slope multiplies the falloff the seed implies, 1.00 being that falloff exactly.
Contrast (WCAG)
Turns the question around. Instead of choosing how light each stop is, it chooses how much contrast each stop has against white, from almost none up to about 17 to 1, and lets lightness follow. Because every stop is solved to a ratio, none of them can carry your seed exactly. Pick it when you have to promise contrast numbers.
Lightness Scale
Keeps your seed's hue and saturation and only walks lightness, from 95% down to 5%. A straight tint-to-shade ramp, the way most people would make one by hand.
Saturation Scale
Keeps the hue, and raises saturation from 10% to 100% while lightness falls from 90% to 20%. The light stops come out almost gray, the dark ones fully colored. Pastel at the top, deep at the bottom.
Hue Shift Scale
Keeps the saturation, walks lightness from 90% to 20%, and turns the hue a little as it goes: 15 degrees to one side at the light end, 15 to the other at the dark end. This is the warm-to-cool lean hand-made palettes have and a plain darken() ramp does not.
Muted Scale
The same lightness steps as the Lightness Scale, with HSL saturation held at a quiet 18%. Tinted neutrals: still recognizably your hue, never a second accent. Made to sit next to the Lightness Scale, so each step has a vivid and a muted tone at the same lightness. Not the same 18% as the muted band of the preset sets, which is eighteen percent of the chroma sRGB reaches at that stop rather than a fixed saturation — the same number twice over, measured in two different spaces.

The seed-is-a-stop switch only works with the first two. Tailwind and Linear can move a stop onto your exact color; Contrast cannot, because its stops are already spoken for, and the HSL scales never look for one.

Petrol is a bitch

I wanted a petrol. Not a teal, not a turquoise: the dark, slightly dusty blue-green you find on old enamel signs and the doors of certain Lisbon houses. I had the color, #055063, and I typed it in expecting a family of it. What came back was petrol in the middle and a swimming pool a few stops higher. Halfway up the ramp it had turned into a summer sky, bright and happy and nothing I had asked for.

It took a while to see that the color had not been betrayed. Petrol lives in a tight corner of what a screen can show: dark, and about as saturated as dark allows. That corner is small. The seed was using almost all of it, which is what makes it look rich rather than muddy. Linear took that as an instruction — use almost all of whatever is there — and carried it upward, where there is far more room. The same instruction, twice the room, twice the color. The ramp had done exactly what it was told, and what it was told was wrong for this color.

The obvious fix was to hold the amount of color steady instead of the share of it. That is Flat Chroma, and it fixed the middle: the stop that had been sky came back a quiet, believable petrol. But higher up it was still tinted, because the amount that reads as petrol on a dark ground reads as plain color on a pale one. The lightest stops were supposed to be surfaces, something you could put a paragraph on, and they still looked like weak sky. The problem had moved a few stops toward white. It had not gone.

So I turned the promise around. Instead of asking every stop to be petrol, Descending Chroma asks every stop to carry color in proportion to how far it is from white, tuned so the seed lands exactly where it was. Near white that is almost nothing, a gray with a memory of petrol in it. Toward black the ramp asks for more than the screen can give and simply runs into the wall, which is where a palette wants its depth anyway. The light half became surfaces. The dark half stayed petrol. The seed sat in the middle of both, untouched.

Linear#80dffe #15aacfa swimming pool, a summer sky
Flat Chroma#9bdbf1 #65a4b9the second is petrol, the first is still sky
Descending Chroma#c4d2d8 #81a0aba surface, and petrol gone pale
Stops 3 and 6 from the same seed, #055063, under the three OKLCH algorithms that keep its lightness steps. All three pass through the seed at stop 11. They disagree about the light half, which is the half an interface is mostly made of.

Two smaller things came out of the same afternoon. The chroma slider could not steer this algorithm, because here the seed’s chroma is a slope rather than an amount, and a slope has no edge to run into; the slider kept changing the ramp long after its track said there was nothing left. So the falloff got a control of its own, Chroma slope, which reads ×1.00 when the ramp is the one the seed implies and nothing else. The seed stays on the left, what you do to it stays on the right.

And the seed stays the seed. Turn the slope all the way down and the whole strip goes gray except one tile, the petrol you typed in. For a moment that looked like a bug. It is the opposite: you asked for one stop to be your color and thirteen to follow a rule, and that is what you got. The tile carries the anchor mark so it can say so.

Contrast shift

The shift bends where along the ramp each stop samples from, rather than moving stops one at a time. Positive values push the whole ramp darker, negative ones lighter, and both ends stay pinned. One control, and the spacing stays even.

−1.00#c4ccfa1.57:1 on white
−0.50#8f9afa2.57:1
0.00#6a6dfb4.04:1
+0.50#4d04fe7.60:1
+1.00#4514e48.51:1
The 500 stop of the same base color at five shift values — set the naming pattern to 50…950 and the shade count to 11 to reproduce them. Useful when a ramp reads right but sits one notch too light or too dark for the contrast you need.

Alpha ramps

A translucent token is only useful if it lands on the same color as its solid twin. Then the two are interchangeable: one knows what it sits on, the other does not, and a component can take either. That requirement leaves exactly one free choice per stop, and spending it on the smallest workable opacity shows as much of the real backdrop as possible.

The obvious construction — one saturated tone per hue at fourteen opacities — covers the light two thirds and then stops. A color over white can never composite to something darker than itself, so at full opacity the ramp is stuck while the ladder keeps descending:

stop 1#fff6f7 vs #fef6f60.002
stop 6#fda0a8 vs #fba29f0.015
stop 13#fa3d4e vs #4d0e130.393
stop 14#fa3d4e vs #2704060.498
One fixed tone against the ladder it should reproduce, with the deltaEOK between them. Two colors stop looking alike at about 0.02. The tone has to darken with the ramp — through the light stops it barely moves, which is where the intuition comes from.

Gray is the case where the solve collapses into something familiar. Over white the answer is always pure black and over black always pure white, so the two overlay ramps are the neutral ladder read as nothing but opacity — not a separate design.

over whiteα = 1 − V#00000024 → #dbdbdb
over blackα = V#ffffff18 → #181818
V is the grey's own sRGB channel, not its OKLCH lightness — the one place on this page where a letter does not mean L. #dbdbdb is V 85.9% and L 89.1%, and it is 1 − V that gives the 0x24 the token actually stores. Both ramps are kept in both themes: a white 18% over a photograph is the same token whether the page around it is light or dark.

Opacity and every channel are stored as bytes, and compositing on that grid is lossy — solving on paper and rounding misses by one bit on about a quarter of the stops. Each one therefore searches upward from its theoretical floor until the whole triple composites back exactly, which usually takes no steps at all and never more than a handful.