Skip to content

fix(liars-dice): stack cups on narrow screens instead of clipping them - #55

Merged
anhuaxiang merged 1 commit into
mainfrom
fix/liars-dice-narrow-layout
Aug 20, 2026
Merged

fix(liars-dice): stack cups on narrow screens instead of clipping them#55
anhuaxiang merged 1 commit into
mainfrom
fix/liars-dice-narrow-layout

Conversation

@anhuaxiang

Copy link
Copy Markdown
Collaborator

The bug

On a phone, the anchor seat's cup rendered as 4+1 while the two opponents kept 5 across.

The anchor station is absolutely positioned with left:50%;transform:translateX(-50%). An absolutely positioned box shrink-to-fits against the room from its left edge to the containing block's right edge — so left:50% capped it at half the stage (~183px on a 390px viewport), and the transform is a paint-time offset that comes too late to affect layout. 183px fits 4 dice (4×34 + 3×5 = 151); the 5th needs 190px, so it wrapped.

center() was squeezed by the same rule. And the two top stations are 220px each at left:2% / right:2%, so they overlap once the stage drops below ~450px.

The fix

  • Centre by spanning the stage (left:0;right:0 + a flex wrapper) instead of 50%+transform. No more halved width budget.
  • Each top station gets half the stage as an explicit width budget, so the pair never collides.
  • Dice keep their size at every width. Whatever doesn't fit on one line moves to the next, dealt into balanced rows — a cup of 5 reads as 3+2, not 4+1; tighter still, 2+2+1.
  • The felt absorbs the extra height. Stacking makes a station taller, and the stage height is fixed — the platform hands the view a 560px iframe (scripts/preview.ts:69, matching SandboxedGameViewer), so growing the stage just clips the status line. Instead the felt flattens by exactly the overflow and the centre rides with it, keeping stacked dice off the standing bid.
  • Redraw on resize (rAF-debounced), since sizes are baked in at draw time and rotation has to re-deal the rows.

Verification

Local pnpm preview liars-dice, screenshotted at 320 / 375 / 390 / 450 / 1100 CSS px, on both a bid frame and the taller LIAR? reveal frame:

viewport top two anchor
320 2+2+1 5 across
375 / 390 3+2 5 across
450 3+2 (not 4+1) 5 across
1100 5 across 5 across — pixel-identical to before

pnpm -F liars-dice typecheck and pnpm validate (11 games / 9 worlds) pass. Only games/liars-dice/view.ts changes; no game logic touched.

🤖 Generated with Claude Code

On a phone the anchor seat's cup rendered as 4+1 while the two opponents
kept 5 across. The anchor station is absolutely positioned with
`left:50%;transform:translateX(-50%)`, and an absolutely positioned box
shrink-to-fits against the room from its `left` edge to the containing
block's right edge — so `left:50%` capped it at HALF the stage (~183px on
a 390px viewport) and the transform came too late to help. The two top
stations, meanwhile, are 220px each at `left:2%` / `right:2%`, which
overlap once the stage drops under ~450px.

Centre by spanning the stage (`left:0;right:0` + a flex wrapper) rather
than by 50%+transform, and give each top station half the stage as its
width budget. Dice keep their size at every width — whatever doesn't fit
on one line moves to the next, dealt into balanced rows, so a cup of 5
reads as 3+2 rather than 4+1.

Stacking makes a station taller, and the stage height is fixed (the
platform hands the view a 560px iframe; growing it clips the status
line), so the felt gives the room up instead: it flattens by exactly the
overflow and the centre rides with it, keeping stacked dice off the
standing bid. Sizes are baked in at draw time, so also redraw on resize.

Verified against the local preview at 320 / 375 / 390 / 450 / 1100 CSS
px, on both a bid frame and the taller `LIAR?` reveal frame. Desktop is
unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@anhuaxiang
anhuaxiang merged commit d16032e into main Aug 20, 2026
2 of 3 checks passed
@anhuaxiang

Copy link
Copy Markdown
Collaborator Author

🔍 AI review — external reviewer

Arena AI Review — games/liars-dice/view.ts

Overall: 🟢 GREEN (a human maintainer review is still required)

Track: games/ — this is a T2 view.ts (client render surface).

What the diff does

A pure responsive-layout refactor of the Liar's Dice render:

  • Adds width-based metrics (measure, diceLines, cupLines) so a seat's cup stacks (5 → 3+2) instead of shrinking on narrow stages.
  • diceRow now builds dice once and deals them into balanced lines inside a column box.
  • station/center thread an avail/CENTER_MAX width through.
  • Adds a resize listener (rAF-throttled) to redraw, and a centred() flex wrapper to replace left:50%;transform centering.

Security / sandbox

  • No fetch / XMLHttpRequest / WebSocket / sendBeacon, no require / dynamic import() / eval / new Function, no filesystem or process, no globalThis/window escapes. Only the DOM view contract is used (document, addEventListener, requestAnimationFrame) — all legitimate for a render surface.
  • requestAnimationFrame/resize drive rendering only; determinism constraints apply to game logic, not the view. No Date/Math.random/entropy introduced.
  • No hidden-info leak: cupLines/diceRow derive faces from f.reveal.dice[sv.seat] (open cups) or sv.dice (already the frame's own visible data), same as before — the change only affects how many lines to draw, never surfacing face values that weren't already in the frame. Face-down seats still render die(0,{faceDown:true}) by count.
  • No obfuscation, no embedded instructions to the reviewer.

Correctness

  • diceLines guards perRow ≥ 1 and rows ≥ 1; degenerate cases (n=0, n=1) are handled (cupLines short-circuits n<=1, diceRow early-returns the "☠ out" branch). No throw paths on normal play; layout math is bounded.

Minor advisory (non-blocking): the resize listener is registered once per root creation but never removed — harmless in a single-view iframe, but worth a note if roots are ever recreated.

Clean layout-only change with no injection, non-determinism, or info-leak concerns.

Automated pre-review. A human maintainer review is still required.

@anhuaxiang anhuaxiang added the ai-review-passed AI review GREEN — ready for human review label Aug 20, 2026
@anhuaxiang
anhuaxiang deleted the fix/liars-dice-narrow-layout branch August 20, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review-passed AI review GREEN — ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant