From 16520d125dcd94a3ebffebd491f8d017b475499b Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 28 Apr 2026 23:48:13 +0700 Subject: [PATCH] fix(mobile): wrap board + controls in centered play-group Group [board + 8px gap + MobileControls] inside a single .play-group div and vertically center it in the space below the HUD on touch devices. The dock no longer hangs at the bottom edge of the viewport; instead the whole [board + controls] reads as one unit floating in the middle of the screen. Implementation: a new .play-stack passthrough flex container fills the remaining column space; on coarse pointers it uses justify-content: safe center to center .play-group, which itself goes flex: 0 1 auto with max-height: 100%. The "safe" keyword falls through to flex-start when the group overflows, so tall finale puzzles still scroll inside .board-wrap instead of clipping above the HUD. Replaces the prior coarse-only .board-wrap { align-items: flex-end } strategy and the now-redundant .board { margin-top: auto } belt-and- suspenders rule in Board.svelte. Desktop layout unchanged. --- src/views/Board.svelte | 8 ---- src/views/GameView.svelte | 83 +++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/src/views/Board.svelte b/src/views/Board.svelte index ab13a3f..9ec0cba 100644 --- a/src/views/Board.svelte +++ b/src/views/Board.svelte @@ -107,14 +107,6 @@ -webkit-user-select: none; } - /* On touch devices the parent .board-wrap is a flex container with - align-items: flex-end. Adding margin-top: auto is a belt-and-suspenders - cross-axis push so the board hugs the dock even if a future refactor - drops the parent's align-items. No effect on desktop (parent is not flex). */ - @media (pointer: coarse) { - .board { margin-top: auto; } - } - .floor, .target, .wall, diff --git a/src/views/GameView.svelte b/src/views/GameView.svelte index e1c71f9..f65e7fe 100644 --- a/src/views/GameView.svelte +++ b/src/views/GameView.svelte @@ -164,25 +164,29 @@ -
- -
+
+
+
+ +
- + +
+
{#if won}
@@ -239,6 +243,25 @@ color: var(--text-muted); } + /* Desktop: .play-stack is a passthrough that lets .board-wrap flex-grow + inside it, matching the previous behavior where the board occupied the + remaining column space. */ + .play-stack { + flex: 1 1 auto; + width: 100%; + min-height: 0; + display: flex; + flex-direction: column; + } + + .play-group { + flex: 1 1 auto; + min-height: 0; + width: 100%; + display: flex; + flex-direction: column; + } + .board-wrap { flex: 1 1 auto; width: 100%; @@ -248,20 +271,30 @@ } /* On touch devices: hide duplicated header action buttons (MobileControls - owns its own row at the bottom), tighten the column rhythm, and anchor - the puzzle to the bottom of .board-wrap so dead space accumulates above - the board (hidden behind HUD) instead of below it (between board and - dock). Closes the perceived "D-pad too far" gap on short puzzles. */ + owns its own row at the bottom), tighten the column rhythm, and center + the [board + 8px gap + dock] group inside the remaining viewport space. + `safe center` falls through to start-aligned when the group overflows so + tall finale puzzles don't get clipped above the HUD. */ @media (pointer: coarse) { .desktop-actions { display: none; } .game { gap: 10px; } + .play-stack { + justify-content: safe center; + } + + .play-group { + flex: 0 1 auto; + gap: 8px; + max-height: 100%; + } + .board-wrap { - display: flex; - align-items: flex-end; - justify-content: center; + flex: 1 1 auto; max-width: calc(100vw - 24px); + display: flex; + justify-content: center; } }