Make the dealer table usable on a phone

Two things were broken on a narrow screen. The seat ring is drawn on a radius
measured from the table wrap, so at phone width the left- and right-most seats
hung off the edge of the viewport — at a ten-handed table two players were
simply not on screen. And tapping a seat opened an editor far enough down the
page that it looked like nothing had happened.

The ring is now inset by half a puck on phones, which keeps every seat on
screen, and the pucks are narrower there so neighbours stop overlapping where
the ring flattens out at the top and bottom. The seat editor becomes a bottom
sheet that slides up over the table: grip, Done button, tap the same seat again
to dismiss, table still visible above it. Its controls grow to thumb size, and
every field goes to 16px — below that iOS zooms the page on focus and the
dealer has to pinch back out mid-hand.

Header actions become full-width tap targets, and the page padding tightens to
give the table the width it needs. Nothing above 700px changes.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
This commit is contained in:
2026-08-31 18:11:09 -06:00
co-authored by Claude Sonnet 5
parent 870dee4a29
commit a39675f975
4 changed files with 232 additions and 2 deletions
@@ -38,7 +38,11 @@
<aside id="holdemSidebar">
<section id="seatEditorPanel" class="sidebarPanel seatEditorPanel">
<h2 id="seatEditorTitle">Select a seat</h2>
<div class="seatSheetGrip" aria-hidden="true"></div>
<div class="seatEditorHeader">
<h2 id="seatEditorTitle">Select a seat</h2>
<button id="closeSeatEditorBtn" class="seatSheetClose" aria-label="Done editing this seat">Done</button>
</div>
<div id="seatEditorBody">
<p class="fieldHint">Click a seat at the table to edit their name, cards, chips and anything else.</p>
</div>
+211
View File
@@ -376,6 +376,27 @@ body {
padding: 0.9rem;
}
.seatEditorHeader {
display: flex;
align-items: center;
gap: 0.6rem;
}
.seatEditorHeader h2 {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Grip and Done button are the sheet's furniture — no reason for either on a
desktop sidebar, where the panel is just a panel. */
.seatSheetGrip,
.seatSheetClose {
display: none;
}
.seatEditorPanel h2 {
margin: 0 0 0.6rem;
padding-bottom: 0.5rem;
@@ -843,3 +864,193 @@ body {
min-height: 44px;
}
}
/* ---------- Phones ----------
Two things break on a narrow screen: seat pucks ring the table on a radius
measured from the wrap, so the left- and right-most ones hang off the edge of
the viewport; and the seat editor lives far enough down the page that tapping a
seat looks like it did nothing. Inset the ring by half a puck, and float the
editor up as a bottom sheet over the table. */
@media (max-width: 700px) {
#holdemApp {
padding: 1rem 0.75rem 2rem;
}
#holdemHeader {
margin-bottom: 1rem;
gap: 0.6rem;
}
#holdemHeader h1 {
font-size: 1.4rem;
}
.holdemSubtitle {
font-size: 0.8rem;
}
#holdemHeaderActions {
width: 100%;
gap: 0.45rem;
}
#holdemHeaderActions .holdemBtn {
flex: 1 1 auto;
justify-content: center;
min-height: 46px;
padding: 0.5rem 0.7rem;
font-size: 0.82rem;
}
/* Keeps every puck inside the viewport: the ring is drawn within this box,
so half a puck of padding is exactly enough to stop the overhang. */
#seatsLayer {
inset: 26px 44px;
}
/* Narrower than the desktop puck: at a full ten-handed table the ring is
flattest at the top and bottom, and wide pucks are what made neighbours
overlap there. The puck is only a summary — the real form is the sheet. */
.seat {
width: 64px;
min-height: 42px;
padding: 0.25rem 0.3rem;
}
#tableWatermark {
font-size: 1.4rem;
}
#potBadge {
padding: 0.5rem 1.1rem;
}
#potBadgeAmount {
font-size: 1.2rem;
}
.tableHint {
margin-top: 0.75rem;
font-size: 0.75rem;
}
/* ---- Seat editor as a bottom sheet ---- */
#seatEditorPanel {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 60;
margin: 0;
max-height: 74vh;
overflow-y: auto;
border-radius: 16px 16px 0 0;
border-bottom: none;
padding: 0.35rem 1rem 1.25rem;
background: linear-gradient(180deg, #242424, #1a1a1a);
box-shadow: 0 -14px 40px rgba(0, 0, 0, 0.65);
transform: translateY(105%);
transition: transform 0.25s ease;
-webkit-overflow-scrolling: touch;
}
body.seatSheetOpen #seatEditorPanel {
transform: translateY(0);
}
.seatSheetGrip {
display: block;
width: 38px;
height: 4px;
margin: 0.35rem auto 0.5rem;
border-radius: 999px;
background: #4a4a4a;
}
.seatSheetClose {
display: inline-flex;
align-items: center;
flex: none;
min-height: 44px;
padding: 0.35rem 1.1rem;
border-radius: 8px;
border: 1px solid #3a3a3a;
background: linear-gradient(180deg, #2e2e2e, #232323);
color: #ddd;
font-size: 0.8rem;
font-family: inherit;
cursor: pointer;
}
/* The sheet covers the bottom of the page while it's open — give the sidebar
room so nothing is stranded underneath it. */
body.seatSheetOpen #holdemSidebar {
padding-bottom: 40vh;
}
/* The seat form was sized for a 320px desktop sidebar. In the sheet it has a
whole phone width to work with, so everything grows to thumb size — and
every field goes to 16px, below which iOS zooms the page on focus and the
dealer has to pinch back out mid-hand. */
.seatEditorForm .seatNameInput,
.seatEditorForm .amountOverrideInput,
.seatEditorForm .chipCountInput,
.seatEditorForm .customFieldLabel,
.seatEditorForm .customFieldValue {
font-size: 16px;
min-height: 44px;
padding: 0.4rem 0.6rem;
}
.seatEditorForm .chipCountInput {
width: 56px;
text-align: center;
}
.seatEditorForm .chipCounts {
gap: 0.6rem;
}
.seatEditorForm .chipSwatch {
width: 16px;
height: 16px;
}
.seatEditorForm .amountOverrideInput {
width: 110px;
}
.seatEditorForm .cardInput {
font-size: 1.35rem;
min-height: 58px;
}
.seatEditorForm .seatCardToggles {
gap: 1rem;
}
.seatEditorForm .inHandToggle,
.seatEditorForm .foldToggle {
min-height: 44px;
font-size: 0.95rem;
}
.seatEditorForm .inHandToggle input,
.seatEditorForm .foldToggle input {
width: 22px;
height: 22px;
}
.seatEditorForm .amountOverrideRow,
.seatEditorForm .seatTotal {
font-size: 0.95rem;
}
.seatEditorForm .addFieldBtn {
width: 100%;
justify-content: center;
min-height: 44px;
margin-top: 0.4rem;
}
}
@@ -71,6 +71,8 @@ document.addEventListener("DOMContentLoaded", () => {
});
});
document.getElementById("closeSeatEditorBtn").addEventListener("click", () => Render.closeSeatEditor());
document.getElementById("closePotWizardBtn").addEventListener("click", () => PotWizard.close());
document.getElementById("potWizardOverlay").addEventListener("click", e => {
if (e.target.id === "potWizardOverlay") PotWizard.close();
@@ -106,7 +106,10 @@ const Render = {
node.classList.toggle("seatSelected", index === this.selectedSeatIndex);
node.addEventListener("click", () => {
this.selectedSeatIndex = index;
// Tapping the open seat again closes it — on a phone the editor is a
// sheet over the table, and the seat you just tapped is the closest
// thing to hand for dismissing it.
this.selectedSeatIndex = this.selectedSeatIndex === index ? null : index;
this.renderSeats();
this.renderSeatEditor();
});
@@ -175,10 +178,20 @@ const Render = {
// ---- Seat editor panel (the full form for whichever seat is selected) ----
closeSeatEditor() {
this.selectedSeatIndex = null;
this.renderSeats();
this.renderSeatEditor();
},
renderSeatEditor() {
const title = document.getElementById("seatEditorTitle");
const body = document.getElementById("seatEditorBody");
// Drives the mobile bottom sheet: the panel only slides up over the table
// while a seat is actually being edited.
document.body.classList.toggle("seatSheetOpen", this.selectedSeatIndex !== null);
if (this.selectedSeatIndex === null) {
title.textContent = "Select a seat";
body.innerHTML = '<p class="fieldHint">Click a seat at the table to edit their name, cards, chips and anything else.</p>';