diff --git a/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml b/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml index dbd7cbf..4e368db 100644 --- a/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml +++ b/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml @@ -24,7 +24,10 @@
- + @* 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. *@ +
diff --git a/JoshHeaps.Net/wwwroot/css/casino/holdem.css b/JoshHeaps.Net/wwwroot/css/casino/holdem.css index 9b53a95..33eaadb 100644 --- a/JoshHeaps.Net/wwwroot/css/casino/holdem.css +++ b/JoshHeaps.Net/wwwroot/css/casino/holdem.css @@ -3312,10 +3312,29 @@ body.hasActionBar #holdemApp { 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 { - background: linear-gradient(180deg, #2b3f57, #1b2a3a); - border-color: #3d5f83; - color: #dceaf8; + background: linear-gradient(180deg, #16783a, #0d5225); + border-color: #1e8449; + color: #ecffef; } /* ---------- Raise pad ---------- */ diff --git a/tests/holdem/uiAudit.js b/tests/holdem/uiAudit.js index 5b033df..4bb7662 100644 --- a/tests/holdem/uiAudit.js +++ b/tests/holdem/uiAudit.js @@ -1018,6 +1018,77 @@ const LayoutAudit = { 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. // // Each of the eight
in the sidebar now carries the one fact you would @@ -1428,6 +1499,7 @@ const LayoutAudit = { this.oneDealerButton(), this.hiddenButPainted(), this.emptyDecoration(), + this.oneNextStep(), this.glancesFitTheirRows(), this.seatLabelsClear(), this.seatsKeepTheirText(),