Make the seats that are out of the hand readable again

A folded player's stack measured 2.22:1.

The contrast audit has been reporting clean for twenty rounds, and it never looked at
these seats: the check skipped anything with opacity below 0.99 as "faded on purpose",
and folded and sat-out seats faded their whole box -- 0.5 and 0.58. Opacity on a box
fades the text you still have to read along with the box you wanted to push back.

Measured off rendered pixels, 11.5px text, where AA asks 4.5:1:

                  name    stack
  live            13.28   4.75
  folded           4.75   2.22
  sitting out      7.00   3.22

A folded player's stack is not decoration. It is what they will have next hand and the
answer to "how much has Ben got?", and at 2.22 it was barely there.

So the box recedes and the text does not: a flatter, darker background and a muted
border, with name and stack colours that measure.

                  name    stack
  live            13.28   4.75   (unchanged)
  folded           7.60   4.55
  sitting out      7.60   4.71

Taking the fade away took the signal with it, which the screenshot showed and the
numbers did not: a sat-out seat became almost indistinguishable from a live one. A
folded seat still says so twice, with a red border and a line through the name, but
sitting out had only faintness. It wears a dashed edge now -- the same thing an empty
chair wears, which is right, because neither is in this hand, and the name and stack
tell the two apart.

The same fault, found once the audit stopped hiding it

The key hints on the action bar are 9.6px and were measuring 2.97 to 3.33 against
their buttons -- a fade chosen for looks pushing text a dealer is meant to read under
the threshold, exactly like the seats. They are 4.93 to 7.63 now, still quieter than
the labels they sit beside.

And the audit no longer passes over faded text in silence. Something invisible is still
exempt, since there is nothing to read at zero opacity, but anything in between is
reported as unmeasured -- a number nobody checked is not the same as a number that is
fine. It now lists five: the four key caps and the disabled Export CSV button, which is
disabled and therefore genuinely exempt.

