The WCAG contrast ratio compares the brightness of two colours on a scale from 1:1 (identical) to 21:1 (black on white). For level AA, normal text needs at least 4.5:1 against its background, and large text needs at least 3:1. Level AAA asks for 7:1 for normal text and 4.5:1 for large text. This article shows how the number is calculated, with a worked example you can repeat, explains what counts as large text, and walks through checking and fixing a colour pair.
The thresholds at a glance
| What you are checking | Level AA | Level AAA |
|---|---|---|
| Normal text | 4.5:1 | 7:1 |
| Large text | 3:1 | 4.5:1 |
| Buttons, icons, input borders and other interface parts | 3:1 | not defined |
Large text means 18 point or bigger, or 14 point or bigger in bold. In CSS pixels that is 24 px, or about 18.66 px in bold. Text is not large because it looks big on your screen: it is large by its size and weight.
The interface row comes from WCAG 2.1, which added a 3:1 requirement for the parts of a control that you need in order to see it, such as the border of a text field, an icon without a label, or the outline of a selected state. Text that is purely decorative, a disabled control and a logo are exempt.
Where the number comes from
The ratio is calculated from the relative luminance of each colour, a number from 0 (black) to 1 (white). To get it:
- Take each colour channel, red, green and blue, as a value from 0 to 255 and divide it by 255.
- Linearise it: if the value is at most 0.03928, divide it by 12.92; otherwise add 0.055, divide by 1.055 and raise the result to the power 2.4. This undoes the curve that screens use for brightness.
- Combine the three results as luminance = 0.2126 × red + 0.7152 × green + 0.0722 × blue. Green counts most because the eye is most sensitive to it.
- The contrast ratio is (lighter luminance + 0.05) divided by (darker luminance + 0.05).
The 0.05 stops the ratio from becoming infinite with black, and it is why black on white is exactly 21:1: (1 + 0.05) / (0 + 0.05) = 21.
A worked example: grey text on white
Take text coloured #767676 on a white background. Because the three channels are equal (118), one calculation covers all of them:
- 118 divided by 255 is 0.4627.
- (0.4627 + 0.055) / 1.055 is 0.4908, and 0.4908 to the power 2.4 is 0.1812.
- The luminance is 0.1812. White is 1.
- The ratio is (1 + 0.05) / (0.1812 + 0.05) = 1.05 / 0.2312 = 4.54.
So #767676 passes AA for normal text, just. The next lighter grey, #777777, gives 4.48:1 and fails. That is a one-step difference that nobody can see, and it decides whether a page passes an audit.
A colour with unequal channels works the same way, one channel at a time. White text on a blue button, #1d4ed8, has channels 29, 78 and 216, which linearise to about 0.0123, 0.0762 and 0.6867. The luminance is 0.2126 × 0.0123 + 0.7152 × 0.0762 + 0.0722 × 0.6867, which is about 0.1067, and the ratio is 1.05 / 0.1567 = 6.70. That passes AA for normal and large text and AAA for large text, but not AAA for normal text, which needs 7.
More pairs to calibrate your eye
| Text | Background | Ratio | AA normal | AA large and UI | AAA normal |
|---|---|---|---|---|---|
| #000000 | #ffffff | 21.00:1 | Pass | Pass | Pass |
| #595959 | #ffffff | 7.00:1 | Pass | Pass | Pass |
| #767676 | #ffffff | 4.54:1 | Pass | Pass | Fail |
| #777777 | #ffffff | 4.48:1 | Fail | Pass | Fail |
| #ffffff | #ea580c | 3.56:1 | Fail | Pass | Fail |
| #ff0000 | #ffffff | 4.00:1 | Fail | Pass | Fail |
| #949494 | #ffffff | 3.03:1 | Fail | Pass | Fail |
Pure red on white, 4.00:1, is the one that surprises people. It looks strong and still fails for body text. White on that orange button is fine for a large heading and not for the small label under it.
Checking a pair with the colour tools
The colour tools have a Contrast checker tab that does all of this in your browser.
- Open the Contrast checker tab and enter the text colour and the background colour, as hex such as #767676 or with the colour picker. "Swap colours" flips them.
- Read the ratio, then the five checks: AA normal text (4.5:1), AA large text (3:1), AAA normal text (7:1), AAA large text (4.5:1), and AA for buttons and icons (3:1). Each shows Pass or Fail.
- Look at the preview, which shows your colours as normal text at 16 px and as large text at 24 px.
- If normal text fails, the tool suggests the closest colour with the same hue and saturation that passes AA, and "Use" applies it. For #777777 on white the suggestion is #767676.
The tool takes care of one trap: if the exact ratio is 4.499, it shows 4.49:1, not 4.50:1. A ratio of 4.499 does not reach 4.5, and a display of 4.50 next to a Fail would only confuse.
Some pairs have no fix inside their own colour. White text cannot be made lighter, so against a mid-tone background the only change that helps is the background. The tool tells you when no colour of that hue reaches AA.
If you are starting from a logo or photo, the same tool's Colours from an image tab pulls out the main colours, and you can copy a swatch and check it as a text or background colour.
Mistakes to avoid
- Judging by eye. Screens, lighting and eyes differ. A number does not.
- Looking only at the large-text row. Body text is normal text. The first row decides.
- Text over a photo or a gradient. The checker compares two flat colours. Check the worst spot behind the text, or put the text on a solid panel.
- Colours with transparency. A semi-transparent text colour is blended with what is behind it. Check the final blended colour, not the one in your style sheet.
- Placeholder text and hover or focus states. They are text and interface parts too, and are often the lightest greys in a design.
- Light and dark versions. A dark theme needs its own check. A colour that passes on white may fail on near-black.
Where else contrast matters
Printed codes follow the same logic. A barcode with pale bars on a pale background scans badly, which is why the barcode generator warns when your bar and background colours are too close and when the bars are lighter than the background.
A short checklist
Normal text needs 4.5:1 for AA, large text and interface parts need 3:1, and AAA is 7:1 and 4.5:1. Check every text and background pair, including states and dark themes, fix a failing pair by darkening the text or changing the background, and trust the number, not your eyes.