Spend the accent on the one thing to do next, not on two at once
The green is a promise: this is the step. Measured over a whole hand — seventeen steps — it was on two controls at thirteen of them, the bar's Call and the header's Resolve Pot painting the identical gradient at opposite ends of a phone, and on the header alone at the other four while the bar held the only button there was to press. At showdown the most consequential tap in the hand, "Showdown — resolve pot", was blue-grey while the corner shortcut was green. So the accent moves to .actionAdvance, which is always the next step by definition, and comes off the header shortcut, which never is. Twenty-two measured steps of a real hand now show exactly one. The sweep checks it: oneNextStep() counts the accent-coloured controls that are actually on screen, scoped to an open sheet when there is one. Six broken versions were run at it, and the two that took three goes to catch were both the same mistake in the injection rather than a hole in the check — a button painted green with the panel shut isn't rendered, and one painted green below the fold is deliberately ignored. Scrolled into view it is named alongside the bar's Call, and goes clean again when the green comes off. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
This commit is contained in:
@@ -24,7 +24,10 @@
|
|||||||
<div id="holdemHeaderActions">
|
<div id="holdemHeaderActions">
|
||||||
<button id="misdealBtn" class="holdemBtn" title="Take this hand back — every chip returns to the player who put it in, and the same button deals again" hidden>Misdeal</button>
|
<button id="misdealBtn" class="holdemBtn" title="Take this hand back — every chip returns to the player who put it in, and the same button deals again" hidden>Misdeal</button>
|
||||||
<button id="nextHandBtn" class="holdemBtn" title="Move the button, small blind and big blind to the next occupied seat">Next Hand ⟳</button>
|
<button id="nextHandBtn" class="holdemBtn" title="Move the button, small blind and big blind to the next occupied seat">Next Hand ⟳</button>
|
||||||
<button id="resolvePotBtn" class="holdemBtn holdemBtnAccent">Resolve Pot</button>
|
@* Not the accent any more. It is a shortcut to a sheet, available all night,
|
||||||
|
and it is never the next step — the bar always has that, and at showdown
|
||||||
|
the bar says so in as many words. See .actionAdvance in holdem.css. *@
|
||||||
|
<button id="resolvePotBtn" class="holdemBtn">Resolve Pot</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -3312,10 +3312,29 @@ body.hasActionBar #holdemApp {
|
|||||||
color: var(--gold-bright);
|
color: var(--gold-bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Deal Flop, Deal Turn, Deal River, "Showdown — resolve pot", "Award pot to Dee".
|
||||||
|
Every one of them is the only thing the bar is offering at the moment it appears, and
|
||||||
|
every one of them was the quietest control on the screen.
|
||||||
|
|
||||||
|
Measured over a whole hand, seventeen steps: the accent green was on two controls at
|
||||||
|
thirteen of them — the bar's Call and the header's Resolve Pot, painted the identical
|
||||||
|
gradient at opposite ends of a phone — and at the other four it was on the header
|
||||||
|
alone while the bar held the only button there was to press. At showdown that is as
|
||||||
|
wrong as it gets: "Showdown — resolve pot" is the most consequential tap in the hand
|
||||||
|
and it was blue-grey, while a shortcut in the corner that had looked exactly the same
|
||||||
|
for the previous five minutes was green.
|
||||||
|
|
||||||
|
So the accent goes where the next step is. The same green as Call, deliberately,
|
||||||
|
because the rule worth learning is "green is the thing to do next" and not "green is
|
||||||
|
a player's action" — and the two can never be confused, because they never appear
|
||||||
|
together: renderStreetDone and renderHandOver each build their own row and neither
|
||||||
|
ever renders alongside the fold/call/raise triad. Checked over a full hand, and
|
||||||
|
checked again by the audit on every viewport. The fold red and the raise gold are
|
||||||
|
untouched, and the labels and the shortcut glyph still tell them apart. */
|
||||||
.actionAdvance {
|
.actionAdvance {
|
||||||
background: linear-gradient(180deg, #2b3f57, #1b2a3a);
|
background: linear-gradient(180deg, #16783a, #0d5225);
|
||||||
border-color: #3d5f83;
|
border-color: #1e8449;
|
||||||
color: #dceaf8;
|
color: #ecffef;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Raise pad ---------- */
|
/* ---------- Raise pad ---------- */
|
||||||
|
|||||||
@@ -1018,6 +1018,77 @@ const LayoutAudit = {
|
|||||||
return { name: "nothing on the felt is painted for nothing", offenders };
|
return { name: "nothing on the felt is painted for nothing", offenders };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// One green, and it is the thing to do next.
|
||||||
|
//
|
||||||
|
// The accent is a promise: this is the step. It only works while it is spent on one
|
||||||
|
// control at a time, and it was being spent on two. Measured over a whole hand,
|
||||||
|
// seventeen steps, before this went in: two accent controls at thirteen of them —
|
||||||
|
// the bar's Call and the header's Resolve Pot, the identical gradient at opposite
|
||||||
|
// ends of a phone — and at the other four the header alone, while the bar held the
|
||||||
|
// only button there was to press. At showdown the bar's own "Showdown — resolve
|
||||||
|
// pot" was blue-grey and the corner shortcut was green.
|
||||||
|
//
|
||||||
|
// A modal is its own view, so while one is open only what is inside it is judged —
|
||||||
|
// a wizard's confirm is the primary action of the wizard, and the bar underneath it
|
||||||
|
// is not on screen in any sense that matters. That is the same scoping rule the
|
||||||
|
// occlusion check uses, and it comes from the same helper.
|
||||||
|
//
|
||||||
|
// Zero is not a failure. Between hands with nobody seated there is genuinely no next
|
||||||
|
// step, and inventing one to keep a colour company would be worse than the silence.
|
||||||
|
//
|
||||||
|
// Six broken versions were run at it and six were caught, but two of them only after
|
||||||
|
// the injection itself was fixed, and both failures were the same mistake: painting
|
||||||
|
// a control green is not the same as putting a green control in front of anybody.
|
||||||
|
// The first attempt turned every sidebar button green with the panels shut, so the
|
||||||
|
// rule landed on nothing that was rendered — `getComputedStyle` reported the accent
|
||||||
|
// gradient and `offsetParent` was still null. The second opened the panels first,
|
||||||
|
// which rendered the button and left it four hundred pixels below the fold, where
|
||||||
|
// this check is meant to ignore it. Scrolled into view, the same green "Add" is
|
||||||
|
// named in the same breath as the bar's Call, and taking the green off goes clean
|
||||||
|
// again. So: read back the computed paint AND the box, on both counts, before
|
||||||
|
// believing a clean result means the check is looking.
|
||||||
|
ACCENT_FILL: [22, 120, 58],
|
||||||
|
ACCENT_TOLERANCE: 24,
|
||||||
|
|
||||||
|
wearsTheAccent(el) {
|
||||||
|
const cs = getComputedStyle(el);
|
||||||
|
const paint = `${cs.backgroundColor} ${cs.backgroundImage}`;
|
||||||
|
return [...paint.matchAll(/rgba?\(([^)]+)\)/g)]
|
||||||
|
.map(m => m[1].split(",").map(Number))
|
||||||
|
.some(([r, g, b, a]) => (a === undefined || a > 0.5) &&
|
||||||
|
Math.abs(r - this.ACCENT_FILL[0]) <= this.ACCENT_TOLERANCE &&
|
||||||
|
Math.abs(g - this.ACCENT_FILL[1]) <= this.ACCENT_TOLERANCE &&
|
||||||
|
Math.abs(b - this.ACCENT_FILL[2]) <= this.ACCENT_TOLERANCE);
|
||||||
|
},
|
||||||
|
|
||||||
|
oneNextStep() {
|
||||||
|
const scope = this.modalScope();
|
||||||
|
const root = scope || document;
|
||||||
|
const onScreen = el => {
|
||||||
|
const r = el.getBoundingClientRect();
|
||||||
|
if (!r.width || !r.height) return false;
|
||||||
|
const cs = getComputedStyle(el);
|
||||||
|
if (cs.visibility === "hidden" || +cs.opacity === 0) return false;
|
||||||
|
return r.bottom > 0 && r.top < innerHeight && r.right > 0 && r.left < innerWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
const green = [...root.querySelectorAll("button, a.holdemBtn")]
|
||||||
|
.filter(el => el.offsetParent !== null && onScreen(el) && this.wearsTheAccent(el));
|
||||||
|
|
||||||
|
if (green.length <= 1) {
|
||||||
|
return { name: "one green, and it is the next step", offenders: [] };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: "one green, and it is the next step",
|
||||||
|
offenders: [`${green.length} controls wear the accent at once` +
|
||||||
|
`${scope ? " inside the open sheet" : ""}: ` +
|
||||||
|
green.map(el => `"${(el.textContent || "").trim().slice(0, 24)}" in ` +
|
||||||
|
`${el.closest("#holdemHeader") ? "the header"
|
||||||
|
: el.closest("#actionBar") ? "the action bar"
|
||||||
|
: el.closest("#holdemSidebar") ? "the sidebar" : "the page"}`).join(", ")],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
// The status a shut sidebar panel wears, and the three ways it can go wrong.
|
// The status a shut sidebar panel wears, and the three ways it can go wrong.
|
||||||
//
|
//
|
||||||
// Each of the eight <details> in the sidebar now carries the one fact you would
|
// Each of the eight <details> in the sidebar now carries the one fact you would
|
||||||
@@ -1428,6 +1499,7 @@ const LayoutAudit = {
|
|||||||
this.oneDealerButton(),
|
this.oneDealerButton(),
|
||||||
this.hiddenButPainted(),
|
this.hiddenButPainted(),
|
||||||
this.emptyDecoration(),
|
this.emptyDecoration(),
|
||||||
|
this.oneNextStep(),
|
||||||
this.glancesFitTheirRows(),
|
this.glancesFitTheirRows(),
|
||||||
this.seatLabelsClear(),
|
this.seatLabelsClear(),
|
||||||
this.seatsKeepTheirText(),
|
this.seatsKeepTheirText(),
|
||||||
|
|||||||
Reference in New Issue
Block a user