Skip to content

Check the contrast between two colours

The contrast ratio between two colours is the lighter one's relative luminance plus 0.05, divided by the darker one's plus 0.05, which lands somewhere between 1 and 21. Type both colours above and you get the number, whether it clears each of the five bars WCAG actually states, and the closest colour to yours that would clear the one it misses. Hex, rgb, hsl and oklch all go in.

In your browser

Check a colour pair

Two colours in, and every threshold WCAG actually states comes back, including the 3 to 1 for borders and icons that most checkers leave out. Hex, rgb, hsl and oklch all parse, which matters if your design tokens are written in the one every other checker refuses.

A ratio has no front and back, so this changes nothing but the preview.

Normal text at sixteen pixels, which is what most of a page is set in.

Large text at twenty four

3.80 to 1

  • Normal text, AAAnything under 24px, or under 18.66px bold.Fails, needs 4.5
  • Normal text, AAAThe stricter bar. Worth aiming at for body copy.Fails, needs 7
  • Large text, AA24px and up, or 18.66px and up if it is bold.Passes, needs 3
  • Large text, AAAHeadings, at the stricter bar.Fails, needs 4.5
  • Borders, icons and controlsWCAG 1.4.11. Button edges, input outlines, focus rings, chart lines.Passes, needs 3

The nearest colour that would pass

Same hue, same intensity, moved only in lightness, so it is still your colour rather than a different one that happens to clear the bar.

  • For normal text at AA, 4.5 to 1
  • For normal text at AAA, 7 to 1

Every number here is WCAG 2's own formula over the two colours as a screen would paint them, which means an oklch outside sRGB is measured at the colour it becomes rather than the one you wrote. Large text means 24px and up, or 18.66px and up if it is bold. A ratio is only ever about two flat colours: text over a photograph, a gradient or a shadow has a different ground under every letter, and the honest way to check one of those is to sample the pixels.

Contrast checker, step by step

  1. Put in your two coloursIn whatever notation you already have them written down in, or use the two swatches if you would rather point at one than type it.
  2. Read the row you care aboutBody copy is the first row, a heading is the third, and a button edge or a focus ring is the last one, which most checkers do not report at all.
  3. Take the nearest colour that passesIt holds the hue and the intensity and moves only the lightness, so what comes back is your colour a shade along rather than somebody else's.
What it actually does

What the number is measuring, and the three places it quietly stops being true

Relative luminance is the three channels put back through the sRGB transfer curve and weighted 0.2126, 0.7152 and 0.0722, which is a rough model of how much light each one contributes to the eye. Everything follows from that, including the three cases where the answer is less useful than it looks. It knows nothing about hue, so two colours of very different colours and nearly equal lightness score about 1 and are genuinely unreadable together. It assumes both colours are flat, so text over a photograph, a gradient or a drop shadow has a different ground under every letter and needs the pixels sampling instead. And it is defined over what a screen paints, so a wide gamut oklch is measured at the colour it becomes in sRGB rather than the one you wrote, which this says out loud when it happens rather than reporting a number for a colour nobody can see.

There is no shortage of contrast checkers, and most of them do the same two thirds of the job. They take a hex pair, they report 4.5 and 7, and they stop. That leaves out the threshold for everything that is not text, which is the one an interface actually trips over, and it leaves out any way of getting from a failing colour to a passing one other than nudging a hex code and pressing the button again.

The other gap is notation. Colours in a modern codebase are increasingly written in oklch, because it is the only widely supported space where turning the lightness down by ten per cent does what you meant and does it evenly across the whole palette. Paste one of those into most checkers and you get an error. This site’s own tokens are written that way, which is how the gap came up.

Neither of those is clever. They are both just the parts nobody bothered with, and they are the parts that turn checking a colour into fixing one.

Questions

Why do you accept oklch when nothing else does?

Because that is increasingly where colours are written. A palette built in oklch keeps its lightness steps even in a way hex never does, which is exactly why people move to it, and then the first checker they reach for asks them to convert everything back by hand. This one takes the tokens as they are written in the stylesheet.

What is the 3 to 1 row for?

Non text things, meaning the edge of an input, a focus ring, a toggle in its off state, the line on a chart, an icon carrying meaning on its own. It is a separate success criterion from the text ones and it is where real interfaces fail most often, because a border light enough to look tasteful is usually well under it.

What counts as large text?

Twenty four pixels and up, or eighteen and two thirds pixels and up if it is bold. The threshold drops to 3 to 1 there because bigger letters are made of more pixels and survive a weaker separation. It is worth knowing that a heading passing at 3 to 1 will still be hard work for somebody reading it on a phone outdoors.

Does a suggestion always exist?

No, and when there is not one this says so rather than inventing something. On a middle grey background the very best any colour can manage is about 5.3 to 1, which is black, so nothing at all reaches the 7 to 1 that AAA asks for. If that is where you have ended up, the background is the thing that has to move.

Why is a colour with transparency refused?

Because it does not have a contrast ratio of its own. What it contrasts with is whatever ends up behind it, so an answer would mean this quietly picking a backdrop for you and then being confident about it. Work out the colour it composites to and put that in instead, and the number you get back will be one you can rely on.