269 tests still passing. Contrast offenders zero, layout audit clean, no console
errors; the folded seat's red line-through and the sat-out seat's dashed edge both
confirmed in cropped screenshots rather than assumed.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
This commit is contained in:
2026-09-01 19:10:06 -06:00
co-authored by Claude Opus 5
parent 95fd46ee99
commit f0cfd24647
2 changed files with 74 additions and 5 deletions
+49 -4
View File
@@ -510,13 +510,53 @@ button,
/* Sitting out, not gone: still a person the dealer may need to deal back in, so /* Sitting out, not gone: still a person the dealer may need to deal back in, so
the name has to stay readable rather than fading almost to the felt. */ the name has to stay readable rather than fading almost to the felt. */
/* Out of the hand, still readable.
Both of these used to fade the whole seat — opacity 0.5 folded, 0.58 sitting out —
and opacity on a box fades the text you still have to read along with the box you
wanted to push back. Measured off the rendered pixels at 11.5px text, where AA asks
for 4.5:1:
live name 13.28 stack 4.75
folded name 4.75 stack 2.22
sat out name 7.00 stack 3.22
A folded player's stack is not decoration. It's what they'll have next hand and the
answer to "how much has Ben got?", and at 2.22:1 it was barely there.
So the box recedes and the text doesn't: a flatter, darker background and a muted
border say "not in this hand", while the name and the stack keep colours that
measure. The folded seat keeps its red border and the line through the name, which
is the part that actually names the state. */
.seat.seatInactive,
.seat.seatFolded {
background: linear-gradient(160deg, #232323, #171717);
border-color: rgba(255, 255, 255, 0.06);
}
/* A folded seat already says so twice — a red border and a line through the name — but
a seat that's sitting out said it only by being faint, and taking the faintness away
took the whole signal with it. A dashed edge is the same thing an empty chair wears,
which is right: neither is in this hand. The name and the stack tell them apart. */
.seat.seatInactive { .seat.seatInactive {
opacity: 0.58; border-style: dashed;
border-color: rgba(255, 255, 255, 0.22);
}
.seat.seatInactive .seatPuckName,
.seat.seatFolded .seatPuckName {
color: #b0b0b0;
}
.seat.seatInactive .seatPuckStack,
.seat.seatFolded .seatPuckStack,
.seat.seatInactive .seatPuckTotal,
.seat.seatFolded .seatPuckTotal {
color: #8f8f8f;
} }
.seat.seatFolded { .seat.seatFolded {
border-color: rgba(224, 123, 123, 0.4); border-color: rgba(224, 123, 123, 0.4);
opacity: 0.5;
} }
.seat.seatFolded .seatPuckName { .seat.seatFolded .seatPuckName {
@@ -2169,11 +2209,12 @@ body.hasActionBar #holdemSidebar {
/* The undo pill's key, inline rather than pinned to a corner — see actionBar.js. */ /* The undo pill's key, inline rather than pinned to a corner — see actionBar.js. */
.actionUndoKey { .actionUndoKey {
/* See .actionKey below for why these are not fainter. */
margin-left: 0.4rem; margin-left: 0.4rem;
padding: 0 0.25rem; padding: 0 0.25rem;
border-radius: 4px; border-radius: 4px;
border: 1px solid currentColor; border: 1px solid currentColor;
opacity: 0.55; opacity: 0.75;
font-family: inherit; font-family: inherit;
font-size: 0.6rem; font-size: 0.6rem;
line-height: 1.4; line-height: 1.4;
@@ -2181,6 +2222,10 @@ body.hasActionBar #holdemSidebar {
pointer-events: none; pointer-events: none;
} }
/* A key hint is small — 9.6px — so AA asks 4.5:1 of it, and at the opacity these used
to carry they measured 2.97 to 3.33 against their buttons. That's the same fault as
the folded seats above: a fade chosen for looks pushing text a dealer is meant to
read under the threshold. They're still quieter than the label they sit beside. */
.actionKey { .actionKey {
position: absolute; position: absolute;
top: 5px; top: 5px;
@@ -2188,7 +2233,7 @@ body.hasActionBar #holdemSidebar {
padding: 0 0.25rem; padding: 0 0.25rem;
border-radius: 4px; border-radius: 4px;
border: 1px solid currentColor; border: 1px solid currentColor;
opacity: 0.4; opacity: 0.75;
font-family: inherit; font-family: inherit;
font-size: 0.6rem; font-size: 0.6rem;
line-height: 1.4; line-height: 1.4;
+25 -1
View File
@@ -349,6 +349,16 @@ const LayoutAudit = {
}); });
}, },
// Every element between this one and the document, so an opacity set on a
// container can be seen from the text inside it.
*ancestors(el) {
let node = el.parentElement;
while (node) {
yield node;
node = node.parentElement;
}
},
contrast({ minimum = 4.5, largeMinimum = 3, selector = null } = {}) { contrast({ minimum = 4.5, largeMinimum = 3, selector = null } = {}) {
const offenders = []; const offenders = [];
const unmeasured = []; const unmeasured = [];
@@ -365,7 +375,21 @@ const LayoutAudit = {
if (rect.width === 0 || rect.height === 0) return; if (rect.width === 0 || rect.height === 0) return;
const style = getComputedStyle(el); const style = getComputedStyle(el);
if (style.visibility === "hidden" || style.display === "none") return; if (style.visibility === "hidden" || style.display === "none") return;
if (parseFloat(style.opacity) < 0.99) return; // faded on purpose // Faded text used to be skipped outright as "faded on purpose", and that
// exemption hid a real fault: the folded and sat-out seats dimmed their
// whole box with opacity, so a folded player's stack measured 2.22:1 at
// 11.5px and this check never looked at it. Something invisible is still
// exempt — there's nothing to read at zero — but anything in between is
// reported as unmeasured rather than passed over in silence, because a
// number nobody checked is not the same as a number that's fine.
const opacity = parseFloat(style.opacity);
if (opacity <= 0.02) return;
const faded = opacity < 0.99 ||
[...this.ancestors(el)].some(a => parseFloat(getComputedStyle(a).opacity) < 0.99);
if (faded) {
unmeasured.push(`${this.describe(el)} :: faded (opacity ${opacity}), contrast not checked`);
return;
}
// Decoration is exempt by definition: the watermark on the felt is meant // Decoration is exempt by definition: the watermark on the felt is meant
// to be barely there, and holding it to a reading threshold would mean // to be barely there, and holding it to a reading threshold would mean
// making it not a watermark. Marked aria-hidden so screen readers skip it // making it not a watermark. Marked aria-hidden so screen readers skip it