feat(ui): landscape two-column game layout, board fitted by height
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 58s

When the viewport is wider than tall (matchMedia orientation: landscape) the
game screen switches from the portrait stack to a two-column layout: the board
fills the right column as the largest square that fits the height (no zoom,
shrinking by width when cramped — lowest priority), while the left panel stacks
the rack (+ make), the status line, the score plaques, the always-open docked
history and the controls. Board zoom, the history slide-drawer gestures and the
growing nav bar are gated off in landscape; the portrait layout is unchanged and
both render from the same snippets so behaviour stays single-sourced.

The mock e2e now defaults to a portrait viewport (the mobile-first app the
gesture/zoom/history specs are written for); landscape.spec.ts covers the wide
layout in its own viewport.
This commit is contained in:
Ilia Denisov
2026-06-14 19:06:35 +02:00
parent 7716353f84
commit 9ec72c8377
5 changed files with 326 additions and 127 deletions
+14 -1
View File
@@ -8,6 +8,7 @@
slots,
variant,
selected,
landscape = false,
shuffling = false,
draggingId = null,
dropIndex = null,
@@ -18,6 +19,9 @@
slots: (RackSlot & { id: number })[];
variant: Variant;
selected: number | null;
/** Landscape layout: size the tiles to share the (narrow) left-panel width instead of the
* viewport-width measure, so seven tiles never overflow the panel. */
landscape?: boolean;
shuffling?: boolean;
// While a rack tile is being dragged to reorder it, draggingId is its id (hidden here —
// the drag ghost stands in) and dropIndex is the slot where a gap opens.
@@ -52,7 +56,7 @@
}
</script>
<div class="rack" class:reordering={draggingId != null} data-rack>
<div class="rack" class:reordering={draggingId != null} class:landscape data-rack>
{#each shown as slot, i (slot.id)}
<button
class="tile"
@@ -100,6 +104,15 @@
outline: 3px solid var(--accent);
outline-offset: -3px;
}
/* Landscape: the rack sits in a fixed-width left panel, so the tiles share the panel width
(capped at the portrait size) and shrink below it when the panel is narrow, instead of the
viewport-width measure that would overflow seven tiles in a column. */
.rack.landscape .tile {
flex: 1 1 0;
width: auto;
min-width: 0;
max-width: 46px;
}
/* While reordering, tiles at/after the drop slot slide right to open a gap there (one
tile width plus the rack gap), so the drop position is visible. */
.rack.reordering .tile {