fix(viewer): keep the UI on-screen at every window width (responsive audit)#183
Merged
Conversation
…audit) Audited the viewer at runtime (Playwright, real server, widths 320-1920 + short/narrow windows + a live resize). Most surfaces were already responsive; this fixes the breaks that remained: - Header action bar overflowed the page at <=320px on a writable workspace: the full cluster (Follow / Console / History / Share / Export / Templates + theme) can't fit one nowrap row that narrow, so the page scrolled sideways (+16px at 320px). Let the cluster wrap onto a second strip below 380px (still >=44px tap targets). The demo hides Share/Export/Templates, so this only bit real boards. - Popover positioning (Share / Export / Templates) only clamped the LEFT edge. Add a shared positionPopover() that clamps into the viewport on BOTH axes -- flips above the trigger if it would overflow the bottom, pins to the margin and relies on max-height otherwise -- so a popover never spills past any edge at any window size. - .export-menu and .share-popover had no max-height (only .templates-popover did), so a short window could clip them. Cap both at min(70vh, 520px) with internal scroll. Adds e2e/responsive.e2e.mjs (no page h-overflow across the width matrix on the demo boards + welcome gallery; popovers on-screen on narrow/short windows; a live small->large->small resize) and chains it into test:e2e / test:e2e:nobuild.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Audited the viewer for responsiveness at runtime (Playwright against a real
dist/server.js, width matrix320 · 360 · 375 · 414 · 480 · 511 · 600 · 768 · 834 · 1024 · 1280 · 1440 · 1920plus short/narrow windows and a live resize) — not by eyeballing the CSS.Good news first: most surfaces are already responsive (the prior
@mediawork,panes-gridauto-fit, viewport-capped popover widths, and theSimpleGrid cols1→2→N normalization all hold up). The demo boards and welcome gallery had zero page horizontal overflow across the whole matrix. So this PR is deliberately tight — it fixes the breaks that actually remained and locks them in with a regression test.Findings → fixes
F1 — page scrolled sideways at ≤320px on a writable workspace (worst offender)
A writable board shows the full header action cluster — Follow · Console · History · Share · Export · Templates (six ≥44px icon buttons) + the theme select — in a
flex-wrap: nowraprow. At 320px those six buttons + gaps exceed the ~300px available, so the page overflowed.documentElement.scrollWidth 336 > clientWidth 320→ +16px at 320px./w/demo/workspace hides Share/Export/Templates, so it fit there (3 buttons) and was easy to miss.@media (max-width: 380px)rule lets.header-actionswrap onto a second strip (buttons stay ≥44px tap targets; the header just grows a row, which beats horizontal overflow). At ≥381px the existing nowrap 2-row header is unchanged.F2 — popovers only clamped their left edge
openExportMenu/openSharePopover/openTemplatesPopovereach didtop = r.bottom + 6; left = max(8, r.right - width)— clamping the left edge only, with nothing catching the right/top/bottom. They stayed on-screen today by luck (top-anchored triggers + CSS width caps), not by design.positionPopover(el, btn)that clampsleftinto[margin, vw - width - margin](both edges), opens below the trigger, flips above if that would overflow the bottom, and otherwise pins to the top margin and relies on the element'smax-height+ scroll. Replaces the three duplicated blocks.F3 —
.export-menu/.share-popoverhad nomax-heightOnly
.templates-popoverhad one, so a short window could clip the other two off the bottom.max-height: min(70vh, 520px); overflow: auto, mirroring.templates-popover. This also makes F2's "pin + scroll" branch correct for all three.After (F2/F3), measured: all three popovers open fully inside the viewport on narrow (320×900, 360×740) and short (768×380, 900×360) windows —
right ≤ iw+1,bottom ≤ ih+1,left ≥ -1,top ≥ -1.Before / after — F1 (the 320px header)
scrollWidth 336 > clientWidth 320(+16px); 6 buttons + theme crammed into one nowrap row(Verified with a Playwright screenshot at 320×560 — header wraps to two rows, no scrollbar.)
Regression test
packages/viewer/e2e/responsive.e2e.mjs(modeled onboard-sort/template): boots the server, then asserts —Chained into
test:e2eandtest:e2e:nobuild. 20/20 pass.Verification
npm run build→ exit 0npx vitest run→ 455 passed (30 files)npm run test:e2e:nobuild→ rich 62/62, board-sort 12/12, template 15/15, responsive 20/20grep -lP '\x00') on every edited.ts/.css→ cleanScope / tradeoffs
Grid/SimpleGridwith a responsive-objectcolswhosebase > 1is not auto-normalized (only numericcolsis, via the existingnormalizeLayoutProps). Not observed breaking in the demo corpus; left as content-author guidance rather than forcing global responsiveness, per the conservative option.