diff --git a/JoshHeaps.Net/wwwroot/css/casino/holdem.css b/JoshHeaps.Net/wwwroot/css/casino/holdem.css index 87379f9..57b44ee 100644 --- a/JoshHeaps.Net/wwwroot/css/casino/holdem.css +++ b/JoshHeaps.Net/wwwroot/css/casino/holdem.css @@ -510,13 +510,53 @@ button, /* 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. */ +/* 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 { - 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 { border-color: rgba(224, 123, 123, 0.4); - opacity: 0.5; } .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. */ .actionUndoKey { + /* See .actionKey below for why these are not fainter. */ margin-left: 0.4rem; padding: 0 0.25rem; border-radius: 4px; border: 1px solid currentColor; - opacity: 0.55; + opacity: 0.75; font-family: inherit; font-size: 0.6rem; line-height: 1.4; @@ -2181,6 +2222,10 @@ body.hasActionBar #holdemSidebar { 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 { position: absolute; top: 5px; @@ -2188,7 +2233,7 @@ body.hasActionBar #holdemSidebar { padding: 0 0.25rem; border-radius: 4px; border: 1px solid currentColor; - opacity: 0.4; + opacity: 0.75; font-family: inherit; font-size: 0.6rem; line-height: 1.4; diff --git a/tests/holdem/uiAudit.js b/tests/holdem/uiAudit.js index 27d2d03..98b270d 100644 --- a/tests/holdem/uiAudit.js +++ b/tests/holdem/uiAudit.js @@ -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 } = {}) { const offenders = []; const unmeasured = []; @@ -365,7 +375,21 @@ const LayoutAudit = { if (rect.width === 0 || rect.height === 0) return; const style = getComputedStyle(el); 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 // 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