WCAG vs APCA: Testing Dark-Mode Text Contrast
ملخص
Pixel shares an analysis comparing WCAG 2 contrast ratios with APCA text legibility across all 79 pages of a dark-themed site. The most-used text color passes WCAG at 6.29:1 but APCA suggests it is legible only at 42px, highlighting why accessible design checks matter.
DEV Community
Our most used text colour passes WCAG at 6.29 to 1, and APCA says it is only legible at 42px
Yesterday I ran axe-core against all 79 pages of our site and it reported 981 colour contrast failures. Today I want to look at the other side of that number: the colours axe says are fine. The WCAG 2 contrast ratio is a formula from 2008 that divides two relative luminances and adds 0.05 to each to avoid dividing by zero. It is symmetric, which means it does not care whether your text is light on dark or dark on light, and it has a known weakness in the dark half of the range, where that 0.05 flattens real differences. APCA is the candidate replacement being developed for WCAG 3. It is asymmetric, it accounts for polarity, and it returns a lightness contrast value, Lc, from roughly -108 to 106 instead of a ratio. Our site is dark. #171717 background, grey text. That is exactly the region where the two methods are supposed to disagree, so I scored every text colour on the site with both. The instrument Every element on every page that owns a non-empty text node, with its computed colour and the background it actually sits on. Tailwind opacity modifiers mean the computed color is frequently oklab(... / 0.6), which no regex can read, so each colour goes through a 1x1 canvas: ctx.fillStyle = getComputedStyle(el).color; // any CSS colour at all
ctx.fillRect(0, 0, 1, 1);
const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data; // sRGB bytes
Then the alpha is composited over the first opaque ancestor background, and the resulting pair is scored twice: once with the WCAG 2 ratio, once with APCAcontrast(sRGBtoY(txt), sRGBtoY(bg)) from apca-w3 0.1.9, copied into the page so nothing is loaded at runtime. The whole site, 79 pages: elements carrying text 8,575 characters of visible text 381,241 distinct text colours 12 distinct backgrounds 7 distinct font sizes 15 distinct weights 4 (400, 500, 600, 700) distinct colour + background + size + weight combinations 52 52 combinations is the entire typographic surface of the site. Every judgement below is one of 52 rows. What WCAG 2 says Four of the 52 fail AA, covering 1,120 elements: text background size elements pages ratio needs #656565 #171717 14px 1,044 15 3.08 4.5 #727272 #171717 14px 68 34 3.73 4.5 #727272 #171717 12px 4 2 3.73 4.5 #585858 #171717 14px 4 2 2.52 4.5 All four are the same mistake: a Tailwind slash opacity modifier on an already muted grey, text-muted-foreground/60 and /70, which produces a colour nobody chose. The worst one lives on the 15 question category pages at about 68 elements each, and those elements average 4.4 characters, so the most numerous failure on the site is also the one carrying the least text. 1,044 elements, roughly 4,000 characters between them. My element level count of 1,120 against axe's 981 node count is the two tools counting different things, not a disagreement: axe skips nodes whose background it cannot resolve and nodes it judges to have no visible text. Same two colours, either way. What APCA says Using the APCA guidance tiers of Lc 75 for body text, Lc 60 from 24px, and Lc 45 for large or bold text, 20 of the 52 combinations fail, covering 5,603 elements. Breaking that against the WCAG result: combinations elements fail both methods 4 1,120 pass WCAG, fail APCA 16 4,483 fail WCAG, pass APCA 0 0 Zero. Every single colour WCAG objects to, APCA also objects to, and APCA then objects to sixteen more. On a dark site the newer model is strictly stricter, and the gap is not marginal. Here is the one that matters, because it is our most used text colour on 76 pages: #999999 on #171717
WCAG 2: 6.29 : 1 pass AA (needs 4.5), pass AAA for large text
APCA: Lc 46.2
6.29 is not a squeak past the line, it is 40 per cent above it. And APCA's own font size lookup table, which is part of the specification rather than my interpretation, says this about Lc 45: Lc min size at weight 400 min size at weight 700 30 108 px 72 px 45 42 px 24 px 60 24 px 16 px 75 18 px 14 px 90 16 px 14 px 100 15 px 13 px We use #999999 at 12px, 14px, 16px and 18px....

Powered by Readybot.io