Mobile: per-host status line forces horizontal page scroll at 320px #42

Closed
opened 2026-08-09 16:50:20 +02:00 by clawbot · 1 comment
Collaborator

Found by the responsive-layout harness added for #13
(make frontend-viewport-test). This is a real defect in the layout, not a
harness artifact.

Symptom

At a 320px viewport the page scrolls horizontally: documentElement.scrollWidth
is 350 against a 320px layout viewport. Reproduced at every run, only at 320px —
667, 767, 768, 769, 844 and 1280 are clean.

Cause

.status-text in hostRowHTML() (src/main.js) carries whitespace-nowrap,
and once a tick has populated it the content reads e.g.
min 167ms / med 167ms / avg 167ms / max 167ms. Measured at 320px that is 325px
of unbreakable text inside a 270px box, and since the element does not clip, the
overflow propagates all the way up to the document:

div.status-text.text-xs | rect 25..295 w270 | scrollWidth 325 / clientWidth 270
                        | overflow-x: visible  white-space: nowrap

The max-width: 768px block in src/styles.css sets the info block to
width: 100% but never addresses this line, so the narrow layout inherits a
nowrap string sized for the 420px desktop column.

Suggested fix

Allow the status line to wrap (or shorten it) below the breakpoint. Any fix is
fine as long as make frontend-viewport-test reports no-horizontal-overflow
passing at 320x568 — the harness already asserts this and names the offending
element on failure.

Note for whoever picks this up

window.innerWidth is useless for detecting this. Under mobile emulation Chrome
lets innerWidth grow to the width of the overflowing content — it reports 350,
matching the overflow exactly, so a check written as
scrollWidth <= innerWidth passes while the page is visibly broken. The
harness compares against Math.min(innerWidth, documentElement.clientWidth)
instead.

Ref #13, #5, #2

Found by the responsive-layout harness added for #13 (`make frontend-viewport-test`). This is a real defect in the layout, not a harness artifact. ## Symptom At a 320px viewport the page scrolls horizontally: `documentElement.scrollWidth` is 350 against a 320px layout viewport. Reproduced at every run, only at 320px — 667, 767, 768, 769, 844 and 1280 are clean. ## Cause `.status-text` in `hostRowHTML()` (`src/main.js`) carries `whitespace-nowrap`, and once a tick has populated it the content reads e.g. `min 167ms / med 167ms / avg 167ms / max 167ms`. Measured at 320px that is 325px of unbreakable text inside a 270px box, and since the element does not clip, the overflow propagates all the way up to the document: ``` div.status-text.text-xs | rect 25..295 w270 | scrollWidth 325 / clientWidth 270 | overflow-x: visible white-space: nowrap ``` The `max-width: 768px` block in `src/styles.css` sets the info block to `width: 100%` but never addresses this line, so the narrow layout inherits a nowrap string sized for the 420px desktop column. ## Suggested fix Allow the status line to wrap (or shorten it) below the breakpoint. Any fix is fine as long as `make frontend-viewport-test` reports `no-horizontal-overflow` passing at 320x568 — the harness already asserts this and names the offending element on failure. ## Note for whoever picks this up `window.innerWidth` is useless for detecting this. Under mobile emulation Chrome lets `innerWidth` grow to the width of the overflowing content — it reports 350, matching the overflow exactly, so a check written as `scrollWidth <= innerWidth` passes while the page is visibly broken. The harness compares against `Math.min(innerWidth, documentElement.clientWidth)` instead. Ref #13, #5, #2
Author
Collaborator

Fixed in #50 (base next).

Dropped whitespace-nowrap from .status-text and made each
label / value / separator group its own unbreakable span, so the line wraps
only between stats and never begins with a /. At 320px it renders as two
right-aligned rows; in the 420px desktop column it still fits on one row, so
the wide layout is unchanged.

Verified with make frontend-viewport-test in a throwaway clone with
#44 merged on top (the harness is not
on next yet, and none of its files are in this branch). At 320x568,
no-horizontal-overflow goes FAIL (scrollWidth 350 vs viewport 320) to PASS,
and nothing-past-viewport-edge with it: 5/8 to 7/8 checks. 1280x800 stays
7/7; 769x1024 and 844x390 are unchanged. make check green.

The remaining 320px failure is tap-targets-44px
(#43), untouched here.

Fixed in https://git.eeqj.de/sneak/netwatch/pulls/50 (base `next`). Dropped `whitespace-nowrap` from `.status-text` and made each `label / value / separator` group its own unbreakable span, so the line wraps only between stats and never begins with a `/`. At 320px it renders as two right-aligned rows; in the 420px desktop column it still fits on one row, so the wide layout is unchanged. Verified with `make frontend-viewport-test` in a throwaway clone with https://git.eeqj.de/sneak/netwatch/pulls/44 merged on top (the harness is not on `next` yet, and none of its files are in this branch). At 320x568, `no-horizontal-overflow` goes FAIL (`scrollWidth` 350 vs viewport 320) to PASS, and `nothing-past-viewport-edge` with it: 5/8 to 7/8 checks. 1280x800 stays 7/7; 769x1024 and 844x390 are unchanged. `make check` green. The remaining 320px failure is `tap-targets-44px` (https://git.eeqj.de/sneak/netwatch/issues/43), untouched here.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/netwatch#42