Measure the text the way an eye reads it, not the way a ratio scores it
The sweep has checked WCAG AA contrast for a long time and the page passed. That result was worth less than it looked. WCAG 2's ratio is a luminance gap, and a luminance gap overstates readability once one colour is near black — which is every reading on a page whose ground is #141414. Tuning a colour until it clears 4.5:1 there lands on text that passes the audit and still reads as a smudge across a table in a dim room. Several rules in this stylesheet said as much in their own comments, one of them "measured 4.5:1 at #a5a5a5 exactly, so it sits above that": the right instinct through the wrong lens. The check also could not see half the backgrounds it scored against. It reads background-color and takes the best contrast of any candidate ancestor, so text on the felt was scored against the dark page behind the felt. Two plain AA failures sat in a clean report for as long as the check has existed — the chair number on the brown rim at 2.65:1, and the pot's own label, gold on green, at 2.66:1. runContrastAudit.js scores with APCA and gets the background by screenshotting the page with its text made transparent and reading the pixels, which no gradient or shadow can fool. The first version walked the DOM and called the gold dealer button black-on-black at Lc 0.4; that false positive is why it works this way. Found: 65 of 92 pieces of text below their level, 54 passing AA at the same moment, worst Lc 28. Now nothing is below Lc 60, nothing fails AA, and the worst is Lc 60.4. Three fixes worth naming, none of them "make the grey lighter": - A player's stack was secondary text at Lc 44, under a name in --text at a heavier weight. That says a dealer needs the name more than the number; during a hand the opposite is true. Hierarchy comes from size and weight now, which is free, rather than from fading, which on a dark ground is the most expensive way to say it. - An empty seat had no background of its own, so the felt decided how readable its number was: Lc 28 on the rim, 30 on the green, 33 further in. It has a ground now, at 0.62 and not the 0.82 first written — contrast is nearly flat across that range and the heavier wash put four dark rectangles on the felt like holes. - The pot's label sat on the one green in the design. The disc is deeper rather than the label bleached, because gold on dark green is the pairing this page is built on. Deepening only its outer stop moved it two points: the highlight is at 32%/28% and that is exactly where the label sits. apca.js is checked against the eight published reference pairs, png.js against round-tripped fixtures for every filter. Four fault injections caught; a fifth turned out to be a no-op — the Paeth predictor is symmetric in its first two arguments over all 16,777,216 byte triples, so that swap is untestable, which is written down rather than papered over with a fixture that appears to catch it. Still below the strict APCA level: 55, all of them 10-12px labels that would need white type or a bigger seat puck. Reported, not chased, and said out loud in the sweep's own notes. Layout sweep unchanged: 5 FAILs, 14 offender lines, no regressions. 950 tests pass. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
This commit is contained in:
@@ -652,6 +652,23 @@ const LayoutAudit = {
|
||||
};
|
||||
},
|
||||
|
||||
// Two things this check cannot do, both found by measuring against real pixels in
|
||||
// runContrastAudit.js and neither of them fixable from inside a DOM walk:
|
||||
//
|
||||
// 1. It reads `background-color`, so anything painted by a gradient is invisible to
|
||||
// it. The chair number on an empty seat sat on the felt's brown rim at 2.65:1 and
|
||||
// the pot's label on green at 2.66:1 — two plain AA failures this reported clean,
|
||||
// for years, because it never saw either background.
|
||||
// 2. Where an element has several candidate ancestors it takes the best contrast of
|
||||
// any of them ("the kindest reading available", below). That is the right instinct
|
||||
// for a fade, and it is what let (1) through: the flattering candidate was the
|
||||
// dark page behind the felt, which is not what the text is sitting on.
|
||||
//
|
||||
// Both are why the sibling audit screenshots the page with its text made transparent
|
||||
// and samples the pixels instead. This one is kept because AA is the number an
|
||||
// accessibility audit will ask for, and because it runs against a live DOM with no
|
||||
// screenshot to decode — but a clean result here is not a claim that the page is
|
||||
// readable. That claim belongs to the APCA sweep.
|
||||
contrast({ minimum = 4.5, largeMinimum = 3, selector = null } = {}) {
|
||||
const offenders = [];
|
||||
const unmeasured = [];
|
||||
|
||||
Reference in New Issue
Block a user