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:
@@ -837,6 +837,75 @@ the bet chips at extreme crowding the way the tight tier already drops the hole
|
||||
capping the seat count by viewport, or letting the felt scroll. Each trades something a
|
||||
dealer can see, so none of them should be picked by whoever is next holding a ruler.
|
||||
|
||||
## The contrast check was passing text nobody could read
|
||||
|
||||
The layout sweep has checked WCAG AA contrast for a long time and the page passed it.
|
||||
That result was worth less than it looked, for two separate reasons.
|
||||
|
||||
The first is the instrument. WCAG 2's contrast ratio is a luminance gap, and a luminance
|
||||
gap overstates readability once one of the two colours is near black — which is every
|
||||
reading taken on this page, whose ground is `#141414` and whose panels are darker than
|
||||
its rows. This is not a quibble about the spec; it is the spec's own working group's
|
||||
position, and it is why APCA exists. Tuning a colour until it clears 4.5:1 on a
|
||||
near-black ground lands on text that passes the audit and still reads as a smudge across
|
||||
a table in a dim room. Several rules in the stylesheet said so in their own comments —
|
||||
one of them "measured 4.5:1 at `#a5a5a5` exactly, so it sits above that" — which is the
|
||||
right instinct aimed through the wrong lens.
|
||||
|
||||
The second is that the check could not see half the backgrounds it was scoring against.
|
||||
It reads `background-color` and walks up the ancestors, so a background painted by a
|
||||
gradient is invisible to it; and where several ancestors are candidates it takes *the
|
||||
best* contrast of any of them. Put together, text on the felt was being scored against
|
||||
the dark page behind the felt rather than the felt. Two plain AA failures had been
|
||||
sitting in a clean report for as long as the check has existed: the chair number on an
|
||||
empty seat, on the table's brown rim, at 2.65:1, and the pot's own label, gold on green,
|
||||
at 2.66:1.
|
||||
|
||||
`runContrastAudit.js` is the answer to both. It scores with APCA, and it gets the
|
||||
background by screenshotting the page with every piece of text made transparent and
|
||||
reading the pixels underneath — which cannot be fooled by a gradient, a shadow or a
|
||||
border, because it is looking at what was painted. The first version of it *did* walk the
|
||||
DOM, and reported the gold dealer button as black-on-black at Lc 0.4; an element anyone
|
||||
can see is legible. That false positive is why the tool works the way it does.
|
||||
|
||||
What it found, on 92 distinct pieces of text: 65 below the level APCA sets for the size
|
||||
and weight they are drawn at, **54 of them passing WCAG AA at the same moment**. The
|
||||
worst was Lc 28. After this round nothing on the page is below Lc 60, nothing fails AA,
|
||||
and the worst reading is Lc 60.4.
|
||||
|
||||
Three of the fixes are worth naming, because none of them is "make the grey lighter":
|
||||
|
||||
- **A player's stack was styled as secondary text.** Muted grey at Lc 44, under a name
|
||||
set in `--text` at a heavier weight. Read back the other way that says a dealer needs
|
||||
the name more than the number, and during a hand the opposite is true — the name is
|
||||
looked up once to find the chair, the stack is read on every action because it decides
|
||||
whether a player can cover the bet. Hierarchy now comes from size and weight, which is
|
||||
free, instead of from fading, which on a dark ground is the most expensive way to say
|
||||
"secondary".
|
||||
- **An empty seat had no background of its own**, just a 2%-white wash at 0.7 opacity, so
|
||||
whatever the felt was doing behind it decided how readable its number was: Lc 28 on the
|
||||
rim, 30 on the dark green, 33 further in. Contrast that depends on where a seat happens
|
||||
to sit cannot be reasoned about. It has a ground now. The wash is 0.62 and not the 0.82
|
||||
it was first written at, because contrast turns out to be nearly flat across that range
|
||||
— two points of Lc — and the heavier one put four dark rectangles on the felt like
|
||||
holes.
|
||||
- **The pot's label sat on the one green in the design.** `--gold` is tuned for near-black
|
||||
and measures Lc 70-odd everywhere else it is used; on the pot disc it was Lc 38. The
|
||||
disc is deeper now rather than the label bleached, because gold on dark green is the
|
||||
pairing the whole page is built on. Deepening only the disc's outer stop moved it two
|
||||
points and no further — the highlight is at 32%/28% and that is exactly where the label
|
||||
sits, so the reading was being taken on the brightest part of the disc.
|
||||
|
||||
### What is deliberately not fixed
|
||||
|
||||
APCA asks Lc 90 of anything at 12px/400 or below, and this page is full of 10px and 11px
|
||||
labels that live inside a 58px seat puck. Fifty-five pieces of text are still below that
|
||||
bar. Reaching it means white micro-labels and a flatter, shoutier page; the honest way to
|
||||
clear it is bigger type, and bigger type is a layout change that would relitigate the
|
||||
seat-crowding work above. So the sweep reports two numbers — the strict per-size level,
|
||||
and the Lc 60 / Lc 75 floors this page actually holds itself to — and only the floors are
|
||||
treated as pass/fail.
|
||||
|
||||
## The failures that only happen at the table
|
||||
|
||||
Everything above this line is about a tool being easy to read and quick to press. This
|
||||
|
||||
Reference in New Issue
Block a user