Files
sokoban/plans/reports/brainstorm-260427-1151-mobile-comfort.md
T
tiennm99 fe2d98e710 docs: document mobile comfort overhaul
Update codebase summary, system architecture, changelog, roadmap and
PDR to cover the new mobile input layer, haptics, gesture blocking
and PWA. Refresh README features. Add the brainstorm, plan and review
artifacts under plans/.
2026-04-27 20:38:23 +07:00

4.9 KiB

Brainstorm — Mobile Comfort for Sokoban

  • Date: 2026-04-27
  • Branch: main
  • Repo: tiennm99/sokoban (Svelte 5 + Vite, DOM-rendered tiles)

Problem

Game is keyboard-only (arrows / WASD / U / Z / R / Esc). On phones it's unplayable: no input affordance, browser gestures (pull-to-refresh, double-tap zoom, long-press select) interfere, no thumb-friendly layout, no offline install.

User initially asked for tap-to-walk pathfinding, then pivoted to a holistic "comfortable for mobile users" goal.

Decisions (Approved)

Area Decision
Input On-screen D-pad, tap-only (no auto-repeat)
Layout Bottom-right D-pad, bottom-left action stack (Undo / Restart / Levels)
Visibility @media (pointer: coarse) — touch devices only; desktop unchanged
Top HUD on mobile Status only: LVL n Moves Best
Browser gestures Block pull-to-refresh, double-tap zoom, long-press select on game area
Big-level fit Lower minTile 10→16; reserve ~120px for bottom controls in computeTileSize; rely on existing .board-wrap scroll for finale dungeons
Safe areas env(safe-area-inset-*) on bottom controls only
Haptics navigator.vibrate(10) on box push, vibrate(60) on win. No buzz on plain step or wall bump
PWA Full offline via vite-plugin-pwa (workbox); manifest, SW autoUpdate, 192/512 icons

Approaches considered & rejected

Approach Why rejected
Tap-to-walk + BFS pathfinding More UX surface (cancel, push semantics, animation queue); user picked D-pad
Swipe gestures Less discoverable; gesture conflict with scrolling on big levels
Tap + swipe hybrid Higher complexity for marginal benefit
Hold-to-repeat D-pad User chose tap-only — explicit intent matches Sokoban's "every move counts"
Pinch-zoom + 2-finger pan Defer; auto-fit + scroll wrapper is sufficient
Manifest-only PWA (no SW) User wants full offline

Architecture

GameView.svelte
  ├─ top HUD (mobile: status only)
  ├─ Board.svelte                       (touch-action: none)
  ├─ MobileControls.svelte    NEW       (pointer: coarse only)
  │   ├─ left stack: UNDO / RESTART / LEVELS
  │   └─ right D-pad: ▲ ◀ ▶ ▼
  └─ overlay (win)

lib/core/
  └─ haptics.js                NEW       pulse(ms) — no-op fallback

vite.config.js                  +vite-plugin-pwa
public/manifest.webmanifest     generated
index.html                      viewport meta + theme-color

D-pad calls existing tryMove(dx,dy) / undo() / restart() — no BoardModel changes. Haptics fires from GameView.syncFromModel() by comparing previous box positions. No pathfinding, no animation queue.

Files

New

  • src/views/MobileControls.svelte — D-pad + action stack (~90 LOC)
  • src/lib/core/haptics.js — ~15 LOC
  • public/manifest.webmanifest + icons (or generated by plugin)

Modified

  • src/views/GameView.svelte — render <MobileControls>, wire haptics, adjust top HUD on mobile, update computeTileSize margin
  • src/views/Board.svelte — touch-action: none, user-select: none
  • src/views/AppButton.svelte — touch-action: manipulation
  • src/app.css — global selection lock, safe-area helpers
  • index.html — viewport meta maximum-scale=1, user-scalable=no, theme-color
  • vite.config.js — vite-plugin-pwa
  • package.json — add devDep

Risks & Mitigations

Risk Mitigation
iOS Safari ignores navigator.vibrate Acceptable — feature degrades silently
GH Pages base path breaks SW scope Set base correctly in vite.config and manifest start_url / scope
D-pad overlaps board on tiny landscape screens Reserve ~120px vertical margin in computeTileSize; left stack collapses to 2 rows if needed
maximum-scale=1 blocks user accessibility zoom Acceptable trade-off for game UX; alternatively keep zoom but rely on touch-action: none on board only
Service worker stale-cache after deploy registerType: 'autoUpdate' + workbox skipWaiting

Success Criteria

  • Phone (≤480px width): D-pad visible, all 155 levels playable one-handed, no browser pull-to-refresh, no text selection on long-press
  • Push a box → short vibrate; solve a level → longer vibrate (where supported)
  • "Add to Home Screen" works; opens fullscreen; plays offline after first load
  • Desktop layout unchanged (D-pad hidden, keyboard intact)
  • Lighthouse PWA installability check passes

Out of scope

  • Tap-to-walk pathfinding (originally requested, replaced by D-pad)
  • Swipe gestures
  • Pinch-zoom / pan
  • Orientation lock
  • Settings toggle for haptics on/off

Unresolved questions

  • Should maximum-scale=1 be relaxed if accessibility-zoom matters more than block-double-tap-zoom? (Currently chose strict for cleaner UX)
  • Icon source: reuse public/favicon.png or generate a Nord-themed maskable icon set?
  • Do we want a tiny in-game settings toggle later (haptics on/off, theme)?