From 33e737e453201977786d70ad5cf1a2b13d383bf0 Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 1 Sep 2026 19:48:51 +0200 Subject: [PATCH] fix(mobile): keep the dashboard filter row from widening the layout viewport --- client/src/index.css | 9 +++++- client/src/mobile/mobile.css | 11 +++++++ .../mobile/screens/dashboard/MDashboard.tsx | 30 ++++++++++++------- .../unit/mobile/dashboard/MDashboard.test.tsx | 15 ++++++++++ 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/client/src/index.css b/client/src/index.css index 115a7eebe..04399e6ce 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -15,7 +15,14 @@ body { height: 100%; overflow: auto; overscroll-behavior: none; -webkit-overflow reloads the installed PWA. Desktop/tablet keep the rules above. */ @media (max-width: 767px) { html { overflow: visible; overscroll-behavior: none; } - body { height: auto; overflow: visible; } + /* overflow-x clip (not hidden: clip creates no scroll container) so a screen + that accidentally overflows sideways cannot widen the document. Android's + forced-zoom setting reacts to a wider document by growing the layout + viewport that position:fixed resolves against, which pushed the top bar + and the dock off the right edge on narrow phones. On body, never on html: + body's overflow only propagates to the viewport while html stays visible, + and the shared body scroll lock relies on that propagation. */ + body { height: auto; overflow: visible; overflow-x: clip; } /* Transparent so the shell's own background layer, which sits behind the page on a negative z-index, is not painted over: a negative layer goes above the canvas but below the background of ordinary blocks, and body is one. The diff --git a/client/src/mobile/mobile.css b/client/src/mobile/mobile.css index 6f6049769..784b1d914 100644 --- a/client/src/mobile/mobile.css +++ b/client/src/mobile/mobile.css @@ -363,3 +363,14 @@ -webkit-appearance: none; appearance: none; } + +/* Horizontal scroller without a visible scrollbar, for control rows that must + survive narrow viewports and large system font scales (dashboard filter). */ +.m-hscroll { + overflow-x: auto; + overscroll-behavior-x: contain; + scrollbar-width: none; +} +.m-hscroll::-webkit-scrollbar { + display: none; +} diff --git a/client/src/mobile/screens/dashboard/MDashboard.tsx b/client/src/mobile/screens/dashboard/MDashboard.tsx index d30cad247..dc2c10072 100644 --- a/client/src/mobile/screens/dashboard/MDashboard.tsx +++ b/client/src/mobile/screens/dashboard/MDashboard.tsx @@ -132,17 +132,25 @@ export default function MDashboard(): React.ReactElement { return ( <>
- - value={tripFilter} - onChange={setTripFilter} - variant="intrinsic" - options={[ - { value: 'planned', label: t('dashboard.filter.planned') }, - { value: 'archive', label: t('dashboard.archived') }, - { value: 'completed', label: t('dashboard.mobile.completed') }, - ]} - /> - setSubOpen(true)}> + {/* The chips scroll inside their own flexible box on narrow viewports + or large system font scales; without it this row was the widest + thing on the page and dragged the fixed bars off-screen under + Android's forced zoom. min-w-max keeps the pill track wrapping + its chips instead of getting squeezed to the box. */} +
+ + value={tripFilter} + onChange={setTripFilter} + variant="intrinsic" + className="min-w-max" + options={[ + { value: 'planned', label: t('dashboard.filter.planned') }, + { value: 'archive', label: t('dashboard.archived') }, + { value: 'completed', label: t('dashboard.mobile.completed') }, + ]} + /> +
+ setSubOpen(true)}>