WCAG Color Contrast Checker

Check a text and background color pair against WCAG AA and AAA. Live ratio, four pass/fail badges, a styled preview and the nearest passing colors.

🌐 Español

Large text preview β€” 24px bold

Normal text preview β€” 16px. The quick brown fox jumps over the lazy dog.

4.54:1contrast ratio
AA Normal (4.5:1): PassAA Large (3:1): PassAAA Normal (7:1): FailAAA Large (4.5:1): Pass

βœ… This pair already meets AA normal text (4.50:1) β€” no change needed.

πŸ”’ Private by design: everything runs locally in your browser and never uploaded to any server.

Re-deriving 4.54:1, the ratio the page opens on

The checker loads with #767676 on #ffffff, and it reports 4.54:1. That number is worth walking through once, because understanding where it comes from explains most of the surprising behaviour further down the page.

Take the foreground first. 76 in hex is 118, and all three channels are the same, so this is a neutral gray. Each channel is divided by 255, giving 0.4627. That is above the 0.03928 cutoff in the spec, so it goes through the gamma decode branch: ((0.4627 + 0.055) / 1.055) ^ 2.4, which is 0.1812. The three results are then weighted 0.2126, 0.7152 and 0.0722 for red, green and blue, and since those coefficients sum to one and the channels are identical, the relative luminance is just 0.1812.

White decodes to a luminance of exactly 1. The ratio formula adds 0.05 to both figures and divides the lighter by the darker, so 1.05 / 0.2312, which the page prints to two decimals as 4.54:1.

Two consequences fall straight out of that formula. Black on white gives 1.05 / 0.05, which is 21.00:1 and is the highest ratio the model can produce. Any color against itself gives 1.00:1. Everything else lives between those.

Four badges, four different questions

The row under the ratio is not four ways of saying the same thing. Each badge names its own threshold and answers independently.

The neutral grays make useful reference points. On white, #767676 clears AA Normal at 4.54:1 while #777777 sits at 4.48:1 and fails, so those two indistinguishable grays land on opposite sides of the line. For AAA Normal the boundary is #595959, which the two-decimal display rounds to 7.00:1 while clearing the threshold by a hair; #5a5a5a drops to 6.90:1 and fails.

The 24px and 18.67px line that picks your threshold

Large scale text is defined by the spec as at least 18pt at regular weight or at least 14pt when bold. Converted at the usual 96 dots per inch, that is 24px regular and roughly 18.67px bold. The tool encodes both figures, and the styled preview shows you the difference directly: the top line renders at 24px bold and the line under it at 16px regular.

That gap catches people out. Sixteen-pixel body copy is normal text, not large text, and so are captions, form labels, helper text and almost every legal disclaimer on the internet. If your design leans on 3:1 because a headline passes at it, check that the paragraphs below the headline are not quietly inheriting the assumption.

Checking a pair against a target

  1. Set the Text (foreground) color, either from the native swatch or by typing into the box beside it. Both fields accept a bare 767676 as readily as #767676, and three-digit shorthand like f0a expands normally.
  2. Set the Background color the same way. Nothing below the two fields renders until both parse, so a mid-edit value briefly hides the results.
  3. Read the ratio and the four badges. Use ⇄ Swap to exchange the two colors, which is the fastest way to sanity-check a dark-mode inversion of the same pair.
  4. If it fails, choose an entry in Suggest colors that meet. The four choices are AA normal at 4.5:1, AA large at 3:1, AAA normal at 7:1 and AAA large at 4.5:1, and it starts on AA normal.
  5. Press Apply on whichever suggested row you want, which loads that color back into the checker so the badges recompute against it. Copy results puts a plain-text summary of both colors, the ratio and all four verdicts on your clipboard for a ticket.

What the suggestion search moves, and how far

The search is deliberately conservative about your design. It never touches hue or saturation, so the suggestion is recognisably the same color family. It only walks lightness, one percentage point at a time, in each of four directions: darker text, lighter text, darker background, lighter background. The first value in each direction that clears the target is returned, and the candidate with the smallest lightness change is flagged as the closest match.

