Repository navigation
Description
Reported
Preferences (global) is broken on mobile — horizontal scroll, and buttons don't wrap so the text
is squashed into one vertical line.
Both confirmed, against production, signed in, at six phone widths in two engines. They are two
separate defects, and a third one is why the first has been reported and closed as
"not reproducible" twice (#333).
1. The horizontal scroll is real, and it exists ONLY in WebKit
main.scrollWidth - main.clientWidth on /console/preferences:
| viewport | Chromium | WebKit |
|---|---|---|
| 320 | 0 | 59 |
| 360 | 0 | 27 |
| 375 | 0 | 18 |
| 390 | 0 | 9 |
| 414 | 0 | 0 |
| 430 | 0 | 0 |
Culprit: the Timezone row — store/console/src/pages/Preferences.tsx:192-207.
<div className="flex justify-between items-center py-2.5 text-sm gap-3">
<span className="text-muted font-medium">Your timezone</span>
<select … className="text-sm bg-paper border border-line rounded-lg px-3 py-2 max-w-[60%]">Measured on that exact element (scrollWidth - clientWidth): +76px at 320, +34px at 390, which
propagates up through the card and the Page container until the whole page pans.
The <select> is a flex item with min-width: auto, so its automatic minimum size is its content
width — and its content is the longest option (America/Argentina/Buenos_Aires,
Not set — agents will say UTC). Chromium clamps that; WebKit does not, so max-w-[60%] caps
the used width and the minimum still wins. There is no min-w-0 on it, and nothing truncates.
Every phone that matters runs WebKit.
2. The squashed text is the Connections rows
store/console/src/components/AccountConnections.tsx:163 (and :132 for the GitHub row):
<div className="flex items-start justify-between gap-3 mb-3">
<div className="text-sm min-w-0 [overflow-wrap:anywhere]"> … Gmail · connected as … </div>
<div className="flex gap-2 shrink-0"> ← Reconnect + Disconnect, ~185px, will not wrapAt 320px the button pair takes the row and the text column is left with 67px, and runs 119px tall —
measured, both engines. The email renders as:
connecte
d as
serge.pro.
job@gmail
.com
Note the two halves of this are individually correct and were added on purpose:
[overflow-wrap:anywhere] (from #333) is what stops the unbreakable email token overflowing the
page, and shrink-0 is what stops the buttons collapsing. Together they mean the row cannot
overflow and cannot wrap, so all of the pressure lands on the one element that will yield — the
text. The row needs flex-wrap so the buttons drop below the label at narrow widths; that is what
the chip rows in VoiceFields.tsx:202,221 already do.
At 390px it is not catastrophic but still wrong: the Gmail label wraps to three lines beside two
buttons.
3. Why this was closed as "not reproducible" twice — the guard is Chromium-only
playwright.config.ts:26-31 declares exactly one project:
projects: [ { name: "chromium", use: { ...devices["Desktop Chrome"] } } ],So measureOverflow, the wide geometry walk and the docOv tripwire — all of the machinery #333
built and hardened specifically for "the page scrolls sideways on mobile" — run in the one engine
where this bug does not exist. Two rounds of investigation on #333 correctly found nothing on
Preferences, because in Chromium there is nothing to find.
The measurement is otherwise sound; it just has no WebKit to run in. Adding a mobile-WebKit project
is what turns this class of defect from "reported by the owner on a phone" into "caught in CI".
Suggested fix
- Timezone select —
min-w-0on the select (the automatic minimum size is the actual cause),
and let it take the full row width on a phone (w-full sm:max-w-[60%]) rather than sitting beside
the label. A<select>in a flex row needsmin-w-0as a rule; this is the first one measured,
probably not the only one. - Connections rows —
flex-wrapon the two rows inAccountConnections.tsx(:132,:163), so
the button group drops under the label instead of crushing it. - Guard — add a WebKit mobile project to
playwright.config.tsand run the existing overflow
guards under it. Without this, fix (1) has no regression test in the engine that needs it.
Method
Playwright against https://proagentstore.online/console/preferences, real session, viewports
320/360/375/390/414/430, chromium 141 and WebKit 26.4. Measured documentElement and main
overflow plus every element's right edge and own scrollWidth. Screenshots at 320 and 390 confirm
both symptoms visually.
Supersedes the Preferences half of #333 (the Profile half of that ticket is fixed and verified).
Files: store/console/src/pages/Preferences.tsx:192-207,
store/console/src/components/AccountConnections.tsx:132,163, playwright.config.ts:26-31,
e2e/console.spec.ts (measureOverflow).
Activity
serge-ivo commentedon Aug 7, 2026
Self-review: fix (3) will probably turn CI red across routes this ticket never measured — scope it deliberately
I recommended adding a WebKit mobile project to
playwright.config.tsso the overflow guards run in the engine where the bug exists. That is right, and it is also the riskiest item here: I only measured/console/preferences. The console has a dozen top-level routes and 14 instance surfaces, none of them ever measured in WebKit, and the cause found here (<select>min-width:autoresolving to the widest option, which Chromium clamps and WebKit does not) is a generic pattern, not a one-off. Every<select>in a flex row is a candidate.So adding the project as a blanket CI gate could fail a batch of specs on the first run and block merges on work unrelated to whoever adds it. Two saner shapes:
continue-on-error/ a separate reporting job) for one cycle, triage what it finds, then make it required.Either way the finding it produces is a feature, not a problem — it is the same class of bug the owner keeps reporting from a phone. It just should not arrive as a surprise red main.
Two smaller notes on my own measurement:
msgstring, a connected Drive row, an error line)".Fixes (1) and (2) —
min-w-0+ full-width-on-phone for the select,flex-wrapon the two Connections rows — are safe, local, and carry no behavioural risk.serge-ivo commentedon Aug 7, 2026
The guard's other hole: it sweeps the right routes with content that cannot reproduce the bug
Found while fixing the same class of defect on Notifications (horizontal scroll on mobile, reported by the owner today).
The cause there was identical in kind: a row's text column is a flex item with default
min-width:auto, so it cannot shrink below its longest unbreakable token — and a notification body carries paths (pas/platform: Here are the 10 largest files in …). Measured +110px at 320, +40px at 390, and unlike the timezone select this one overflows in Chromium too.So why is CI green?
/console/notificationsis in the guard's route list (console.spec.ts:1938). The answer is atconsole.spec.ts:163:The guard visits the page and measures an empty list. It has never seen a notification row. The route appears covered and the coverage is nominal.
That generalises past this one fixture. Both defects found this week are min-content overflow, whose trigger is the longest string on the page — so a guard fed short, tidy fixture data is structurally unable to catch the entire class, in any engine. The WebKit project (fix 3) is necessary and would still not have caught Notifications.
Suggested addition to fix (3): give the fixtures a hostile string. Every mock that feeds user-visible text — notification bodies, agent names, instance names, file names, error messages, connector account labels — should include at least one row carrying a long unbroken token (a repo path, a URL, a 40-char email). That is one edit per fixture and it turns the existing guards, which are otherwise sound, into something that can actually fail.
The Notifications fix itself (
min-w-0+[overflow-wrap:anywhere], matchingAccountConnections.tsx) is verified in the live DOM in both engines: 110 → 0 at 320px, 40 → 0 at 390px.fix(console): a select's own option label widened the page in WebKit,…
fix(console): a notification body wraps instead of widening the page
refactor(console): four button shapes and one card, instead of forty-…
refactor(console,admin): a floor under the type scale and a floor und…
test(console): the touch half of the mute invariant, and the defect i…
fix(console): a select's option list is contained, not merely told to…
serge-ivo commentedon Aug 8, 2026
Fixed in
79bf551. CI is green — run on that commit passed, which is the verification that matters here, andmainis clean for the first time in 13 commits.Root cause, and why the previous fix looked right
WebKit's menulist renderer silently overrides
overflowon a<select>— computedoverflow-xreadsvisiblewith the rule live and matching. The control's whole option list, sized by the widest option rather than the selected label, therefore stays in every ancestor's layout overflow.contain: paintis the declaration WebKit cannot decline, and that is the fix.8dd92f1was measured honestly and was still wrong — on macOS WebKit, where the labels happen to fit. CI runs Linux WebKit, where the page never stopped panning. macOS WebKit is green with or without the change, which is exactly why two rounds of measurement on #333 and the fix here all reported clean. Reproduced inmcr.microsoft.com/playwright:v1.60.0-noble, failing the same 10 specs as CI run 31233309026.This corrects the guidance in this repo: "check it in WebKit" is not sufficient on a Mac. The engine that matters is Linux WebKit.
Measurements (Linux WebKit,
main.scrollWidth − main.clientWidth)Rejected after measuring, all still 68px at 320:
overflow: clip,overflow: hidden !important,field-sizing: content,min-width: 0.appearance: nonedoes reach 0 and was rejected — it works by making the control stop being a native menulist, which deletes the disclosure arrow from ~40 selects.Two diagnoses in the earlier tickets were backwards, and are corrected in the code: it is the widest option, not the selected label (
voice-tts-providerwidened its card by 43px while displaying a value that fits), and it was never only the timezone row — the 68px came from two cards, the widest option on the page belonging to a Voice select.The new guard asserts the mechanism, not the symptom
no select widens its own row at 320/390px. The failure signature —mainOv=68withwideempty — is the one classwidestructurally cannot name, because the escaping content belongs to a native control with no element to measure. Attribution works by taking each control out of flow and re-measuring. Against the reverted stylesheet it names all four real offenders, and it is strictly stronger than the page check: it catches 11px at 390px thatmainOvreports as 0.Verified rather than assumed:
box-shadowis not clipped by paint containment, soselect:focus's ring is unchanged. #390's type-scale floor and #389's touch-target floor are untouched and green.Left for a separate ticket:
store/admin/src/index.csscarries the sameselectrule and the same latent defect, but has no mobile geometry guard and no measurement — outside this change's blast radius.feat(coding): a Pulls panel, and a GitHub cache whose key is a tenanc…
fix(admin): the operator portal's selects are contained, and are now …
fix(a11y): a focused <select> is visible in Safari, where it showed n…
fix(console): the connected-account buttons can wrap, because `shrink…
ci(e2e): the WebKit project's selection is counted, and two phone-geo…
test(e2e): retitle the #1129/#1130 phone-width tests "mobile — " so W…
fix(e2e): the app-shell navigation spec carries the "mobile — " prefi…