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
+13
View File
@@ -132,6 +132,19 @@ Login uses `Screen`.
`Modal` keyboard-overlay mode — the small sheet is top-anchored and the soft keyboard
overlays the empty area below, so the layout doesn't resize/jank; other modals stay
keyboard-aware (they size to the area above the keyboard).
- **Landscape (wide) layout** (`Game.svelte`): when the viewport is wider than tall
(`matchMedia('(orientation: landscape)')`) the game switches from the portrait stack to a
**two-column** layout. The board fills the **right** column, fitted to the available height as
the largest square that still fits (`min(100cqw, 100cqh)` inside a `container-type: size` pane),
shrinking by width when the column is narrow — the board has the **lowest priority**, so the
left panel is never squeezed. The **left panel** stacks the rack (+ the ✅ make control), the
status line (bag · turn · score preview), the score plaques, the **always-open** history (docked
and scrolling with its header sticky — no slide-down drawer, no score-bar toggle) and, pinned at
the bottom, the controls tab bar. Board **zoom is disabled** (the whole board is already visible)
along with the history open/close swipes, and the nav bar does not grow (`growNav` off). The
portrait layout is byte-for-byte unchanged; both layouts render from the same snippets, so the
behaviour and markup stay single-sourced. The rack tiles size to the (fixed-width) panel rather
than the viewport width so seven tiles never overflow the column.
- **Highlights**: pending tiles use a slightly darker tile background (no outline). The
last completed word keeps the normal tile background; instead its letters — not the point
values — are drawn in the recent-move colour, in both themes. It is static while it is the