Working through a concrete case makes the cost clear. A friendly mid-blue like #4a9be0 on white scores 2.98:1, so it fails AA normal outright. Asking for AA normal returns two options rather than four. The nearest is darker text at #2178c1, reaching 4.64:1, which required dropping lightness from about 58 to about 44. The alternative is darkening the background all the way to #2e2e2e for 4.56:1, an 82-point move that is plainly not a tweak. Both lighter directions come back empty, because the background is already white with nowhere left to go and lightening the text only walks it closer to that background.

So the reassuring line that accessibility only costs you a few percent of lightness is sometimes true and sometimes not, and this tool will tell you which case you are in rather than pretending.

Hex only, opaque only, WCAG 2.x only

Three limits are worth stating plainly. There is no alpha channel here, so semi-transparent text over a photograph or a gradient cannot be checked: composite it first and test the resulting flat color. There is no gradient or image sampling either, though you can pull a flat color out of a screenshot with the image color picker and paste it in, or lift a stop out of something built in the CSS gradient generator.

And this is WCAG 2.x arithmetic only. APCA, the perceptual contrast model drafted for WCAG 3, uses a completely different calculation and would give different answers; it is left out rather than blended in, because 2.x is what conformance is measured against today.

For the neighbouring pieces of the same job: convert an RGB or HSL brand value to hex with the color converter, build a set to test with the palette generator, and check the same pair through protanopia and deuteranopia in the color blindness simulator. The rest of the set is in developer tools.

See it in action

Screenshot of the WCAG Color Contrast Checker tool with its default #767676-on-white pair, the live large/normal text preview and the WCAG AA/AAA pass-fail badges
WCAG Color Contrast Checker mid-process: its default #767676-on-white pair, the live large/normal text preview and the WCAG AA/AAA pass-fail badges.
Screenshot of the WCAG Color Contrast Checker result screen showing grey #9aa4ae text on white scoring only 2.53:1 β€” failing all four WCAG levels β€” with the suggested darker #697683 that passes AA at 4.65:1
The finished result: grey #9aa4ae text on white scoring only 2.53:1 β€” failing all four WCAG levels β€” with the suggested darker #697683 that passes AA at 4.65:1. The download link is a local blob URL β€” the file never leaves your device.

Frequently asked questions

Why does the whole results panel vanish while I am halfway through typing a hex code?

Because the ratio, the four badges, the preview and the suggestion list all render only when both fields hold a color the parser accepts. A half-typed value like "76" is not one, so the panel disappears until the code is complete again. The field itself stays put and shows an inline note reading "Not a valid hex color (try #767676)." under the text field, or "Not a valid hex color (try #ffffff)." under the background one, so you can see which of the two is the problem.

Which color notations can I paste into the two fields?

Three-digit and six-digit hex, with or without the leading hash character. Everything else is rejected, including eight-digit hex with an alpha channel, rgb() and hsl() function syntax, and CSS color keywords such as rebeccapurple. Convert first if your source is in another notation, then paste the hex.

Does a pair that lands on exactly 4.5:1 pass, or does it need to be above it?

Exactly 4.5:1 passes. The success criterion is worded as a minimum, and the comparison in this tool is a greater-than-or-equal test, so a value sitting precisely on the threshold is a pass. It is still worth leaving a little headroom in a design system, because rounding in the two-decimal display can make a borderline pair look safer than it is.

How far can a suggested color drift from the one I started with?

Further than people expect. Hue and saturation are held exactly and only lightness moves, one percentage point per step, stopping at the first value that clears your chosen target. A mid-blue sitting at 58 percent lightness on white has to travel about 14 points down before it clears AA normal text. The list shows the ratio each candidate reaches and tags the smallest move as the closest match.

Why do only two of the four suggestions appear when my background is white?

Because the other two searches found nothing. Each direction walks the lightness range independently and returns the first passing value it hits, and against a white background the walks toward lighter text and lighter background reach the end of the range without ever clearing the target. Those two rows are simply omitted. When no direction works at all, the tool tells you so instead of showing an empty list.

Do icons and form borders need checking too, or is this only about text?

They need checking, at a different number. WCAG 2.1 sets a 3:1 minimum for the visual boundaries of user interface components and for graphics you need to understand the content, which is the same figure as the large-text threshold. That means you can read the AA Large badge as your answer for a focus ring, an input border or a meaningful icon, even though the badge is worded for text.

Related tools