Compare commits

...
Author SHA1 Message Date
jheapsandClaude Opus 5 c4e842f3e3 Stop the button's chip sitting on top of the name underneath it
A phone screenshot, mid-hand, eight-handed: the seat with the button reads
"Christop" and then a white D. The name is centred across the whole puck and the
chip sits in its top-right corner on a layer above, so a long name runs
underneath and stops — measured, "Christopher" lost 20px of its 64px box.

The bad part is the silence. The text believed it had the room it was being
painted out of, so no ellipsis appeared: nothing on screen said the name went on.
On the one seat at the table a dealer looks at most.

The name now keeps out of the chip's corner on the seat that has the button, so a
long one ends in an ellipsis before it gets there. Only that seat — every other
puck has its full width, because the chip on those is invisible.

And the audit has a check for it now. Nothing it had would have caught this: the
occlusion check is about controls a thumb has to reach, and this is a label a
dealer has to read. The new one measures every seat's name against its own chip
and badges, and the fix was undone by hand to confirm it fires — "20px under
button.dealerChip.active".

182 audits across 13 viewports, both input kinds, plain and straddled tables,
every overlay: clean.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
2026-09-02 09:52:00 -06:00
jheapsandClaude Opus 5 8616636212 Make hidden mean hidden, and check that it does
Three rounds inside the money, so this one was a look at the thing. On a desktop
screenshot of a table mid-session there is a small empty box under the bank
check: 21 pixels wide, 44 tall, no text, no purpose. It is the "Take $600 from 3
players" button from three rounds ago, which sets `hidden` when there is nothing
to record — and `hidden` was losing.

The attribute carries its `display: none` from the browser's own stylesheet,
which is the weakest rule in the cascade, so any class that sets a display beats
it. Every button class here sets `inline-flex`. This is not a new discovery in
this file either: seven overlays already carry their own `[hidden] { display:
none }`, each one presumably found the same way. One global rule now covers all
of them, so there is no ninth.

The audit had been telling me. Its touch check reported the box as "could not be
probed (drawn 21x44)" — in the warnings, which my sweeps printed only sometimes
and never failed on. So the check now says the plainer thing: an element that
claims to be hidden and is drawn anyway is a fault, not a note. It is in runAll
with the rest, and the bug was put back by hand to confirm it fires.

The same warning stream had a second thing in it: the waiting list's Add button
was 43px wide. Three letters, one pixel under the rule, and a thumb doesn't know
the word is short.

