From 10f712f9d7bff63b3ef80f6754d326caa6942a79 Mon Sep 17 00:00:00 2001 From: Kpa-clawbot Date: Fri, 3 Apr 2026 16:54:36 -0700 Subject: [PATCH] fix: restructure scroll containers for iOS status bar tap-to-scroll (#330) (#554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes #330 — iOS status bar tap-to-scroll broken because `#app` had `overflow: hidden`, preventing `` from being the scroll container. ## Approach: Option B from the issue Instead of a JS polyfill, this restructures scroll containers so `` is the primary scroll container by default, which iOS Safari requires for native status-bar tap-to-scroll. ### How it works **`#app` default (body-scroll mode):** Uses `min-height` instead of fixed `height`, no `overflow: hidden`. Content pushes beyond the viewport and body scrolls naturally. **`#app.app-fixed` (fixed-layout mode):** Restores the original `height: calc(100dvh - 52px); overflow: hidden` for pages that need constrained containers. The router in `app.js` toggles this class based on the current page. ### Fixed-layout pages (`.app-fixed`) These pages need fixed-height containers and are unchanged in behavior: - **packets** — virtual scroll requires fixed-height `.panel-left` to calculate visible rows - **nodes** — split-panel layout with independently scrollable panels - **map** — Leaflet requires fixed-dimension container - **live** — Leaflet map (also has its own `#app:has(.live-page)` override in live.css) - **channels** — split-panel chat layout - **audio-lab** — split-panel layout ### Body-scroll pages (no `.app-fixed`) These pages now let the body scroll, enabling iOS tap-to-scroll: - **analytics** — removed `overflow-y: auto; height: 100%` - **observers** — removed `overflow-y: auto; height: calc(100vh - 56px)` - **traces** — removed `overflow-y: auto; height: 100%` - **home** — removed `#app:has(.home-hero)` override (no longer needed) - **compare** — removed inline `overflow-y:auto; height:calc(100vh - 56px)` - **perf** — removed inline `height:100%; overflow-y:auto` - **observer-detail** — removed inline `overflow-y:auto; height:calc(100vh - 56px)` - **node-analytics** — removed inline `height:100%; overflow-y:auto` ### Files changed | File | Change | |------|--------| | `public/style.css` | `#app` default → `min-height`; added `.app-fixed` class | | `public/app.js` | Router toggles `.app-fixed` based on page | | `public/home.css` | Removed `#app:has()` workaround | | `public/compare.js` | Removed inline overflow/height | | `public/perf.js` | Removed inline overflow/height | | `public/observer-detail.js` | Removed inline overflow/height | | `public/node-analytics.js` | Removed inline overflow/height | ### What's preserved - Sticky nav (`position: sticky; top: 0`) — works with body scroll - Split-panel resize handles — unchanged, still in fixed containers - Virtual scroll on packets page — unchanged, `.panel-left` still has fixed height - Leaflet maps — unchanged, containers still have fixed dimensions - Mobile responsive overrides — unchanged Co-authored-by: you --- public/app.js | 3 +++ public/compare.js | 2 +- public/home.css | 3 +-- public/node-analytics.js | 2 +- public/observer-detail.js | 2 +- public/perf.js | 2 +- public/style.css | 13 +++++++++---- 7 files changed, 17 insertions(+), 10 deletions(-) diff --git a/public/app.js b/public/app.js index 9272955d..6323e47e 100644 --- a/public/app.js +++ b/public/app.js @@ -463,6 +463,9 @@ function navigate() { currentPage = basePage; const app = document.getElementById('app'); + // Pages with fixed-height containers (maps, virtual-scroll, split-panels) + const fixedPages = { packets: 1, nodes: 1, map: 1, live: 1, channels: 1, 'audio-lab': 1 }; + app.classList.toggle('app-fixed', basePage in fixedPages); if (pages[basePage]?.init) { const t0 = performance.now(); pages[basePage].init(app, routeParam); diff --git a/public/compare.js b/public/compare.js index ca263eb6..75dab998 100644 --- a/public/compare.js +++ b/public/compare.js @@ -48,7 +48,7 @@ if (typeof window !== 'undefined') window.comparePacketSets = comparePacketSets; packetsB = []; currentView = 'summary'; - app.innerHTML = '
' + + app.innerHTML = '
' + '