Hold'em: every seat on the ring is the same seat, and the ring is measured before it is believed #122

Open
jheaps wants to merge 27 commits from polish/holdem-seats-one-size into master
3 changed files with 210 additions and 20 deletions
Showing only changes of commit 382238e708 - Show all commits
+80 -20
View File
@@ -90,6 +90,32 @@
--gold-deep: #a9782f; --gold-deep: #a9782f;
--gold-tint: #3a2f18; --gold-tint: #3a2f18;
--gold-shade: #241d0f; --gold-shade: #241d0f;
/* The page is #141414 and never said so.
Everything above is a colour this stylesheet paints. `color-scheme` is the half
the stylesheet cannot paint: the browser draws the checkboxes, the dropdown a
<select> opens, the scrollbars, the caret, the spellcheck underline and the
autofill wash itself, and with no scheme declared it draws all of them for a
light page. Measured before this line: `color-scheme: normal`, three checkboxes
rendering as bright white squares in the Table Setup panel — the loudest objects
on a near-black screen, brighter than the gold — and a <select> whose menu opens
white over the felt.
One declaration moves every one of them, and it moves them *natively*, which is
the part worth having: a control the browser draws still answers Windows High
Contrast Mode, still matches the platform a dealer already knows, and cannot
lose its tick to a stylesheet. See .potWizardChoice input for what painting one
by hand costs, and what it has to pay back.
--accent-on is the checked colour, and it is the green this tool already uses for
yes: the same #1e8449 the pot wizard's hand-drawn box fills with, so the one
painted checkbox and the six native ones agree. Chrome picks the tick's own
colour from it, and against a green this dark that is white. */
--accent-on: #1e8449;
color-scheme: dark;
accent-color: var(--accent-on);
} }
/* Every number that changes while the dealer is looking at it — the pot, the stacks, /* Every number that changes while the dealer is looking at it — the pot, the stacks,
@@ -1974,8 +2000,13 @@ button,
/* A bare checkbox ships at 13x13, which misses the 24px minimum with a mouse as well /* A bare checkbox ships at 13x13, which misses the 24px minimum with a mouse as well
as with a thumb. The row around it is the real target on touch, but the box itself as with a thumb. The row around it is the real target on touch, but the box itself
should be findable on any device. */ should be findable on any device.
#dedicatedDealerToggle {
This said `#dedicatedDealerToggle` and fixed one box of six. The other five were
still 13x13 — measured — including the two in the seat sheet that a dealer uses far
more often than any setting. Said about checkboxes instead, since that is what the
sentence above is about. */
input[type="checkbox"] {
width: 24px; width: 24px;
height: 24px; height: 24px;
} }
@@ -2888,7 +2919,13 @@ button,
gap: var(--sp-2); gap: var(--sp-2);
} }
/* `flex: none` here for the same reason the coarse-pointer block already says it: a
stated width is only a suggestion to a flex item, and this one sits in a crowded row.
It was declared 32px wide and measured 22 with a mouse — under the 24px floor the
checkbox rule near the top of this file is written to clear, and squeezed by exactly
the mechanism that block had already been bitten by once. */
.chipColorInput { .chipColorInput {
flex: none;
width: 32px; width: 32px;
height: 28px; height: 28px;
background: none; background: none;
@@ -4554,17 +4591,23 @@ body.hasActionBar #toastHost {
} }
} }
/* The last raw browser control in the tool. /* The one checkbox in the tool that is drawn rather than asked for.
`accent-color` only paints the tick; the box around it stayed the platform's own — The comment here called it "the last raw browser control", which was not true when
a bright white square, which on this near-black dialog was the loudest thing on it was written — six more were still the platform's, and they were white because the
screen and read as unfinished next to controls that had all been drawn. Painted to page had never declared `color-scheme`. That is fixed at the top of this file, and
match the rest, on the same surface ramp and the same corner radius. */ fixing it there is what makes this one the exception rather than the rule.
It stays painted because it is not a setting: it is the control a dealer ticks to
move money, with players watching, and it wants a fill and a tick that read across a
table rather than the platform's 24px square. What painting costs is the browser's
own answer to forced-colors mode, which is bought back below.
It takes its size from the checkbox rule near the top rather than restating it, so
there is one number for how big a checkbox is here. */
.potWizardChoice input { .potWizardChoice input {
flex: none; flex: none;
appearance: none; appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
width: 22px;
height: 22px;
border: 2px solid var(--line-strong); border: 2px solid var(--line-strong);
border-radius: var(--r-sm); border-radius: var(--r-sm);
background: var(--surface-2); background: var(--surface-2);
@@ -4602,6 +4645,34 @@ body.hasActionBar #toastHost {
} }
} }
/* What the paint job owes back.
In forced-colors mode the OS replaces every colour on the page with its own palette,
and a tick drawn as a `background` on a pseudo-element is not a colour it knows how
to replace — it is simply gone. So this box, alone among the tool's checkboxes, had
no visible checked state at all in Windows High Contrast Mode, on the dialog that
pays the table. The system keywords are the fix: they are the only colours
guaranteed to survive, because they *are* the palette. */
@media (forced-colors: active) {
.potWizardChoice input {
border-color: ButtonBorder;
background: Canvas;
}
.potWizardChoice input::before {
background: CanvasText;
}
.potWizardChoice input:checked {
border-color: Highlight;
background: Canvas;
}
/* The row's own chosen state is a background gradient, which goes the same way. */
.potWizardChoice.chosen {
outline: 2px solid Highlight;
}
}
/* Selected has to be obvious at a glance — a dealer confirms this with players /* Selected has to be obvious at a glance — a dealer confirms this with players
watching, and a checkbox alone is easy to misread across a table. */ watching, and a checkbox alone is easy to misread across a table. */
.potWizardChoice.chosen { .potWizardChoice.chosen {
@@ -5213,12 +5284,6 @@ body.hasActionBar #toastHost {
min-height: 44px; min-height: 44px;
} }
/* A bare checkbox with no label box around it to grow. */
#dedicatedDealerToggle {
width: 24px;
height: 24px;
}
/* An underlined word is a 14px-tall target; give it room without making it look /* An underlined word is a 14px-tall target; give it room without making it look
like a button. */ like a button. */
.linkBtn { .linkBtn {
@@ -6047,11 +6112,6 @@ textarea:focus-visible {
min-height: 44px; min-height: 44px;
} }
#dedicatedDealerToggle {
width: 24px;
height: 24px;
}
/* An underlined word is a 14px-tall target; give it room without making it look /* An underlined word is a 14px-tall target; give it room without making it look
like a button. */ like a button. */
.linkBtn { .linkBtn {
@@ -1,6 +1,15 @@
/* Quick Split is phone-first: sized for one thumb at the table, then allowed to /* Quick Split is phone-first: sized for one thumb at the table, then allowed to
breathe on a laptop. Palette matches the dealer table so the two read as one tool. */ breathe on a laptop. Palette matches the dealer table so the two read as one tool. */
/* And matches it in the half a stylesheet does not paint, for the same reason: this
page is #0a0a0a and had never told the browser so, which leaves the scrollbars, the
caret, the selection and the autofill wash drawn for a light page. It has no
checkboxes to go white, but a dealer who opens it from the felt should not find a
light scrollbar down the side of the darker of the two pages. */
:root {
color-scheme: dark;
}
body { body {
background: radial-gradient(ellipse at 50% -10%, #1c1c1c 0%, #101010 55%, #0a0a0a 100%); background: radial-gradient(ellipse at 50% -10%, #1c1c1c 0%, #101010 55%, #0a0a0a 100%);
min-height: 100vh; min-height: 100vh;
+121
View File
@@ -0,0 +1,121 @@
// The half of the interface the stylesheet does not draw.
//
// The browser draws the checkboxes, the menu a <select> opens, the scrollbars, the
// caret, the spellcheck underline and the autofill wash, and it draws all of them for a
// light page unless the page says otherwise. This one never did: measured,
// `color-scheme: normal` on a #141414 ground, with three checkboxes in the Table Setup
// panel rendering as bright white squares — the loudest objects on the screen, brighter
// than the gold — and two more in the seat sheet doing the same over the felt.
//
// The comment on the pot wizard's hand-painted checkbox called it "the last raw browser
// control in the tool". Six others were still the platform's, and three of those were
// still 13x13 against a 24px floor this stylesheet had already written down once and
// applied to exactly one box.
//
// Painting is the expensive way to fix that, and the pot wizard's box shows the bill:
// a tick drawn as a `background` on a pseudo-element is not a colour forced-colors mode
// knows how to replace, so it simply disappears — no visible checked state at all, on
// the dialog that pays the table. Declaring the scheme fixes six controls natively and
// costs nothing; the seventh stays painted on purpose and pays its own way.
const test = require("node:test");
const { describe } = require("node:test");
const assert = require("node:assert");
const fs = require("fs");
const path = require("path");
const CSS = fs.readFileSync(
path.join(__dirname, "..", "..", "JoshHeaps.Net", "wwwroot", "css", "casino", "holdem.css"), "utf8");
const ruleFor = selector => {
const at = CSS.indexOf(`\n${selector} {`);
assert.ok(at >= 0, `there is no \`${selector}\` rule`);
return CSS.slice(at, CSS.indexOf("}", at));
};
// The body of a whole at-rule block, braces balanced.
const atRule = query => {
const at = CSS.indexOf(`@media ${query} {`);
assert.ok(at >= 0, `there is no \`@media ${query}\` block`);
let depth = 0;
for (let i = CSS.indexOf("{", at); i < CSS.length; i++) {
if (CSS[i] === "{") depth++;
else if (CSS[i] === "}" && --depth === 0) return CSS.slice(at, i + 1);
}
assert.fail(`\`@media ${query}\` never closes`);
};
// ---------------------------------------------------------------------------
describe("the page says what colour it is", () => {
const root = ruleFor(":root");
test("dark, to the browser and not only to the eye", () => {
assert.match(root, /color-scheme:\s*dark/);
});
// The checked colour is the green this tool already uses for yes, so the one
// painted checkbox and the six native ones agree rather than nearly agree.
test("and the checked colour is the tool's own green, not the platform's blue", () => {
assert.match(root, /--accent-on:\s*#1e8449/);
assert.match(root, /accent-color:\s*var\(--accent-on\)/);
assert.match(ruleFor(".potWizardChoice input:checked"), /background:\s*#1e8449/);
});
});
describe("a checkbox is one size", () => {
test("every one of them, not the one that was noticed", () => {
assert.match(ruleFor('input[type="checkbox"]'), /width:\s*24px/);
assert.match(ruleFor('input[type="checkbox"]'), /height:\s*24px/);
});
test("and no box carries a size of its own any more", () => {
assert.ok(!/#dedicatedDealerToggle\s*\{/.test(CSS),
"one checkbox is still sized by id, which is how five got left behind");
assert.ok(!/width:\s*22px/.test(ruleFor(".potWizardChoice input")),
"the painted box restates its size instead of taking the one above");
});
// A stated width is only a suggestion to a flex item. The coarse-pointer block had
// already been bitten by this and said so; the mouse case had not.
test("a swatch in a crowded row keeps the width it was given", () => {
assert.match(ruleFor(".chipColorInput"), /flex:\s*none/);
});
});
describe("what painting a control costs, and who pays it", () => {
// If a future round paints another one, this is the bill it has to settle too.
test("nothing gives up the platform's drawing without answering forced colours", () => {
const forced = atRule("(forced-colors: active)");
const painted = [...CSS.matchAll(/\n([^\n{}]+)\s*\{[^}]*appearance:\s*none/g)]
.map(m => m[1].trim());
assert.ok(painted.length > 0, "nothing is painted, so this test is measuring nothing");
painted.forEach(selector => {
assert.ok(forced.includes(selector),
`\`${selector}\` drops the platform's control but has no forced-colors answer`);
});
});
// A tick drawn as a background is gone in forced-colors mode; a system keyword is
// the only colour guaranteed to survive, because it *is* the palette.
test("the tick that is drawn rather than typed survives the OS repainting it", () => {
const forced = atRule("(forced-colors: active)");
assert.match(forced, /\.potWizardChoice input::before\s*\{[^}]*background:\s*CanvasText/);
});
test("and so does the row's own chosen state, which is a gradient", () => {
const forced = atRule("(forced-colors: active)");
assert.match(forced, /\.potWizardChoice\.chosen\s*\{[^}]*outline:\s*2px solid Highlight/);
});
});
describe("the other page of the same tool", () => {
const QUICKSPLIT = fs.readFileSync(
path.join(__dirname, "..", "..", "JoshHeaps.Net", "wwwroot", "css", "casino", "quicksplit.css"), "utf8");
// It is the darker of the two pages and is opened from the felt mid-hand. It has no
// checkboxes to go white, but scrollbars, caret and selection were still drawn for
// a light page.
test("Quick Split says what colour it is too", () => {
assert.match(QUICKSPLIT, /:root\s*\{[^}]*color-scheme:\s*dark/);
});
});