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
3 changed files with 19 additions and 70 deletions
+14 -20
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;
}
@@ -1480,12 +1480,6 @@ body.hasActionBar #holdemSidebar {
font-variant-numeric: tabular-nums;
}
.actionRunout {
margin-left: auto;
color: #d8b46a;
font-size: 0.74rem;
}
.actionWho {
margin-left: auto;
color: #9c9c9c;
@@ -1500,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;
}
@@ -1738,7 +1732,7 @@ body.hasActionBar #holdemSidebar {
.seatPuckStack {
font-size: 0.58rem;
color: #8f8f8f;
color: #9a9a9a;
font-variant-numeric: tabular-nums;
margin-top: 0.1rem;
}
@@ -1842,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;
@@ -74,16 +74,6 @@ const ActionBar = {
players.textContent = `${remaining} ${remaining === 1 ? "player" : "players"}`;
row.appendChild(players);
// Nobody is being asked to act because nobody can — say so, or the bar just
// looks like it's waiting for something.
if (Betting.isRunningOut(this.state) && !Betting.isHandOver(this.state)) {
const runout = document.createElement("span");
runout.className = "actionRunout";
const stillIn = Betting.liveSeats(this.state).length;
runout.textContent = `All-in — running out the board (${stillIn} players)`;
row.appendChild(runout);
}
const index = this.state.actingSeatIndex;
if (index !== null && index !== undefined && !Betting.isHandOver(this.state)) {
const player = this.state.seats[index];
@@ -70,19 +70,7 @@ const Betting = {
for (let step = 1; step <= count; step++) {
const index = this.seatAt(state, fromIndex, step);
const player = state.seats[index];
if (player.inHand && !player.folded) return index;
}
return null;
},
// Who the dealer asks next. A player who is all-in is still in the hand and still
// wins pots, but has no decision left, so the action passes over them.
nextToAct(state, fromIndex) {
const count = state.seatCount;
for (let step = 1; step <= count; step++) {
const index = this.seatAt(state, fromIndex, step);
const player = state.seats[index];
if (player.inHand && !player.folded && this.stackOf(state, player) > this.EPSILON_CHIPS) return index;
if (player.inHand && !player.folded && this.stackOf(state, player) >= 0) return index;
}
return null;
},
@@ -168,7 +156,7 @@ const Betting = {
},
passAction(state, index) {
state.actingSeatIndex = this.isStreetComplete(state) ? null : this.nextToAct(state, index);
state.actingSeatIndex = this.isStreetComplete(state) ? null : this.nextLiveIndex(state, index);
},
// ---- Street and hand boundaries ----
@@ -196,13 +184,8 @@ const Betting = {
const nextStreetIndex = this.STREETS.indexOf(state.street || "preflop") + 1;
state.street = this.STREETS[nextStreetIndex] || "river";
// It takes two players with chips to have a bet. Once everyone left is all-in
// — or only one player still has anything behind — the rest of the board is
// just dealt out, and asking someone to check when they have nothing to check
// with only slows the dealer down.
state.actingSeatIndex = this.canStillBet(state)
? this.nextToAct(state, state.dealerSeatIndex)
state.actingSeatIndex = this.liveSeats(state).length > 1
? this.nextLiveIndex(state, state.dealerSeatIndex)
: null;
},
@@ -210,24 +193,6 @@ const Betting = {
return this.liveSeats(state).length <= 1;
},
// Players still in the hand who have chips left to put in. Fewer than two of them
// means the betting is finished even though the hand isn't.
seatsWithChips(state) {
return this.liveSeats(state).filter(s => this.stackOf(state, s.player) > this.EPSILON_CHIPS);
},
EPSILON_CHIPS: 0.004,
canStillBet(state) {
return this.liveSeats(state).length > 1 && this.seatsWithChips(state).length > 1;
},
// True when the hand is still live but nobody can bet again — the dealer is just
// running the board out.
isRunningOut(state) {
return this.liveSeats(state).length > 1 && !this.canStillBet(state);
},
// Deals a fresh betting round: blinds posted, action to the seat after the big
// blind, which is where a dealer expects to be looking when the cards go out.
beginHand(state) {
@@ -250,7 +215,7 @@ const Betting = {
state.currentBet = this.round(Math.max(this.committed(state.seats[sbIndex]), this.committed(state.seats[bbIndex])));
state.lastRaiseSize = bb || 0;
state.actingSeatIndex = this.nextToAct(state, bbIndex);
state.actingSeatIndex = this.nextLiveIndex(state, bbIndex);
},
// Winners get their share put back in front of them.