Compare commits

..
Author SHA1 Message Date
jheapsandClaude Sonnet 5 d0f75d0c14 Lift the faint greys to something readable in a dim room
This is a dark interface used in poker rooms and kitchens with the lights down,
and I'd been generous with grey-on-black. Measured every piece of text against
what's actually painted behind it: thirteen declarations came in under 4.5:1.

The worst was the empty card slot's "+" at 2.63:1 — an affordance telling the
dealer the slot is tappable, and the least legible thing on the screen. Then a
run of #777 labels around 3.9 to 4.1: the standings header, the time on a logged
hand, the currency mark in the add-chips field. The rulings source line sat at
3.62 and the rulings link itself at 4.39.

All of them move up just far enough to clear 4.5 rather than being flattened to
white, so the hierarchy between a heading, a value and a hint survives.

Worth recording what the measurement can't see: it reads background-color, so
text on a gradient — the pot badge, the dealer button, the blind badges — comes
back as a false result in both directions, and those were checked by eye
instead. The felt watermark is deliberately at 6% and stays that way.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
2026-09-01 01:06:20 -06:00
2 changed files with 20 additions and 147 deletions
+18 -104
View File
@@ -146,7 +146,7 @@ body {
}
.tableHint {
color: #888;
color: #949494;
font-size: 0.8rem;
text-align: center;
margin-top: 1rem;
@@ -357,7 +357,7 @@ body {
}
.seatEmptyLabel {
color: #6d6d6d;
color: #8a8a8a;
font-size: 0.6rem;
letter-spacing: 0.08em;
text-transform: uppercase;
@@ -373,7 +373,7 @@ body {
top: 2px;
left: 6px;
font-size: 0.58rem;
color: #777;
color: #8a8a8a;
}
.seatPuckName {
@@ -578,7 +578,7 @@ body {
.cardSlot.cardSlotEmpty {
background: #191919;
color: #5c5c5c;
color: #8a8a8a;
border-style: dashed;
border-color: #3f3f3f;
box-shadow: none;
@@ -694,7 +694,7 @@ body {
}
.addChipsCurrency {
color: #777;
color: #8a8a8a;
font-size: 1.3rem;
}
@@ -735,7 +735,7 @@ body {
background: none;
border: none;
padding: 0;
color: #a9782f;
color: #d8b46a;
font-family: inherit;
font-size: 0.78rem;
text-decoration: underline;
@@ -834,7 +834,7 @@ body {
margin: 0;
padding-top: 0.6rem;
border-top: 1px solid #2c2c2c;
color: #7a7a7a;
color: #8a8a8a;
font-size: 0.72rem;
line-height: 1.45;
}
@@ -1213,7 +1213,7 @@ body {
.subPanel summary {
cursor: pointer;
color: #8f8f8f;
color: #9a9a9a;
font-size: 0.78rem;
}
@@ -1249,7 +1249,7 @@ body {
.fieldHint {
font-size: 0.75rem;
color: #888;
color: #949494;
line-height: 1.4;
margin: 0;
}
@@ -1306,7 +1306,7 @@ body {
}
.standingsHeader {
color: #777;
color: #8a8a8a;
font-size: 0.7rem;
letter-spacing: 0.04em;
text-transform: uppercase;
@@ -1374,7 +1374,7 @@ body {
.handHistoryTime {
margin-left: auto;
color: #777;
color: #8a8a8a;
font-size: 0.72rem;
}
@@ -1494,7 +1494,7 @@ body.hasActionBar #holdemSidebar {
flex: none;
min-width: 3.1rem;
text-align: right;
color: #7d7d7d;
color: #949494;
font-variant-numeric: tabular-nums;
transition: color 0.3s ease;
}
@@ -1732,7 +1732,7 @@ body.hasActionBar #holdemSidebar {
.seatPuckStack {
font-size: 0.58rem;
color: #8f8f8f;
color: #9a9a9a;
font-variant-numeric: tabular-nums;
margin-top: 0.1rem;
}
@@ -1836,7 +1836,7 @@ body.hasActionBar #toastHost {
border-radius: 50%;
border: none;
background: transparent;
color: #888;
color: #949494;
font-size: 0.8rem;
font-family: inherit;
cursor: pointer;
@@ -1962,75 +1962,20 @@ body.hasActionBar #toastHost {
margin-bottom: 0.6rem;
}
/* This is the screen where the money gets decided, so it holds the same 44px+
targets as the rest of the tool rather than the smallest ones in it. Picking the
wrong winner is the one mistake here that costs somebody real money. */
.potWizardChoice {
display: flex;
align-items: center;
gap: 0.7rem;
min-height: 56px;
gap: 0.5rem;
background: #141414;
border: 1px solid #333;
border-radius: 10px;
padding: 0.6rem 0.8rem;
border-radius: 6px;
padding: 0.5rem 0.7rem;
cursor: pointer;
font-size: 0.95rem;
transition: border-color 0.12s ease, background 0.12s ease;
font-size: 0.85rem;
}
.potWizardChoice:hover {
border-color: #555;
background: #191919;
}
.potWizardChoice input {
flex: none;
width: 22px;
height: 22px;
accent-color: #1e8449;
}
/* 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. */
.potWizardChoice.chosen {
border-color: #1e8449;
background: linear-gradient(180deg, #16301f, #12241a);
}
.potWizardChoice.chosen .potWizardChoiceName {
color: #d6ffe0;
}
.potWizardChoiceText {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.potWizardChoiceName {
color: #f0f0f0;
font-weight: 600;
}
.potWizardChoiceUpTo {
color: #9a9a9a;
font-size: 0.76rem;
font-variant-numeric: tabular-nums;
}
.potWizardChoiceCards {
flex: none;
font-family: var(--font-mono, monospace);
font-size: 1rem;
font-weight: 700;
color: #ddd;
}
.potWizardChoiceCards .cardRed {
color: #ff6b6b;
}
.potWizardSummary ul {
@@ -2051,37 +1996,6 @@ body.hasActionBar #toastHost {
gap: 0.6rem;
}
/* Every button in this modal is one a dealer presses with players waiting. */
#potWizardBody .holdemBtn {
min-height: 48px;
justify-content: center;
}
/* The confirm is the whole point of the step, so it gets the whole width rather
than being sized to the length of its own label. */
.potWizardRound > .holdemBtn {
width: 100%;
}
.potWizardActions .holdemBtn {
flex: 1;
}
.potWizardSummary li {
font-variant-numeric: tabular-nums;
}
@media (max-width: 700px) {
/* Stacked, with the action that ends the hand furthest from a stray thumb. */
.potWizardActions {
flex-direction: column-reverse;
}
.potWizardChoice {
min-height: 60px;
}
}
@media (max-width: 900px) {
#holdemLayout {
grid-template-columns: 1fr;
@@ -18,14 +18,6 @@ const PotWizard = {
this.playerLabelById = new Map(
players.map(p => [p.id, this.labelFor(p, state.seats.indexOf(p))])
);
// The label carries name and cards together for the log and the payout lines;
// the choice rows want them separately so each can be styled and read on its own.
this.nameById = new Map(
players.map(p => [p.id, HoldemState.displayName(p, state.seats.indexOf(p))])
);
this.cardsById = new Map(
players.map(p => [p.id, (p.cards || []).filter(Boolean)])
);
const contributions = players.map(p => ({
id: p.id,
contribution: HoldemState.computeContribution(p, state.chipLegend, state.basePotAmount),
@@ -113,46 +105,13 @@ const PotWizard = {
contenders.forEach(c => {
const label = document.createElement("label");
label.className = "potWizardChoice";
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.value = c.id;
// The row is the target, not the box — so the row has to look chosen too.
checkbox.addEventListener("change", () => {
label.classList.toggle("chosen", checkbox.checked);
});
label.appendChild(checkbox);
const text = document.createElement("span");
text.className = "potWizardChoiceText";
const name = document.createElement("span");
name.className = "potWizardChoiceName";
name.textContent = this.nameById.get(c.id);
text.appendChild(name);
const upTo = document.createElement("span");
upTo.className = "potWizardChoiceUpTo";
upTo.textContent = `up to ${Money.format(c.remaining)}`;
text.appendChild(upTo);
text.textContent = `${this.playerLabelById.get(c.id)} — up to ${Money.format(c.remaining)}`;
label.appendChild(checkbox);
label.appendChild(text);
// Cards sit apart from the name rather than inside it, so a dealer reading
// a row across the table sees the hand, not a sentence.
const cards = this.cardsById.get(c.id) || [];
if (cards.length > 0) {
const cardsEl = document.createElement("span");
cardsEl.className = "potWizardChoiceCards";
cards.forEach((card, i) => {
if (i > 0) cardsEl.appendChild(document.createTextNode(" "));
const span = document.createElement("span");
if (/[HD]$/i.test(card)) span.className = "cardRed";
span.textContent = Render.formatCard(card);
cardsEl.appendChild(span);
});
label.appendChild(cardsEl);
}
choices.appendChild(label);
});
round.appendChild(choices);