ADR-023 — Porkchop plot mobile interaction (RFC-006 Option C)
Status · Accepted Date · 2026-04-28 Closes · RFC-006 (Porkchop plot mobile interaction) TA anchor · §components/porkchop-plot · §contracts/porkchop-mobile
Context
The porkchop plot on /plan is dense — 112 × 100 = 11,200 cells in a viewport that, on a 375 × 812 phone, gives each cell roughly 2.8 × 4.5 CSS pixels. Direct tap selection is impossible: thumbs land on a 5–7 cell region and the user can't tell which one was picked.
RFC-006 enumerated three options:
- Option A — pan/zoom with native pinch gestures.
- Option B — strip the colour overview, replace with a simpler date picker.
- Option C — full plot visible; touch-and-hold opens a magnifier bubble showing 5×5 cells at large scale; slide to navigate; lift to select.
PRD-002's educational core is the colour pattern itself ("see the launch windows opening"), so any option that hides that pattern (Option B) is a non-starter. Option A is richer but adds zoom/pan complexity (gesture conflicts with the underlying scroll, momentum, viewport math).
Closure was scheduled for the Slice 3 gate after device validation. We close it here in 3a-8 alongside the implementation; validation is via the Playwright e2e suite (tests/e2e/plan.spec.ts) running against a Pixel 5 mobile-Chromium device profile that exercises the touch-tap → magnifier → cell-select flow on every push to main. Real-iOS / real-Android user testing is deferred to the Slice 6 polish gate when WCAG 2.1 AA work also lands.
Decision
Option C — magnifier bubble. Touch and hold anywhere in the plot opens a 140 × 140 px circular magnifier showing a 5 × 5 cell window centred on the touch point. Sliding the finger updates which cells are shown and which is at the centre crosshair. Lifting selects the centre cell. The Δv value of the centre cell is rendered inside the bubble for confirmation before lift.
Desktop interaction is unchanged: hover highlights, click selects.
Implementation lives in src/routes/plan/+page.svelte (commit 12e5359) — about 50 lines of touch event handling, a separate magnifier <canvas> overlay, and a drawMag() helper that reads from the same in-memory grid as the main heatmap.
Answers to RFC-006 open questions
- Should the magnifier show Δv values numerically inside each enlarged cell? No — the centre cell's Δv is shown at the bottom of the bubble (the only cell that matters at lift time). Rendering 25 small text labels inside the bubble would clutter the colour pattern.
- What happens when the user lifts their finger outside the plot area — cancel or select nearest cell? Selects whichever cell the magnifier was centred on at lift. The magnifier itself is constrained to the plot area (
cellFromCanvasreturns null outside), so dragging off the plot hides the magnifier; lifting in that state is a no-op (touch end withmag === nulldoesn't change selection). - Should there be a "best window" auto-suggest button? Deferred. The full overview already makes the cheap blue zones visually obvious; an auto-suggest button would short-circuit the educational moment. v2 candidate if usability testing shows users skipping the colour pattern entirely.
Rationale
Option C preserves the educational colour overview that is PRD-002's core claim. The magnifier is a familiar mobile pattern (iOS text selection, Notes app annotations) so users hit it without onboarding. Touch-action: none on the canvas prevents native scroll/zoom from conflicting with the gesture.
The 5 × 5 region is a balance: fewer cells (3 × 3) makes the magnifier feel imprecise; more (7 × 7) shrinks each enlarged cell back below the thumb's hit-test pad and defeats the purpose. 5 × 5 with a 140 px bubble gives each enlarged cell ~28 px on a side — comfortable thumb selection.
The magnifier offsets above the touch point by 90 px so the user's finger doesn't occlude the cells they're navigating.
Alternatives considered
- Option A (pinch-to-zoom). Richer but introduces gesture conflicts and viewport-coordinate complexity. Strong v2 candidate once the mobile baseline is established.
- Option B (strip the heatmap, pure date picker). Loses the educational core. Rejected.
- Tap-only with hit-pad expansion. Tested informally: even with 16 px hit-pad per cell, target overlap is ambiguous in the dense low-Δv basin where every adjacent cell is also viable. Doesn't actually solve the precision problem.
Consequences
Positive: full porkchop pattern visible at native resolution on mobile; precise selection via familiar pattern; no gesture conflicts; same code path as desktop for everything except the magnifier overlay.
Negative: no current way to pan a non-existent zoomed-in view (intentionally — Option A is the v2 answer if pan/zoom becomes desirable). Magnifier consumes the second touch for two-finger gestures; touchstart guards on e.touches.length !== 1 so a pinch attempt is a no-op rather than a conflicting selection.