Menu

Search toolsChangelog

to move to openDescribe the problem, not the tool

A palette you can inspect, ship and use again

Turn named opaque hex colours into collision-safe CSS custom properties and structured JSON, with WCAG relative luminance and the stronger of black or white text measured for 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

brand-palette.css · 240 B

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

Structured palette JSON

brand-palette.json · 1 KB

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

Every palette colour

ink#1f293731, 41, 550.02#ffffff14.68
paper#f8fafc248, 250, 2520.95#00000020.07
accent#ea980e234, 152, 140.4#0000009
success#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.

How it works

Relative luminance is not average brightness

For each channel, the two hex digits are converted to a number from 0 to 255, divided by 255 and linearised:

linear(c) = c / 12.92                         when c <= 0.04045
linear(c) = ((c + 0.055) / 1.055) ^ 2.4       otherwise

L = 0.2126 R + 0.7152 G + 0.0722 B

Green contributes more to relative luminance than blue, and the linearisation means two encoded channel values cannot be averaged before the calculation. The contrast ratio for two luminances is (lighter + 0.05) / (darker + 0.05).

The tool calculates that ratio once with black at luminance 0 and once with white at luminance 1. Whichever is larger becomes the suggested text token.

The default palette

TokenHexBest textContrast ratio
ink#1f2937white14.6791
paper#f8fafcblack20.0712
accent#ea980eblack8.9961
success#16803awhite5.0205

The headline outputs are 4 colours, 2 using light text, 0 renamed tokens, and a lowest best-text ratio of 5.02 after display rounding. That minimum belongs to success; it is not an average across the palette.

What the files contain

The CSS download keeps the pairing close to the colour:

:root {
  --brand-ink: #1f2937;
  --brand-ink-text: #ffffff;
}

The JSON download carries the canonical hex, integer RGB channels, relative luminance, suggested text and unrounded-to-display contrast ratio. Three-digit hex such as #abc becomes #aabbcc in both files, so downstream consumers do not have to support two spellings.

CSS custom-property names are case-sensitive. Normalising all entered names to one ASCII kebab-case convention prevents --brand-Ink and --brand-ink from looking like the same token while behaving as different properties.

What the result does not certify

WCAG’s normal-text target is a property of an actual foreground and background as rendered. A token file cannot know whether text will be large, bold, disabled, placed over a gradient, blended with opacity or changed on hover. It also cannot replace focus visibility or non-colour status cues.

Treat the paired variable as a strong default that has been measured, then test the real component. Use the PNG to JPEG converter only for file format work; changing an image container does not make its embedded text accessible.

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

Is black or white always the better text colour?

One of them always has the higher ratio against an opaque colour, and the tool reports that measured winner. “Better” here means contrast only; brand constraints and the rest of the component still need review.

Why not average the red, green and blue numbers?

Hex channels are gamma-encoded sRGB values, not proportional light. WCAG first converts each channel to a linear value and then applies different red, green and blue weights.

What happens when two names become the same CSS token?

The first keeps the base name and later matches receive -2, -3 and so on. No declaration is silently replaced, and the table shows every final token before download.

Why are eight-digit hex colours rejected?

Their alpha channel blends with a background before contrast exists. A reliable ratio would need that background as another explicit input, so this tool accepts only opaque #RGB and #RRGGBB values.

Sources