From 589d223058c758607b7918378c47fff484928fd9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 02:54:22 +0000 Subject: [PATCH] chess: fit the board to the screen, side-by-side on landscape --- .../chess/components/ChessTable.module.css | 52 ++++- src/apps/chess/components/ChessTable.tsx | 190 +++++++++--------- 2 files changed, 141 insertions(+), 101 deletions(-) diff --git a/src/apps/chess/components/ChessTable.module.css b/src/apps/chess/components/ChessTable.module.css index e8fda7c..6b80b30 100644 --- a/src/apps/chess/components/ChessTable.module.css +++ b/src/apps/chess/components/ChessTable.module.css @@ -2,9 +2,12 @@ title, header row, status line, buttons — is castle's (CastleTable.module.css), as rummy's is. */ +/* The table fills the overlay's height, so the board can take what the + rest leaves of it. */ .table { width: 100%; max-width: 560px; + height: 100%; margin: 0 auto; display: flex; flex-direction: column; @@ -54,24 +57,34 @@ } .play { + flex: 1 1 auto; + min-height: 0; display: flex; flex-direction: column; gap: 0.5rem; } -/* The board fits the narrower of the column and the screen's height less - the header, both clocks and the actions (about 19rem), so the whole of - it is on screen; below 16rem the overlay scrolls instead of shrinking - the squares past a tap. The pieces are sized from the board, not the - screen, so they always fit their squares. */ +/* What the clocks, header and actions leave, measured rather than + budgeted: the board is the larger square that fits it. Below 15rem + the overlay scrolls rather than shrink the squares past a tap. */ +.boardSlot { + flex: 1 1 0; + min-height: 15rem; + container-type: size; + display: flex; + align-items: center; + justify-content: center; +} + +/* The pieces are sized from the board, not the screen, so they always + fit their squares. */ .board { position: relative; container-type: inline-size; display: grid; grid-template-columns: repeat(8, 1fr); - width: max(16rem, min(100%, calc(100dvh - 19rem))); + width: min(100cqw, 100cqh); aspect-ratio: 1; - align-self: center; border: 2px solid rgba(0, 0, 0, 0.5); border-radius: 4px; overflow: hidden; @@ -365,3 +378,28 @@ background: radial-gradient(circle, #f0c96a 20%, #c27a1a 100%); outline: none; } + +/* A phone on its side: too short for the board between its clocks, so the + board takes the left and everything else stacks to its right. */ +@media (orientation: landscape) and (max-height: 500px) { + .table { + max-width: none; + display: grid; + grid-template-columns: auto minmax(0, 1fr); + grid-auto-rows: min-content; + align-content: start; + column-gap: 1rem; + } + + .play { + display: contents; + } + + .boardSlot { + grid-column: 1; + grid-row: 1 / span 6; + width: calc(100dvh - 60px - 4.5rem); + height: calc(100dvh - 60px - 4.5rem); + min-height: 0; + } +} diff --git a/src/apps/chess/components/ChessTable.tsx b/src/apps/chess/components/ChessTable.tsx index 8c1ff8a..e4291a2 100644 --- a/src/apps/chess/components/ChessTable.tsx +++ b/src/apps/chess/components/ChessTable.tsx @@ -283,101 +283,103 @@ const ChessTable = ({ playerId, connected, view, table }: ChessTableProps) => { a move -
- {squares.map((square, index) => { - const piece = shown.get(square) - const mine = piece !== undefined && colorOfPiece(piece) === myColor - // a1 dark: a square is dark where its file and rank index sum even. - const light = (square.charCodeAt(0) - 97 + Number(square[1])) % 2 === 0 - const target = targets.includes(square) - const grab = mine && myTurn && connected && targetsFrom(view.legalMoves, square).length > 0 - return ( - + ) + })} + {/* Lichess's picker: the pieces stacked down the file from the + promotion square, which is always on the viewer's far rank, + over a dimmed board a tap on which lets the pawn go back. */} + {pendingPromotion !== null && ( +
{ + if (event.key === 'Escape') { + event.preventDefault() + cancelPromotion() + } + }} > - {index % 8 === 0 && ( - - )} - {index >= 56 && ( - - )} - {piece !== undefined && ( - - )} - - ) - })} - {/* Lichess's picker: the pieces stacked down the file from the - promotion square, which is always on the viewer's far rank, - over a dimmed board a tap on which lets the pawn go back. */} - {pendingPromotion !== null && ( -
{ - if (event.key === 'Escape') { - event.preventDefault() - cancelPromotion() - } - }} - > -
    - {PROMOTIONS.map(({ letter, name }, i) => { - const uci = pendingPromotion.moves.find(move => move.endsWith(letter)) - if (uci === undefined) return null - return ( -
  • - -
  • - ) - })} -
-
- )} +
    + {PROMOTIONS.map(({ letter, name }, i) => { + const uci = pendingPromotion.moves.find(move => move.endsWith(letter)) + if (uci === undefined) return null + return ( +
  • + +
  • + ) + })} +
+
+ )} +
{me?.color !== undefined && } {/* On the page, not the board: the board clips its overflow, and a