← All articles

Craft · · 3 min

Building a colour palette you can defend

Neutrals do the work, one accent does the pointing, and every colour needs a job description.


A usable product palette is mostly neutrals. This surprises people who arrive from brand work, where the palette is the identity. In a product the palette is infrastructure, and the accent is a single instrument played sparingly.

Neutrals do the work

Four or five neutrals carry every surface, border, divider and line of text in a product. They are the page background, the card that sits on it, the hairline between rows, the body copy, the muted caption underneath.

Two rules make them behave:

Pick a temperature and hold it. Warm greys or cool greys, not both. Mixed-temperature neutrals are the most common reason an interface looks subtly dirty without anyone being able to point at the cause — a warm card on a cool background reads as a rendering error rather than a choice.

Give each one a role, not a number. "Surface", "surface-subtle", "border-card", "border-hairline" survive being applied by someone who was not there when you chose them. grey-200 does not: nothing about the name says whether it is a border or a background, so it gets used as both, and then you cannot change either.

Most design systems converge on this. IBM's Carbon is a thorough public example of role-named colour and worth reading before you invent your own scheme.

One accent, with one job

Then a single accent, and its job is showing where to act. Buttons. The active tab. The focused input's border. That is the list.

The moment the accent also appears in headings, illustrations, badges and the occasional decorative underline, it stops meaning anything. Users learn the association by repetition, and the association only forms if the colour is boringly consistent. An accent used everywhere is a brand colour, which is a different and less useful thing inside a product.

This has a contrast consequence people discover late. A saturated mid-tone accent — the kind that looks superb on a button — usually fails WCAG's 4.5:1 minimum as text under 18px. Our own accent, #E8511E, lands at 3.6:1 on an off-white background. The fix is not to abandon it but to keep two values: the accent for large type and fills, and a darkened variant for small text. Deciding that once beats rediscovering it in an audit.

While you are there: colour must never be the only carrier of meaning. WCAG's use of colour criterion is the one most often failed by a status dot with no label beside it.

Semantic colours are not decorations

Success, warning and danger are a separate set, and they should not be sampled from the accent. Two reasons.

The obvious one is that if danger is a shade of your accent, a destructive button and a primary button look related, which is the opposite of the message.

The subtler one is that semantic colours are the palette's most reusable vocabulary and the easiest to dilute. Once green means "success", using it for an unrelated marketing chart teaches people it means nothing in particular. Reserve them for state and accept that this costs you some flexibility elsewhere.

Write the job description next to the colour

This is the step that turns a palette into something another person — or an agent — can apply correctly.

Compare these two records of the same decision:

#E8511E
#101010
#F4F4F1
#E8511E  primary action only — buttons and focus. Never body text.
#C4400F  the same accent, darkened, for accent text under 18px.
#101010  ink. All body copy and headings.
#F4F4F1  surface. Cards and inset panels, never page background.

The first is six unexplained hexes and a lottery. The second generalises: faced with a component nobody has designed yet, you can derive the right answer from the rules rather than guessing which existing screen to copy.

That difference matters most when the thing applying the palette is an agent, which has never seen your product and can only reason from what it is told. Asked for a pricing page with the first list, it will distribute the colours plausibly and wrongly — orange headings, orange links, an orange badge. With the second it has nothing to guess. That is the general argument in how to teach a coding agent your design taste, and the same reason type pairings need their rules recorded, not just their names.

Once the rules exist, getting them to the agent is one block of config — see the docs or the Claude Code guide.

Further reading

Keep reading