AniUI Academy

Compute a WCAG contrast ratio

hardDOM & Utilities+350 XPPractice

"Is this grey readable on white?" has an exact answer, defined by WCAG, and it is a short piece of arithmetic. Implementing it is how the rule that body text needs 4.5:1 stops being folklore.

Implement contrastRatio(hexA, hexB). Each argument is a hex colour, with or without a leading #, in either the 6-digit or 3-digit shorthand form, case-insensitive.

For each colour, convert every channel to c = value / 255, then linearise it: if c is less than or equal to 0.03928 use c / 12.92, otherwise use Math.pow((c + 0.055) / 1.055, 2.4). The relative luminance is 0.2126 * R + 0.7152 * G + 0.0722 * B of those linearised channels.

The ratio is (lighter + 0.05) / (darker + 0.05) where lighter is the larger of the two luminances, so the result is always at least 1 and does not depend on argument order. Return the raw unrounded number — the tests round it themselves with Math.round(value * 100) / 100.

Arguments

hexAstring
The first hex colour, with or without #, in 6-digit or 3-digit form.
hexBstring
The second hex colour, in the same accepted forms.

Returns

number — The WCAG contrast ratio, always at least 1 and independent of argument order. Unrounded so callers can decide the precision.

Examples

contrastRatio("#ffffff", "#000000");
// => 21 (exactly 21:1, the maximum)

Black on white is the highest possible contrast.

contrastRatio("#ffffff", "#777777");
// => ~4.48

Mid-grey on white is just under the WCAG AA threshold of 4.5:1 for body text.

contrastRatio("#3366cc", "#3366cc");
// => 1 (a colour against itself)

Equal luminance gives ratio 1 — no contrast at all.

What it has to do

  • Accept 6-digit and 3-digit hex colours, with or without #, in any case.
  • Linearise each channel with the 0.03928 threshold, / 12.92 below it and the 2.4 power above it.
  • Weight the channels 0.2126, 0.7152 and 0.0722 for relative luminance.
  • Return (lighter + 0.05) / (darker + 0.05), so the result never depends on argument order.
  • Return the raw number without rounding it yourself.

Notes

  • WCAG 2.1 AA requires 4.5:1 for normal text and 3:1 for large text. WCAG AAA raises those to 7:1 and 4.5:1.
  • APCA (the proposed WCAG 3 contrast algorithm) uses a different formula altogether — perceptually weighted and asymmetric in text vs background. This function is the current standard, not that future one.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.