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.
This commit is contained in:
tiennm99 committed 2026-04-28 23:48:13 +07:00
1 parent 179f052c89
commit 16520d125d
2 files changed
+58 -33

No files matched your search

-8
View File
@@ -107,14 +107,6 @@
-webkit-user-select: none; -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, .floor,
.target, .target,
.wall, .wall,
+58 -25
View File
@@ -164,25 +164,29 @@
</div> </div>
</header> </header>
<div class="board-wrap"> <div class="play-stack">
<Board <div class="play-group">
width={level.width} <div class="board-wrap">
height={level.height} <Board
walls={level.walls} width={level.width}
targets={level.targets} height={level.height}
floors={level.floors} walls={level.walls}
{player} targets={level.targets}
{boxes} floors={level.floors}
{tileSize} {player}
/> {boxes}
</div> {tileSize}
/>
</div>
<MobileControls <MobileControls
onMove={tryMove} onMove={tryMove}
onUndo={undo} onUndo={undo}
onRestart={restart} onRestart={restart}
{onLevels} {onLevels}
/> />
</div>
</div>
{#if won} {#if won}
<div class="overlay"> <div class="overlay">
@@ -239,6 +243,25 @@
color: var(--text-muted); 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 { .board-wrap {
flex: 1 1 auto; flex: 1 1 auto;
width: 100%; width: 100%;
@@ -248,20 +271,30 @@
} }
/* On touch devices: hide duplicated header action buttons (MobileControls /* On touch devices: hide duplicated header action buttons (MobileControls
owns its own row at the bottom), tighten the column rhythm, and anchor owns its own row at the bottom), tighten the column rhythm, and center
the puzzle to the bottom of .board-wrap so dead space accumulates above the [board + 8px gap + dock] group inside the remaining viewport space.
the board (hidden behind HUD) instead of below it (between board and `safe center` falls through to start-aligned when the group overflows so
dock). Closes the perceived "D-pad too far" gap on short puzzles. */ tall finale puzzles don't get clipped above the HUD. */
@media (pointer: coarse) { @media (pointer: coarse) {
.desktop-actions { display: none; } .desktop-actions { display: none; }
.game { gap: 10px; } .game { gap: 10px; }
.play-stack {
justify-content: safe center;
}
.play-group {
flex: 0 1 auto;
gap: 8px;
max-height: 100%;
}
.board-wrap { .board-wrap {
display: flex; flex: 1 1 auto;
align-items: flex-end;
justify-content: center;
max-width: calc(100vw - 24px); max-width: calc(100vw - 24px);
display: flex;
justify-content: center;
} }
} }