Tabellab
Data, tech, science & health

Contrast ratio formula: how WCAG contrast is calculated

The WCAG contrast ratio is (L1 + 0.05) / (L2 + 0.05), where L1 is the relative luminance of the lighter colour and L2 that of the darker one. It runs from 1:1 (no contrast) to 21:1 (black on white). Normal text needs at least 4.5:1 for level AA. In the example below, grey #767676 on white reaches 4.54:1 and just passes, while orange #F28C28 on white only reaches 2.45:1 and fails even for large text.

Updated 21 Oct 2026 5 min read By Tabellab team

Step 1: hex code to RGB

A hex colour such as #2F6FDB holds three two-digit hexadecimal numbers: red 2F, green 6F, blue DB. MID cuts them out and HEX2DEC turns them into 0–255 values: =HEX2DEC(MID(B5,2,2)) = 47 for red, then 111 for green and 219 for blue.

Step 2: linearise each channel

Screen values are gamma-encoded, so 128 is not half as bright as 255. WCAG uses the sRGB conversion back to linear light. Divide by 255 to get a value c between 0 and 1, then:

linear = c / 12.92 if c ≤ 0.04045, otherwise ((c + 0.055) / 1.055)^2.4

Older WCAG 2 texts print 0.03928 as the threshold instead of 0.04045. For 8-bit colours the results are identical, because no value from 0 to 255 divided by 255 falls between the two.

Step 3: relative luminance

L = 0.2126 × R + 0.7152 × G + 0.0722 × B, using the linearised channels.

The weights follow the eye's sensitivity: green counts almost ten times as much as blue. White has L = 1, black L = 0, and the mid-grey #767676 only 0.1812, far below 0.5, because of the gamma curve. The Blue in the example has L = 0.1709 and the Orange 0.3779.

Step 4: the contrast ratio and the WCAG levels

=(MAX(L1,L2)+0.05)/(MIN(L1,L2)+0.05) puts the lighter colour on top automatically, so the order of text and background does not matter: Orange on Navy and Navy on Orange both give 5.80:1. The 0.05 stands for light reflected by the screen and avoids dividing by zero for black.

  • Level AA: 4.5:1 for normal text, 3:1 for large text (about 24 px regular or 18.66 px bold and larger).
  • Level AAA: 7:1 for normal text, 4.5:1 for large text.
  • Non-text contrast (icons, input borders, focus indicators): 3:1 under WCAG 2.1 and later.

White text on the Blue reaches 4.75:1, so it passes AA but not AAA. Report the ratio as text with =TEXT(C11,"0.00")&":1", but test the unrounded number: 4.499 displays as 4.50 and still fails.

Worked example: six colours, six pairs, AA and AAA

Rows 2 to 7 turn hex codes (yellow inputs) into RGB and relative luminance. Rows 10 to 15 look up the luminance of each text and background colour, calculate the contrast ratio and test it against the WCAG thresholds.

Worked example: six colours, six pairs, AA and AAA
ABCDEF
1ColourHexRGBLuminance L
2White#FFFFFF2552552551.0000
3Black#0000000000.0000
4Grey#7676761181181180.1812
5Blue#2F6FDB471112190.1709
6Orange#F28C28242140400.3779
7Navy#1B2A4A2742740.0238
8
9TextBackgroundContrast ratioAA normal 4.5AA large 3AAA normal 7
10BlackWhite21.00PassPassPass
11GreyWhite4.54PassPassFail
12OrangeWhite2.45FailFailFail
13WhiteBlue4.75PassPassFail
14OrangeNavy5.80PassPassFail
15NavyOrange5.80PassPassFail
16
17Grey on white as a label4.54:1
18Darkest background for white text at 4.5:1 (max L)0.1833
Key formulas
  • C5=HEX2DEC(MID(B5,2,2))Red channel from the hex code
  • F5=0.2126*IF(C5/255<=0.04045,C5/255/12.92,((C5/255+0.055)/1.055)^2.4)+0.7152*IF(D5/255<=0.04045,D5/255/12.92,((D5/255+0.055)/1.055)^2.4)+0.0722*IF(E5/255<=0.04045,E5/255/12.92,((E5/255+0.055)/1.055)^2.4)Relative luminance with linearised channels
  • C11=(MAX(VLOOKUP(A11,$A$2:$F$7,6,FALSE),VLOOKUP(B11,$A$2:$F$7,6,FALSE))+0.05)/(MIN(VLOOKUP(A11,$A$2:$F$7,6,FALSE),VLOOKUP(B11,$A$2:$F$7,6,FALSE))+0.05)Contrast ratio, lighter colour on top
  • D11=IF(C11>=4.5,"Pass","Fail")AA for normal text
  • E17=TEXT(C11,"0.00")&":1"Ratio as a label
  • E18=(1+0.05)/4.5-0.05Largest background luminance for white text at 4.5:1

Illustrative colours. WCAG thresholds are cited in general terms, with no affiliation with the W3C. Contrast is one part of accessibility, so test real pages with real users and tools as well.

Turning the formula around

Solve the ratio for the background: white text (L = 1) reaches 4.5:1 on any background with L ≤ (1 + 0.05) / 4.5 − 0.05 = 0.1833. Grey #767676 (L = 0.1812) is just inside, which is why it is often quoted as the lightest grey that passes AA on white. One step lighter, #777777, has L = 0.1845 and drops to 4.48:1. To fix a failing brand colour, darken it in small steps and recalculate until it passes. Highlight failing pairs with conditional formatting formulas to audit a whole palette at once.

Common mistakes

  • Using the RGB values directly. 0.2126 × 118 + … on the raw 0–255 values gives wrong luminances. Divide by 255 and linearise first.
  • Putting the darker colour on top. (L2 + 0.05) / (L1 + 0.05) gives a ratio below 1. Use MAX and MIN so the order never matters.
  • Rounding before testing. A ratio of 4.499 displays as 4.50:1 but fails the 4.5 rule. Compare the unrounded value.
  • Treating all text as large. The 3:1 threshold only applies from about 24 px regular or 18.66 px bold. Body text needs 4.5:1.
  • Wrong MID positions. With the # sign in the code, red starts at position 2, green at 4 and blue at 6. Without the #, it is 1, 3 and 5.
  • Forgetting non-text elements. Icons, chart lines, input borders and focus rings need 3:1 against their background too.

Frequently asked questions

How is the contrast ratio calculated?

Calculate the relative luminance of both colours, then divide the lighter one plus 0.05 by the darker one plus 0.05. Black on white gives (1 + 0.05) / (0 + 0.05) = 21:1, the maximum.

What contrast ratio does WCAG AA require?

At least 4.5:1 for normal text and 3:1 for large text, which is about 24 px regular or 18.66 px bold and larger. Level AAA raises this to 7:1 and 4.5:1. User interface components and graphics need 3:1.

What is the relative luminance formula?

L = 0.2126 R + 0.7152 G + 0.0722 B, where R, G and B are the sRGB channels converted to linear light: c / 12.92 for small values and ((c + 0.055) / 1.055)^2.4 otherwise, with c = channel ÷ 255.

What is the lightest grey that passes on white?

#767676 gives 4.54:1 on white and passes AA for normal text. #777777 gives 4.48:1 and fails.

Practise it in a lab

Related guides

Reading is step one. Doing is step two.

Every guide links to a lab where you build the same calculation in an online spreadsheet and get instant feedback.

Open the lab