Skip to main content
Version: 3.0.0-beta

The palette

This page is about the layer underneath the tokens. The scale is not exposed, so it is hidden from the Figma picker and is not part of the public token set. Read it when you want to know why a token looks the way it does.

How the scale is built​

No individual colour in the scale is hand-picked. Each one is three OKLCH numbers, assembled from three decisions taken once: a hue angle per anchor, a chroma curve shared by every hue, and one lightness value per step, shared by every hue and every tone.

There are seven hue anchors, each one a single hue angle that does not change across the 15 steps or between schemes: blue, gray, green, moss green, north sea, orange and red. Those are the names in the token source, which is why gray is spelled the American way there and "grey" everywhere else on this page.

OKLCH is a way of writing a colour as three numbers: L for how light it is, C for how saturated, and H for which hue. It replaces hex and RGB, where the same three numbers tell you nothing about how a colour will look. The property we rely on is that L means the same thing across every hue, so changing H does not change how light the colour appears. That is what lets every tone's step 9 read as equally solid whatever colour it is.

light
123456789101112131415red21°orange59°green143°moss green205°blue241°north sea243°grayno hue
dark
123456789101112131415red21°orange59°green143°moss green205°blue241°north sea243°grayno hue
All 7 anchors, every step, ordered by hue angle, which is the same at every step and in both schemes, so it is listed once. Reading down a column shows every anchor taking the same lightness at that step. Reading across a row shows the chroma curve: muted at both ends, strongest through the middle. The curve is one shape scaled per anchor, so red is the most saturated at every step, north sea the least, and gray none at all. Steps 14 and 15 break the run because they are foregrounds meant to sit on a fill rather than positions along it. blue and north sea sit only 2.3° apart, which is why north sea can carry neutral on a dark page without the interface changing character.

Chroma is one curve per scheme, scaled by a single constant per hue: saturated through the middle of the scale and muted at both ends. Because the shape is shared, saturation is never set per step or per tone. red carries more chroma than north sea at every step, and gray carries none.

Lightness is the part that is chosen rather than derived. Every hue takes the same L at the same step, so it is set once for the whole scale, but the steps are deliberately uneven, and in dark mode they do not even rise in order. The steps jump on purpose explains why.

Every tone is built the same way. A step number means the same thing in each: the same lightness, and the same position on the chroma curve. So swapping a tone changes the colour and nothing else. What varies is the hue and how much chroma it carries, both set once per hue anchor.

What the step numbers mean​

Each tone has 15 steps. The obvious reading, "1 is lightest and 15 is darkest", is not how they work.

A step number is a role position, not a lightness. Step 9 is "the solid fill". Step 15 is "the foreground that sits on a solid fill". That is true in both schemes, and it is what lets one set of token names work in light and dark with no overrides.

Steps 1 → 13 broadly travel from closest to the background toward furthest from it, which means descending lightness in light mode and ascending in dark.

The scale is deliberately not monotonic

"Broadly" is carrying weight in that sentence. In dark mode the lightness values dip at steps 4 and 5 on purpose, because a strictly increasing ramp cannot hold the contrast that every role at those steps needs.

So global ordering is not a rule and the dip is not a bug. The two rules that do hold are narrower:

  1. No two steps within one scale resolve to the same value.
  2. Every sequence of states progresses visibly in one direction.

Steps 14 and 15 sit outside the second rule, as described below. The first holds for them too.

Which step does what​

Every tone, every step. Reading down a column shows the jump where strokes stop and fills begin; reading across a row shows that a step is the same role in every tone.

accentneutralinfosuccesswarningdangerrole1canvas, input fills, resting muted fill2muted fill on hover; disabled fill3muted fill when pressed4muted borders, disabled border5accent selected; muted border on hover6not used7muted border when pressed; tertiary foreground8links; secondary foreground9the solid fill; emphasis borders10solid fill on hover; on-default foregrounds11solid fill when pressed; interactive foregrounds12on-muted foregrounds; the inverted surface13primary foreground; the selected indicator14not used15the content plane; on-emphasis foregrounds

Steps 14 and 15 are inverse-polarity​

They sit outside the ordering by design: their lightness runs against the direction of steps 1 to 13. That is the criterion, not what a step is used for, which is why step 1 can hold text.inverted and icon.inverted and still be in order. Both must be excluded from any check on state order.

Step 15 serves two roles at once: the on-emphasis foregrounds that sit on a solid fill, and the panel surface, which is why background.surface, background.dialog and background.floating all resolve to it. That is the point of the reversal rather than a conflict, because it makes the panel lighter than the canvas in light and darker in dark.

The steps jump on purpose​

The scale is not a smooth gradient. Lightness values are chosen per step for intended pairings, such as text against surface and fill against canvas. Check contrast against the actual background for every state rather than assuming that all combinations meet the same target.

Notice that the middle of the scale is evenly spaced while the run from borders into fills jumps roughly twice as far. That gap is where strokes stop and fills begin, and it needs to be visible.

Tone to hue​

The mapping from a tone to a hue happens per scheme, which is what allows neutral to be a different hue in each:

ToneLightDark
accentmoss greenmoss green
neutralgraynorth sea
infoblueblue
successgreengreen
warningorangeorange
dangerredred

Dark-mode neutral is north sea rather than grey on purpose: a pure-grey dark interface reads as dead, and the slight blue keeps large dark surfaces feeling like a material.

Data visualisation​

data-visualization.* is a separate group with its own rules, because chart colour is about telling series apart rather than about contrast against a background.

Categorical

Ten hues, five steps each. Use the hues to separate series; use the steps within one hue when a single series needs shading.

Sequential

For magnitude, where one end means more than the other.

Diverging

For values either side of a midpoint, such as above and below a target.

These are the only values in the set that sit outside the OKLCH generator, and no EDS component binds them. They are for you to apply in charts.

Why you cannot extend the scale yourself​

A step number is a position, not a name for a particular colour. Inserting one moves every position above it, and most semantic tokens are defined by step number, so each of those would resolve to a different colour while the name a consumer binds to stayed exactly the same.

If you need a value the scale does not have, that is a token request rather than something to generate locally.