182 audits across 13 viewports, both input kinds, every overlay and a table with
a rake, a cash-out, a waiting list and six logged hands on it: clean, and the
only note left is the one that says the 44px rule stood itself down on a mouse.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
2026-09-02 09:36:51 -06:00
3 changed files with 91 additions and 0 deletions
@@ -318,6 +318,20 @@ button,
margin-top: 1rem;
}
/* `hidden` means hidden.
The attribute only carries `display: none` from the browser's own stylesheet, which
is the weakest rule there is: any class that sets a display — and every button here
sets `inline-flex` — beats it. That is not a hypothetical. Seven overlays in this
file already carry their own `[hidden] { display: none }` because each one was found
the hard way, and the eighth was a button that shipped as a 21x44 empty box under the
bank check on every table that didn't need it.
One rule, for everything, so there is no ninth. */
[hidden] {
display: none !important;
}
/* ---------- Poker table ---------- */
#pokerTableWrap {
@@ -608,6 +622,14 @@ button,
margin-top: 0.6rem;
}
/* The button's chip sits in this puck's top-right corner, on a layer above the text.
Reserving its width means a long name ends in an ellipsis before it reaches the
chip, rather than disappearing underneath one — on the one seat at the table a
dealer looks at most. */
.seat.seatHasButton .seatPuckName {
padding-right: 22px;
}
.seatPuckCards {
font-size: 0.68rem;
font-weight: 700;
@@ -2338,6 +2360,9 @@ body.hasActionBar #holdemSidebar {
.waitingAdd .holdemBtn {
flex: none;
min-height: 44px;
/* "Add" is three letters, which came out 43px wide — one pixel under the rule, and
the audit called it. A thumb doesn't know the word is short. */
min-width: 56px;
}
.waitingList {
@@ -408,6 +408,12 @@ const Render = {
const dealerChip = node.querySelector(".dealerChip");
const hasButton = index === dealerIndex;
dealerChip.classList.toggle("active", hasButton);
// Marked on the seat as well as the chip, so the puck's own text can keep
// out from under it. A name is centred across the whole puck and the chip
// sits in the corner on a layer above: measured, "Christopher" on the
// button lost 20px of its 64px box to the chip and showed no ellipsis,
// because the text believed it had the room it was painted out of.
node.classList.toggle("seatHasButton", hasButton);
dealerChip.classList.toggle("hidden", !!this.state.dedicatedDealer);
// Every one of these announced itself as "D", so a screen reader heard a row
// of nine identical buttons and couldn't tell which seat held the real one.
+60
View File
@@ -719,6 +719,64 @@ const LayoutAudit = {
return { name: "the felt shows one dealer button", offenders };
},
// Anything that says it is hidden and is drawn anyway.
//
// `hidden` gets its `display: none` from the browser's own stylesheet, the weakest
// rule in the cascade, so any class that sets a display beats it — and in this tool
// every button class does. The result is an element the code believes is gone,
// sitting on the screen: found as a 21x44 empty box under the bank check, and this
// file's own touch check had been calling it "could not be probed" rather than
// saying the plainer thing, which is that it should not be there at all.
hiddenButPainted() {
const offenders = [...document.querySelectorAll("[hidden]")]
.filter(el => {
const rect = el.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return false;
const style = getComputedStyle(el);
return style.display !== "none" && style.visibility !== "hidden";
})
.map(el => `${this.describe(el)} :: hidden, but drawn ${Math.round(el.getBoundingClientRect().width)}x${Math.round(el.getBoundingClientRect().height)}`);
return { name: "hidden means hidden", offenders };
},
// A seat's name, painted under the decoration on its own puck.
//
// The chip that marks the button sits in the puck's top-right corner on a layer
// above the text, and the name is centred across the whole width. A long one
// therefore ran underneath it and stopped — measured, "Christopher" on the button
// lost 20px of its 64px box and showed no ellipsis, because as far as the text was
// concerned it had all the room it needed. Nothing else here would have caught it:
// the occlusion check is about controls a thumb has to reach, and this is a label
// a dealer has to read.
seatLabelsClear() {
const offenders = [];
document.querySelectorAll(".seat").forEach(seat => {
const name = seat.querySelector(".seatPuckName");
if (!name || !name.textContent.trim()) return;
const box = name.getBoundingClientRect();
if (!box.width || !box.height) return;
// Padding is room the text has already been kept out of, so it is not
// somewhere the text can be covered.
const style = getComputedStyle(name);
const textLeft = box.left + parseFloat(style.paddingLeft || 0);
const textRight = box.right - parseFloat(style.paddingRight || 0);
seat.querySelectorAll(".dealerChip, .blindBadge").forEach(mark => {
const over = mark.getBoundingClientRect();
if (!over.width || !over.height) return;
const marked = getComputedStyle(mark);
if (marked.display === "none" || marked.visibility === "hidden" ||
parseFloat(marked.opacity) < 0.05) return;
const covered = Math.min(textRight, over.right) - Math.max(textLeft, over.left);
const vertical = Math.min(box.bottom, over.bottom) - Math.max(box.top, over.top);
if (covered > 1 && vertical > 1) {
offenders.push(`${this.describe(name)} :: ${Math.round(covered)}px under ${this.describe(mark)}`);
}
});
});
return { name: "seat names aren't painted under their own badges", offenders };
},
// ---- Runner ----
// Every check this file knows how to make, every time.
@@ -747,6 +805,8 @@ const LayoutAudit = {
this.fieldFontSize(),
this.seatOwnership(),
this.oneDealerButton(),
this.hiddenButPainted(),
this.seatLabelsClear(),
this.contrast(options.contrast),
this.touchTargets(options.minimumTarget, options.touchTargets),
];