From 18d63d07c7b6eb549970910cf3ee83a7703f373c Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 28 Apr 2026 23:11:43 +0700 Subject: [PATCH] fix(mobile): close dock-internal gap by top-aligning the d-pad Round 1 anchored the board to .board-wrap's bottom but exposed a 32px gap caused by .mobile-dock { align-items: flex-end } combined with .dock-left (148px tall) being taller than .dpad (116px). With both columns bottom-aligned, the d-pad's top sat ~32px below the action stack's top, so the arrows still felt far from the board. Switch the dock to align-items: stretch so each column picks its own cross-axis position: action stack stays bottom-anchored (closer to the thumb), d-pad uses align-self: flex-start to hug the dock top (closer to the board). Movement is the primary interaction; UNDO/RESET are secondary. Shrink arrows 56 -> 48px (still well above 44pt HIG) and bump grid gap 4 -> 6px. Trim the JS computeTileSize coarse-pointer chrome margin 220 -> 195 to reflect the slimmer dock so the board can grow into the freed space on tall puzzles. Add an explicit margin-top: auto on .board inside a coarse media query in Board.svelte as a belt-and-suspenders cross-axis push that survives future parent-flex refactors. Combined with the round-1 changes, the board-bottom to arrow-top distance drops from ~42px to ~10px on touch viewports. Desktop is untouched (all changes gated by @media (pointer: coarse)). --- src/views/Board.svelte | 8 ++++++++ src/views/GameView.svelte | 4 +++- src/views/MobileControls.svelte | 13 +++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/views/Board.svelte b/src/views/Board.svelte index 9ec0cba..ab13a3f 100644 --- a/src/views/Board.svelte +++ b/src/views/Board.svelte @@ -107,6 +107,14 @@ -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 7ca764e..e1c71f9 100644 --- a/src/views/GameView.svelte +++ b/src/views/GameView.svelte @@ -43,7 +43,9 @@ const minTile = 16; // Dock is now in-flow (flex row at bottom); margin only covers header + hud + padding. const isCoarse = typeof window !== 'undefined' && window.matchMedia('(pointer: coarse)').matches; - const margin = isCoarse ? 220 : 120; // header + hud (+ in-flow mobile dock) + padding + // 195 reflects the slimmer mobile chrome after R6+R7 (top-aligned d-pad + // at 48px arrows). If the dock layout changes, retune this. + const margin = isCoarse ? 195 : 120; // header + hud (+ in-flow mobile dock) + padding const maxByWidth = Math.floor((window.innerWidth - 80) / level.width); const maxByHeight = Math.floor((window.innerHeight - margin - 100) / level.height); return Math.max(minTile, Math.min(maxTile, maxByWidth, maxByHeight)); diff --git a/src/views/MobileControls.svelte b/src/views/MobileControls.svelte index fd70371..e8aee96 100644 --- a/src/views/MobileControls.svelte +++ b/src/views/MobileControls.svelte @@ -108,10 +108,13 @@ } @media (pointer: coarse) { + /* stretch lets each column choose its own cross-axis position so the + D-pad can hug the dock top (closer to board) while the action stack + stays bottom-anchored (closer to thumb). */ .mobile-dock { display: flex; justify-content: space-between; - align-items: flex-end; + align-items: stretch; gap: 12px; width: 100%; padding: @@ -125,17 +128,19 @@ .dock-left { display: flex; flex-direction: column; + justify-content: flex-end; gap: 8px; } .dpad { display: grid; - grid-template-columns: 56px 56px 56px; - grid-template-rows: 56px 56px; + grid-template-columns: 48px 48px 48px; + grid-template-rows: 48px 48px; grid-template-areas: ". up . " "left down right"; - gap: 4px; + gap: 6px; + align-self: flex-start; } }