From 575a0a34deb7496643d8c60b9f551cfcc9200010 Mon Sep 17 00:00:00 2001 From: Precious Oritsedere Date: Thu, 24 Sep 2026 14:40:06 +0100 Subject: [PATCH 1/5] allow one-finger page scroll over map --- app/components/OpportunityMap.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/OpportunityMap.tsx b/app/components/OpportunityMap.tsx index d574ca8..bdd8a48 100644 --- a/app/components/OpportunityMap.tsx +++ b/app/components/OpportunityMap.tsx @@ -398,7 +398,7 @@ export function OpportunityMap({
{isAutoFramed && ( @@ -441,7 +441,7 @@ export function OpportunityMap({ Date: Thu, 24 Sep 2026 14:48:33 +0100 Subject: [PATCH 2/5] require two fingers to pan map on touch --- app/components/OpportunityMap.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/components/OpportunityMap.tsx b/app/components/OpportunityMap.tsx index bdd8a48..9ba3a12 100644 --- a/app/components/OpportunityMap.tsx +++ b/app/components/OpportunityMap.tsx @@ -331,6 +331,14 @@ export function OpportunityMap({ const zoom = d3 .zoom() .scaleExtent([1, 14]) + // One-finger touch scrolls the page; two fingers (or mouse/wheel) pan/zoom. + .filter((event) => { + if (event.type === "wheel") return true; + if (event.type === "touchstart" || event.type === "touchmove") { + return event.touches.length >= 2; + } + return !event.ctrlKey && !event.button; + }) .on("zoom", (event) => { zoomRoot.attr("transform", event.transform.toString()); }); From 89bd4e5a1d8f095a36e02f4cae030499e23735c1 Mon Sep 17 00:00:00 2001 From: Precious Oritsedere Date: Thu, 24 Sep 2026 14:50:32 +0100 Subject: [PATCH 3/5] clarify map touch gestures in caption --- app/components/MapLegend.tsx | 8 +++++--- app/components/OpportunityMap.tsx | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/components/MapLegend.tsx b/app/components/MapLegend.tsx index c19e72a..708b467 100644 --- a/app/components/MapLegend.tsx +++ b/app/components/MapLegend.tsx @@ -35,12 +35,14 @@ export function MapLegend({ ) : selectedLabel ? ( <> Viewing {selectedLabel} - . Click another area to change · drag to pan · scroll to zoom. + . Click another area to change · drag or two-finger drag to pan · + scroll or pinch to zoom. ) : ( - Click an area to filter, or use the location filter. Drag to pan · - scroll or pinch to zoom. + Click an area to filter, or use the location filter. Drag or use two + fingers to pan · scroll or pinch to zoom. One finger scrolls the + page. )}

diff --git a/app/components/OpportunityMap.tsx b/app/components/OpportunityMap.tsx index 9ba3a12..ad698ab 100644 --- a/app/components/OpportunityMap.tsx +++ b/app/components/OpportunityMap.tsx @@ -469,9 +469,10 @@ export function OpportunityMap({
Choropleth map of opportunities per {boundaryNoun(loadedBoundaryType)}. - Click an area to filter by that location, or use the location filter. - Drag to pan and scroll or pinch to zoom; zoom buttons are available - after the filters in the tab order. + Click or tap an area to filter by that location, or use the location + filter. On a mouse, drag to pan and scroll to zoom. On touch, use two + fingers to move or zoom the map; one finger scrolls the page. Zoom + buttons are available after the filters in the tab order.
Date: Thu, 24 Sep 2026 15:00:35 +0100 Subject: [PATCH 4/5] document map two-finger touch behaviour --- docs/explorer-map.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/explorer-map.md b/docs/explorer-map.md index 4b32a2a..c97eff1 100644 --- a/docs/explorer-map.md +++ b/docs/explorer-map.md @@ -39,6 +39,20 @@ Clicking an area calls `onAreaSelect` with a name, optional code, and boundary t `scopeAreaNames` highlights which areas are in the current selection. `selectedDistrict` is used when exactly one area or trust is chosen. Zoom controls and the legend sit in `MapZoomControls.tsx` and `MapLegend.tsx`. +## Touch and scroll + +The map sits on a long page, so one-finger swipes must scroll the page, not steal the gesture. + +In `OpportunityMap.tsx`: + +- The map container does **not** use `touch-none`, so the browser can scroll on one finger +- D3 zoom has a `.filter(...)` so touch pan/zoom only starts with **two or more** fingers +- Mouse drag and wheel still pan/zoom on desktop +- The `+` / `-` / reset buttons always zoom +- A short tap still selects an area (existing click vs drag threshold) + +Same rules on phones, tablets, and large touch screens. + ## What this is not The map does not call Monitor for geometry. It only uses Monitor for the counts. Changing the boundary files in `app/data/` is how you update the basemap. \ No newline at end of file From 602cda46e80128b4581eb4d26214b2e2d2ca47c7 Mon Sep 17 00:00:00 2001 From: Precious Oritsedere Date: Thu, 24 Sep 2026 15:30:53 +0100 Subject: [PATCH 5/5] let trackpad scroll the page; require Ctrl/pinch to zoom the map --- app/components/MapLegend.tsx | 10 +++++----- app/components/OpportunityMap.tsx | 19 +++++++++++++------ docs/explorer-map.md | 10 ++++++---- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/components/MapLegend.tsx b/app/components/MapLegend.tsx index 708b467..835dfe3 100644 --- a/app/components/MapLegend.tsx +++ b/app/components/MapLegend.tsx @@ -35,14 +35,14 @@ export function MapLegend({ ) : selectedLabel ? ( <> Viewing {selectedLabel} - . Click another area to change · drag or two-finger drag to pan · - scroll or pinch to zoom. + . Click another area to change · click-drag to pan · Ctrl+scroll or + pinch to zoom. ) : ( - Click an area to filter, or use the location filter. Drag or use two - fingers to pan · scroll or pinch to zoom. One finger scrolls the - page. + Click an area to filter, or use the location filter. Click-drag to + pan · Ctrl+scroll or pinch to zoom · two fingers on touch. Normal + scroll moves the page. )}

diff --git a/app/components/OpportunityMap.tsx b/app/components/OpportunityMap.tsx index ad698ab..eaebc34 100644 --- a/app/components/OpportunityMap.tsx +++ b/app/components/OpportunityMap.tsx @@ -331,13 +331,19 @@ export function OpportunityMap({ const zoom = d3 .zoom() .scaleExtent([1, 14]) - // One-finger touch scrolls the page; two fingers (or mouse/wheel) pan/zoom. + // Page scroll wins for one-finger touch and normal trackpad/mouse wheel. + // Map pan/zoom: primary-button drag, Ctrl/Meta+wheel (pinch), two-finger touch. .filter((event) => { - if (event.type === "wheel") return true; + if (event.type === "wheel") { + // Pinch is delivered as wheel+ctrlKey; plain two-finger scroll must + // reach the page, so only accept modified wheel here. + return event.ctrlKey || event.metaKey; + } if (event.type === "touchstart" || event.type === "touchmove") { return event.touches.length >= 2; } - return !event.ctrlKey && !event.button; + // Primary mouse button drag pans the map. + return event.button === 0; }) .on("zoom", (event) => { zoomRoot.attr("transform", event.transform.toString()); @@ -470,9 +476,10 @@ export function OpportunityMap({
Choropleth map of opportunities per {boundaryNoun(loadedBoundaryType)}. Click or tap an area to filter by that location, or use the location - filter. On a mouse, drag to pan and scroll to zoom. On touch, use two - fingers to move or zoom the map; one finger scrolls the page. Zoom - buttons are available after the filters in the tab order. + filter. Click and drag to pan. Pinch or hold Ctrl and scroll to zoom; on + touch, use two fingers to move or zoom the map. One finger or a normal + trackpad scroll moves the page. Zoom buttons are available after the + filters in the tab order.