Hold'em dealer: 55 polish rounds, one merge #101

Merged
jheaps merged 71 commits from release/casino-holdem-polish into master 2026-09-02 10:00:15 -06:00
3 changed files with 99 additions and 1 deletions
Showing only changes of commit f0994a8aed - Show all commits
@@ -294,6 +294,8 @@
not during it. not during it.
</p> </p>
<nav id="rulingsIndex" class="rulingsIndex" aria-label="Jump to a ruling"></nav>
<section class="ruling"> <section class="ruling">
<h3>Redeal it — misdeal</h3> <h3>Redeal it — misdeal</h3>
<ul> <ul>
@@ -383,6 +385,22 @@
</p> </p>
</section> </section>
<section class="ruling">
<h3>Betting in one motion — the string bet</h3>
<p>
A bet is whatever goes in on the first trip to the stack, unless the
amount was said out loud first. Reaching back for more chips without
having said "raise" is a string bet, and only the first motion stands.
Saying the number is always enough on its own: "raise to sixty" binds
whatever order the chips arrive in.
</p>
<p>
It's the rule home games invoke most and enforce least, and both are
fine — the point is that a player can't watch the table react and then
decide how much to bet. Say it, or push it all at once.
</p>
</section>
<p class="rulingsSource"> <p class="rulingsSource">
Follows the Tournament Directors Association rules, which most rooms and Follows the Tournament Directors Association rules, which most rooms and
home games take their lead from. home games take their lead from.
@@ -1095,6 +1095,51 @@ button,
line-height: 1.5; line-height: 1.5;
} }
/* One chip per ruling, wrapped across the top of the sheet. Names rather than icons:
a dealer scanning this is looking for the words somebody at the table just said. */
.rulingsIndex {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
margin: 0 0 1rem;
}
.rulingsIndexBtn {
box-sizing: border-box;
min-height: 32px;
padding: 0.3rem 0.6rem;
border-radius: 999px;
border: 1px solid #3a3a3a;
background: #232323;
color: #d8b46a;
font-family: inherit;
font-size: 0.72rem;
line-height: 1.25;
text-align: left;
cursor: pointer;
}
@media (hover: hover) {
.rulingsIndexBtn:hover {
background: #2e2e2e;
border-color: #4a4a4a;
}
}
/* A thumb needs the same 44px here as everywhere else. */
@media (pointer: coarse) {
.rulingsIndexBtn {
min-height: 44px;
font-size: 0.78rem;
}
}
/* The heading the index jumps to takes focus, and a focus ring on a heading that the
dealer just asked for reads as noise rather than as help. */
.ruling h3:focus {
outline: none;
}
.ruling { .ruling {
margin-bottom: 1rem; margin-bottom: 1rem;
padding-left: 0.7rem; padding-left: 0.7rem;
+36 -1
View File
@@ -458,7 +458,42 @@ document.addEventListener("DOMContentLoaded", () => {
// Escape is Modal's job now, for this sheet and the five others that never had it. // Escape is Modal's job now, for this sheet and the five others that never had it.
const rulingsOverlay = document.getElementById("rulingsOverlay"); const rulingsOverlay = document.getElementById("rulingsOverlay");
const closeRulings = () => { rulingsOverlay.hidden = true; }; const closeRulings = () => { rulingsOverlay.hidden = true; };
document.getElementById("openRulingsBtn").addEventListener("click", () => { rulingsOverlay.hidden = false; }); // An index across the top of the rulings sheet, one chip per ruling.
//
// This is opened mid-argument with players waiting, and it was nine rulings of
// prose with no way in: reaching the last one meant scrolling 1.6 screens on a
// desktop, 2.1 on a phone and 3.5 on a phone held sideways. A dealer looking for
// the odd-chip rule had to read past seven others to find it.
//
// Built from the sections rather than written out beside them, so a ruling added
// later turns up in the index without anyone having to remember to add it twice.
const buildRulingsIndex = () => {
const index = document.getElementById("rulingsIndex");
if (!index || index.childElementCount > 0) return;
[...rulingsOverlay.querySelectorAll(".ruling")].forEach((section, i) => {
const heading = section.querySelector("h3");
if (!heading) return;
if (!section.id) section.id = `ruling${i + 1}`;
const jump = document.createElement("button");
jump.type = "button";
jump.className = "rulingsIndexBtn";
jump.textContent = heading.textContent;
jump.addEventListener("click", () => {
section.scrollIntoView({ block: "start", behavior: "smooth" });
// The heading is what the dealer is now reading, so that is where the
// keyboard and a screen reader should be too.
heading.setAttribute("tabindex", "-1");
heading.focus({ preventScroll: true });
});
index.appendChild(jump);
});
};
document.getElementById("openRulingsBtn").addEventListener("click", () => {
buildRulingsIndex();
rulingsOverlay.hidden = false;
});
document.getElementById("closeRulingsBtn").addEventListener("click", closeRulings); document.getElementById("closeRulingsBtn").addEventListener("click", closeRulings);
rulingsOverlay.addEventListener("click", e => { rulingsOverlay.addEventListener("click", e => {
if (e.target === rulingsOverlay) closeRulings(); if (e.target === rulingsOverlay) closeRulings();