Menu

Search toolsChangelog

to move to openDescribe the problem, not the tool

Choose Black or White Text for Hex Colours

The choice depends on relative luminance, not whether a hex value looks subjectively bright. For the example palette, the orange accent pairs more strongly with black while the dark navy pairs more strongly with white; the table reports the winning ratio for each instead of applying one threshold to every swatch.

One name and opaque #RGB or #RRGGBB value per line. Multi-word names are converted to safe kebab-case tokens.

Lower-case letters, numbers and hyphens. The generated variables begin --brand- by default.

Colours ready
4
Lowest best-text contrast
5.02
Colours needing light text
2
Names de-duplicated
0

CSS custom properties

ui-palette.css · 216 B

One colour variable and one suggested text-colour variable per token, inside a :root block.

Structured palette JSON

ui-palette.json · 1 KB

Names, canonical hex and RGB values, relative luminance, suggested black or white text and its contrast ratio.

Every palette colour

navy#1f293731, 41, 550.02#ffffff14.68
orange#ea980e234, 152, 140.4#0000009
cloud#f8fafc248, 250, 2520.95#00000020.07
forest#16803a22, 128, 580.16#ffffff5.02

Results are provided as-is, with no warranty of accuracy. The method and its sources are published below so you can check the working.

What to take away
  1. sRGB channels are linearised before their weighted luminance is added, so averaging the encoded red, green and blue values gives the wrong result.

  2. Contrast is the lighter luminance plus 0.05 divided by the darker luminance plus 0.05.

  3. The exported CSS pairs each swatch with its individually measured black or white text token.

How it works

How it is done

  1. Parse one name plus an opaque three- or six-digit hex colour per non-empty line, expanding shorthand to canonical lower-case six-digit RGB.
  2. Normalise names and the prefix to lower-case ASCII kebab-case, adding -2, -3 and later suffixes rather than overwriting duplicate tokens.
  3. Linearise each encoded sRGB channel, combine relative luminance with the WCAG weights and compare the resulting contrast against black and white.
  4. Export one colour and suggested-text custom property per token in CSS and the same values, channels, luminance and ratio as structured JSON.

What it assumes

  • Every entered colour is opaque. Alpha colours are refused because their displayed contrast depends on an unstated surface underneath.
  • The reported text choice compares only #000000 and #ffffff; it does not search every possible foreground colour.
  • A higher measured ratio does not by itself make a complete component accessible; type size, weight, states, focus, non-text cues and actual rendered colours still matter.
  • Hex values are interpreted as sRGB and no ICC profile or wide-gamut colour space is attached to the exported tokens.

Common questions

Does the higher of black or white always meet normal-text contrast?

For an opaque sRGB colour, one of the two extremes will produce a ratio a little above 4.5 or better, but other accessibility factors such as font weight and state cues still matter.

Why are transparent hex colours refused?

Contrast for a transparent colour depends on the surface behind it. Requiring an opaque swatch keeps the reported ratio a property of the entered pair rather than an unstated background assumption.

Sources

The full method, worked example and every assumption behind this figure are on Colour Palette File Maker.