cmux-next: Settings scrolls in one place (cx-jh7kq)
Summary
Settings now scrolls in one place. Before, an open select pop-up made the document itself scrollable. That produced a second scrollbar, a column that slid up under the window with dead space below, rows cut mid-row at the inner edge, and pop-ups drawn past the scroll area.
Cause. Select and popover pop-ups were portaled to <body> with Base UI's default absolute positioning, and .ui-menu had no height cap. While the content column scrolled, an open pop-up followed its anchor out of the viewport and grew the document: in a 700 px window the document reached 1232 px. From then on the wheel chained from the content column to the document, because nothing clipped html/body and .content had no overscroll containment. Opening the font or theme list also pushed every row below it down, since it was an inline panel.
Fix.
- One scroller.
html/bodyand.settingsclip their overflow, so the document never scrolls..content(and the sidebar) contain their overscroll. - Soft edges.
.contentfades rows out over its top 18 px and bottom 28 px instead of a hard clip. The fades sit inside the padding, so nothing is faded at rest, and the scrollbar strip on the right stays unmasked. - Pop-ups.
UiProvidergainsoverlayPosition, and Settings opts in to"fixed"; Menu and Popover pass it to their positioners. A fixed pop-up follows its row without growing the document. Every other page keepsabsolute. - Shared
ui.css. A select's options are capped to the room on their side (--available-height, at most 420 px) and scroll inside. A positioner whose anchor is scrolled out of view (data-anchor-hidden) is hidden until the anchor is back. - Font and theme list. DomainListEditor's list is now a
Popoverover the rows rather than an in-flow panel. Its own Escape handler went away, so itsui-rulespending entry is deleted.
There was never virtualization: every row already renders once. The new test pins that, so a later change can't add remounts.
Testing
Boundary test: webviews/test/settings-scroll.test.ts, on the browser lane. It runs in real Chromium and WebKit against the settings dev server with the mock (?mock), in a 1000×700 window, and checks:
- Wheeling Appearance (97 rows) from top to bottom and past the end leaves exactly one row scroller (
.content), the document never scrolls, and no row is removed, replaced or moved. - A select opened near the bottom edge (
sidebar.sectionLook) while the content is wheeled down and up: the document never grows, the shown pop-up stays on screen within 12 px of its anchor, and it may be hidden only while its anchor is out of view. - Opening the font list moves no row.
.contenthas the edge fade.
- Red on base (commit 1c57ebcddba6, Chromium): cases 2–4 fail. Case 2:
document 832px … 1032px … 1232px, pop-up off screen. Case 3: 98 rows moved. Case 4: masknone. Case 1 passes on base, because nothing virtualizes. - Green on this head (Chromium): 4/4. WebKit cannot launch on the machine that ran it, so CI's react-apps-check runs both engines.
- Also green locally:
- the 27 settings and
src/uiunit test files; test/ui-rules.test.ts;- the
ui-a11yandcomposer-menu-widthbrowser suites; gallery-coverageandgallery-play;bun run check.
- the 27 settings and
Before/after: the new gallery variant pages.settings › play-select-scroll opens that select near the bottom and scrolls the content under it. The PR gallery renders base vs head. The font-picker variant shows the list as a pop-up.
Changelog
- Fixed: Settings scrolls in one place: no second scrollbar, rows fade at the edges, and open pop-ups stay with their row.
closes cx-jh7kq
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.
Summary by cubic
Fixes the Settings page so it scrolls in a single place instead of letting an open select pop-up make the document scroll. That previously caused a second scrollbar, dead space below the column, rows cut mid-row, and pop-ups drawn past the scroll area.
- The document never scrolls: the content column is the only scroller, with contained overscroll and edges that fade rows out instead of clipping them.
- Select and popover pop-ups now render fixed and follow their row without growing the document; a long list's options scroll inside it, and a pop-up waits hidden while its anchor is scrolled out.
- The font and theme list is a pop-over over the rows rather than an in-flow panel that pushed rows down; clicking its button again closes it, close clears the filter, and a row disabled while open closes it too.
- The document clip applies only to the Settings page, so gallery frames that borrow its stylesheet keep their own scrolling.