Reported: "when I change who the blind is, it acts like I want to start the round."
The seat menu's blind actions reposted whenever Betting.onlyBlindsIn(state) was true — which means nothing has happened in this hand except the blinds, true of a hand one second old and equally true of a table in setup. So naming a blind ran repostBlinds -> beginHand: betting opened, two stacks docked, action bar up. Betting.blindsArePosted(state) is the question all three wanted.
2. A vocabulary for the stylesheet, and a bar that stops moving
holdem.css had 42 spacing values, 31 font sizes, 14 border-radii and 139 colours — and 3 custom properties. Constrained scales now: 7 spacing steps on an 8px rhythm, 8 type sizes, 4 corners, a 4-step surface ramp off pure black. 661 values rewritten; colour mentions 381 -> 174.
before
after
Undo pill width, one hand
148, 110, 109, 108, 109, 110, 120
160 throughout
Action bar height mid-hand
151 / 147 / 129
constant everywhere
Pot, stacks, standings
proportional digits
tabular figures
3. The felt gets its screen back
Measured on a 390x844 phone, six-handed, same table before and after:
before
after
header
193px
120px
felt starts at
225px
152px
controls on screen
27
18
bordered elements
37
22
text elements
61
49
Header: six equally weighted buttons in two rows, two of which are per-hand controls. The rest went where they belong rather than into a "more" menu, which relocates crowding instead of removing it — Reset Table into the Session panel beside Clear history (it throws the night away and was sitting at the same weight as Resolve Pot), Quick Split down with Common rulings, and the way back to the site as a quiet top link. The subtitle is gone; the landscape and short-phone rules already hid it.
Teaching copy, once: Table Setup no longer ships open — it opens only on a table nobody has set up, decided once at load so it can't reopen under a dealer who just shut it. The gesture hint goes after the first deal of the session, keyed to the hand number rather than bettingOpen, which would have made it appear and disappear twice a hand.
Borders: panels had a border and a lighter background and a shadow; the standings had a rule under every row. Both drop the line, keep the grouping. Fields and buttons keep theirs — those say "interactive", which isn't decoration.
The felt: names wrap to two lines instead of ellipsising, so "Christopher" and "Mike from work" are readable rather than "Chri..." and "Mike fro..." — the truncation was eating the half that tells two Mikes apart. A stack of $1,234,567 rendered at 7px; it now drops the thousands separators before the type size, so the same number is eight characters at 10px with every digit intact.
Tried and reverted, both measured
Reserving a second name line made every puck the height of the tallest (69-73px -> a uniform 88). Fifteen pixels on nine seats is felt spent on chrome.
Dropping the name a type step still didn't fit "Christopher" on the button seat, where the dealer chip takes 22px of the row — cost legibility, bought nothing.
Known limit
A single word too long for a puck breaks across the two lines and gets a hyphen only where the browser has hyphenation dictionaries. Real phones do; headless Chromium doesn't, so audit screenshots show "Christo / pher". The whole name is visible either way, which it wasn't before.
Validated
599 unit tests, 0 failures.designTokens.test.js fails if anyone types a raw value instead of reaching for a token — fault-injected four ways. aimingTheBlinds.test.js fault-injected: the old condition fails 6 of its 11.
52 layout audits across 13 viewports x 4 table sizes (2, 6, 9, 12 seats), 20 across the five overlays: 0 failures. The action bar doesn't change height mid-hand at any of them.
Three regressions caught by the sweeps and fixed: the Add Chips field dropping to 42px, the bar coming out 2px shorter in landscape and lifting the dealer button into the header, and the back link becoming a 56x13 touch target when it stopped being a button.
Reset Table verified working from its new home.
Three changes this session, all into `master`.
## 1. Naming a blind no longer deals the hand
Reported: *"when I change who the blind is, it acts like I want to start the round."*
The seat menu's blind actions reposted whenever `Betting.onlyBlindsIn(state)` was true — which means *nothing has happened in this hand except the blinds*, true of a hand one second old and equally true of a table in setup. So naming a blind ran `repostBlinds` -> `beginHand`: betting opened, two stacks docked, action bar up. `Betting.blindsArePosted(state)` is the question all three wanted.
## 2. A vocabulary for the stylesheet, and a bar that stops moving
`holdem.css` had **42 spacing values, 31 font sizes, 14 border-radii and 139 colours — and 3 custom properties.** Constrained scales now: 7 spacing steps on an 8px rhythm, 8 type sizes, 4 corners, a 4-step surface ramp off pure black. 661 values rewritten; colour mentions 381 -> 174.
| | before | after |
|---|---|---|
| Undo pill width, one hand | 148, 110, 109, 108, 109, 110, 120 | **160 throughout** |
| Action bar height mid-hand | 151 / 147 / 129 | **constant everywhere** |
| Pot, stacks, standings | proportional digits | **tabular figures** |
## 3. The felt gets its screen back
Measured on a 390x844 phone, six-handed, same table before and after:
| | before | after |
|---|---|---|
| header | 193px | **120px** |
| felt starts at | 225px | **152px** |
| controls on screen | 27 | **18** |
| bordered elements | 37 | **22** |
| text elements | 61 | **49** |
**Header:** six equally weighted buttons in two rows, two of which are per-hand controls. The rest went where they belong rather than into a "more" menu, which relocates crowding instead of removing it — Reset Table into the Session panel beside Clear history (it throws the night away and was sitting at the same weight as Resolve Pot), Quick Split down with Common rulings, and the way back to the site as a quiet top link. The subtitle is gone; the landscape and short-phone rules already hid it.
**Teaching copy, once:** Table Setup no longer ships open — it opens only on a table nobody has set up, decided once at load so it can't reopen under a dealer who just shut it. The gesture hint goes after the first deal of the session, keyed to the hand number rather than `bettingOpen`, which would have made it appear and disappear twice a hand.
**Borders:** panels had a border *and* a lighter background *and* a shadow; the standings had a rule under every row. Both drop the line, keep the grouping. Fields and buttons keep theirs — those say "interactive", which isn't decoration.
**The felt:** names wrap to two lines instead of ellipsising, so "Christopher" and "Mike from work" are readable rather than "Chri..." and "Mike fro..." — the truncation was eating the half that tells two Mikes apart. A stack of $1,234,567 rendered at **7px**; it now drops the thousands separators before the type size, so the same number is eight characters at **10px** with every digit intact.
### Tried and reverted, both measured
- Reserving a second name line made every puck the height of the tallest (69-73px -> a uniform 88). Fifteen pixels on nine seats is felt spent on chrome.
- Dropping the name a type step still didn't fit "Christopher" on the button seat, where the dealer chip takes 22px of the row — cost legibility, bought nothing.
### Known limit
A single word too long for a puck breaks across the two lines and gets a hyphen only where the browser has hyphenation dictionaries. Real phones do; headless Chromium doesn't, so audit screenshots show "Christo / pher". The whole name is visible either way, which it wasn't before.
### Validated
- **599 unit tests, 0 failures.** `designTokens.test.js` fails if anyone types a raw value instead of reaching for a token — fault-injected four ways. `aimingTheBlinds.test.js` fault-injected: the old condition fails 6 of its 11.
- **52 layout audits** across 13 viewports x 4 table sizes (2, 6, 9, 12 seats), **20** across the five overlays: 0 failures. The action bar doesn't change height mid-hand at any of them.
- Three regressions caught by the sweeps and fixed: the Add Chips field dropping to 42px, the bar coming out 2px shorter in landscape and lifting the dealer button into the header, and the back link becoming a 56x13 touch target when it stopped being a button.
- Reset Table verified working from its new home.
The seat menu's "Make small blind" / "Make big blind" / "Blinds follow the
button again" all re-posted the blinds whenever `Betting.onlyBlindsIn(state)`
was true. That condition means "nothing has happened in this hand except the
blinds" — which is true of a hand one second old and equally true of a table
sitting in setup with no cards out and nothing committed.
So naming a blind before the deal ran repostBlinds -> beginHand: betting
opened, two stacks docked, the action bar up and waiting on the seat after the
big blind. The dealer had said where the blinds go and the tool heard "deal".
Betting.blindsArePosted() is the question these three actually wanted — the
blinds are down only once a hand is open — and it's the guard makeDealer was
already using by hand. Before the deal, naming a blind now moves a badge and
nothing else; the toast says "Dee has the small blind next hand." rather than
claiming she posted it. Mid-hand behaviour is unchanged: chips come back and
go in again from the new positions, and the chip count stays straight.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
The tool read as crowded on screens where nothing overlapped, and the reason
turned out to be arithmetic rather than taste: holdem.css used 42 different
spacing values, 31 font sizes, 14 border-radii and 139 colours, and defined
three custom properties. Every number had been picked by hand at the moment it
was needed. A 0.42rem gap beside a 0.45rem gap beside a 0.5rem gap gives the
eye no rhythm to lock onto, so it reads the whole surface as noise.
So: constrained scales, and everything snapped to them. Seven spacing steps on
an 8px rhythm, eight type sizes, four corners, and a four-step surface ramp
that stays off pure black — shadows are invisible on #000 and white-on-black
smears on the OLED phone this is used on. 661 values rewritten; colour mentions
halved, 381 to 174. Nothing here changes what the tool does.
Two things were measurably moving under the dealer's thumb, and both are fixed
by reserving space rather than by animating anything:
* The undo pill named the last action, so it resized on every single tap.
Measured across one hand: 148, 110, 109, 108, 109, 110, 120px. It is now a
fixed 10rem, with the label in its own element so it can ellipsize without
truncating through the keycap beside it.
* The action bar is fixed to the bottom, so anything that changes its height
moves its top edge. The status row wraps, and it wrapped differently as the
street and pot changed. It now holds two lines' worth of room whenever a
hand is running. Across 13 viewports x 3 table sizes, the bar no longer
changes height mid-hand at all.
Numbers that change while the dealer watches — pot, stacks, bets, standings —
are now set in tabular figures, so a pot ticking $18 to $21 stops shuffling its
digits sideways.
Two regressions the sweeps caught on the way, both from the snapping:
* The Add Chips field cleared 44px by way of its font size plus its padding,
so moving either dropped it to 42. It states its own min-height now; a
touch target shouldn't be an accident of two other numbers.
* The bar came out 2px shorter in landscape, which grew the felt, which
lifted the top seat's dealer button 2px into the header. That clearance was
structurally 1-7px, so the top seat inset became a floor rather than a
ceiling instead of chasing the 2px.
tests/holdem/designTokens.test.js keeps the scales honest — a new value has to
be a step or an exception with a reason. Fault-injected four ways.
599 tests, 0 failures. 39 layout audits across 13 viewports and 3 table sizes,
plus 35 across the five overlays: 0 failures.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
jheaps
changed title from Naming a blind shouldn't deal the hand to Hold'em dealer: blind fix, plus a design-token foundation2026-09-03 03:12:07 -06:00
Measured on a 390x844 phone, six-handed, same table before and after:
before after
header 193px 120px
felt starts at 225px 152px
controls on screen 27 18
bordered elements 37 22
text elements 61 49
A quarter of the screen went on chrome before the dealer saw the table.
**The header.** Six equally weighted buttons in two rows, of which two are
per-hand controls. The other four have gone where they belong rather than into
a "more" menu — that relocates crowding instead of removing it. Reset Table is
in the Session panel now, beside Clear history and the save/load pair: it is
the control that throws the night away and it was sitting in the same weight as
Resolve Pot, the most-pressed button in a session. Quick Split is down with
Common rulings, where the other "open something else" link already lived. The
way back to the site is a quiet link at the top, where a back link goes.
The subtitle is gone. It was one sentence of orientation on a tool used
nightly, and the landscape and short-phone rules already hid it — the layout
had decided it was expendable on every screen this is actually used on.
**Teaching copy, once.** Table Setup shipped open, so the first screen was the
most crowded one; it now opens only on a table nobody has set up yet, decided
once at load so it cannot reopen itself under a dealer who just shut it. The
gesture hint under the felt goes after the first deal of the session — keyed to
the hand number, not to bettingOpen, which was the obvious test and the wrong
one: it would have appeared and disappeared twice a hand, shunting the panel
stack up and down all night.
**Borders.** Panels had a border, a lighter background and a shadow — three
ways of drawing one edge. The standings had a rule under every row, which is
what makes a list read as ruled paper. Both drop the line and keep the
grouping; the standings header keeps its rule, because that one marks a change
of kind. Fields and buttons keep theirs: those say "interactive", which is not
decoration.
**The felt itself.** Names wrap to two lines instead of ellipsising, so
"Christopher" and "Mike from work" are readable rather than "Chri..." and "Mike
fro..." — the truncation was eating exactly the half a dealer typed to tell two
Mikes apart. And a stack of $1,234,567 rendered at 7px; it now drops the
thousands separators before it drops the type size, so the same number is eight
characters at 10px with every digit intact. The comment above fitAmount was
right that abbreviating loses precision — but precision the dealer cannot read
is not precision.
Two things tried and reverted, both measured:
* Reserving the second name line made every puck the height of the tallest
(69-73px became a uniform 88). Fifteen pixels on all nine seats is felt
spent on chrome, which is what this round is trying to give back.
* Dropping the name a type step to fit more on one line still did not fit
"Christopher" on the button seat, where the dealer chip takes 22px of the
row — so it cost legibility and bought nothing.
Known limit: a single word too long for a puck breaks across the two lines, and
gets a hyphen only where the browser has hyphenation dictionaries. Real phones
do; headless Chromium does not, so audit screenshots show "Christo / pher". The
whole name is visible either way, which it was not before.
599 tests, 0 failures. 52 layout audits across 13 viewports x 4 table sizes
(2, 6, 9, 12 seats) and 20 across the five overlays: 0 failures, and the action
bar still does not change height mid-hand at any of them. The back link was
caught by the audit at 56x13 when it stopped being a button; its hit area is
44px now, paid for with negative margins so the header keeps the 73px.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
jheaps
changed title from Hold'em dealer: blind fix, plus a design-token foundation to Hold'em dealer: blind fix, design tokens, and the decrowd2026-09-03 10:17:43 -06:00
jheaps
merged commit aaf96543a8 into master2026-09-03 10:26:51 -06:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Three changes this session, all into
master.1. Naming a blind no longer deals the hand
Reported: "when I change who the blind is, it acts like I want to start the round."
The seat menu's blind actions reposted whenever
Betting.onlyBlindsIn(state)was true — which means nothing has happened in this hand except the blinds, true of a hand one second old and equally true of a table in setup. So naming a blind ranrepostBlinds->beginHand: betting opened, two stacks docked, action bar up.Betting.blindsArePosted(state)is the question all three wanted.2. A vocabulary for the stylesheet, and a bar that stops moving
holdem.csshad 42 spacing values, 31 font sizes, 14 border-radii and 139 colours — and 3 custom properties. Constrained scales now: 7 spacing steps on an 8px rhythm, 8 type sizes, 4 corners, a 4-step surface ramp off pure black. 661 values rewritten; colour mentions 381 -> 174.3. The felt gets its screen back
Measured on a 390x844 phone, six-handed, same table before and after:
Header: six equally weighted buttons in two rows, two of which are per-hand controls. The rest went where they belong rather than into a "more" menu, which relocates crowding instead of removing it — Reset Table into the Session panel beside Clear history (it throws the night away and was sitting at the same weight as Resolve Pot), Quick Split down with Common rulings, and the way back to the site as a quiet top link. The subtitle is gone; the landscape and short-phone rules already hid it.
Teaching copy, once: Table Setup no longer ships open — it opens only on a table nobody has set up, decided once at load so it can't reopen under a dealer who just shut it. The gesture hint goes after the first deal of the session, keyed to the hand number rather than
bettingOpen, which would have made it appear and disappear twice a hand.Borders: panels had a border and a lighter background and a shadow; the standings had a rule under every row. Both drop the line, keep the grouping. Fields and buttons keep theirs — those say "interactive", which isn't decoration.
The felt: names wrap to two lines instead of ellipsising, so "Christopher" and "Mike from work" are readable rather than "Chri..." and "Mike fro..." — the truncation was eating the half that tells two Mikes apart. A stack of $1,234,567 rendered at 7px; it now drops the thousands separators before the type size, so the same number is eight characters at 10px with every digit intact.
Tried and reverted, both measured
Known limit
A single word too long for a puck breaks across the two lines and gets a hyphen only where the browser has hyphenation dictionaries. Real phones do; headless Chromium doesn't, so audit screenshots show "Christo / pher". The whole name is visible either way, which it wasn't before.
Validated
designTokens.test.jsfails if anyone types a raw value instead of reaching for a token — fault-injected four ways.aimingTheBlinds.test.jsfault-injected: the old condition fails 6 of its 11.Naming a blind shouldn't deal the handto Hold'em dealer: blind fix, plus a design-token foundationMeasured on a 390x844 phone, six-handed, same table before and after: before after header 193px 120px felt starts at 225px 152px controls on screen 27 18 bordered elements 37 22 text elements 61 49 A quarter of the screen went on chrome before the dealer saw the table. **The header.** Six equally weighted buttons in two rows, of which two are per-hand controls. The other four have gone where they belong rather than into a "more" menu — that relocates crowding instead of removing it. Reset Table is in the Session panel now, beside Clear history and the save/load pair: it is the control that throws the night away and it was sitting in the same weight as Resolve Pot, the most-pressed button in a session. Quick Split is down with Common rulings, where the other "open something else" link already lived. The way back to the site is a quiet link at the top, where a back link goes. The subtitle is gone. It was one sentence of orientation on a tool used nightly, and the landscape and short-phone rules already hid it — the layout had decided it was expendable on every screen this is actually used on. **Teaching copy, once.** Table Setup shipped open, so the first screen was the most crowded one; it now opens only on a table nobody has set up yet, decided once at load so it cannot reopen itself under a dealer who just shut it. The gesture hint under the felt goes after the first deal of the session — keyed to the hand number, not to bettingOpen, which was the obvious test and the wrong one: it would have appeared and disappeared twice a hand, shunting the panel stack up and down all night. **Borders.** Panels had a border, a lighter background and a shadow — three ways of drawing one edge. The standings had a rule under every row, which is what makes a list read as ruled paper. Both drop the line and keep the grouping; the standings header keeps its rule, because that one marks a change of kind. Fields and buttons keep theirs: those say "interactive", which is not decoration. **The felt itself.** Names wrap to two lines instead of ellipsising, so "Christopher" and "Mike from work" are readable rather than "Chri..." and "Mike fro..." — the truncation was eating exactly the half a dealer typed to tell two Mikes apart. And a stack of $1,234,567 rendered at 7px; it now drops the thousands separators before it drops the type size, so the same number is eight characters at 10px with every digit intact. The comment above fitAmount was right that abbreviating loses precision — but precision the dealer cannot read is not precision. Two things tried and reverted, both measured: * Reserving the second name line made every puck the height of the tallest (69-73px became a uniform 88). Fifteen pixels on all nine seats is felt spent on chrome, which is what this round is trying to give back. * Dropping the name a type step to fit more on one line still did not fit "Christopher" on the button seat, where the dealer chip takes 22px of the row — so it cost legibility and bought nothing. Known limit: a single word too long for a puck breaks across the two lines, and gets a hyphen only where the browser has hyphenation dictionaries. Real phones do; headless Chromium does not, so audit screenshots show "Christo / pher". The whole name is visible either way, which it was not before. 599 tests, 0 failures. 52 layout audits across 13 viewports x 4 table sizes (2, 6, 9, 12 seats) and 20 across the five overlays: 0 failures, and the action bar still does not change height mid-hand at any of them. The back link was caught by the audit at 56x13 when it stopped being a button; its hit area is 44px now, paid for with negative margins so the header keeps the 73px. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgbHold'em dealer: blind fix, plus a design-token foundationto Hold'em dealer: blind fix, design tokens, and the decrowd