diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 652cda5ca..e266e2cba 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -155,6 +155,9 @@ never the field's only name. Help and validation belong to that field without changing the alignment of neighboring controls and their actions. Short fixed choices keep native selects, including the platform's own popup. +A control never widens its column: a select shows its chosen label on one +line, clipped at its own edge, and the full label stays in the platform's +popup. Model selection uses the shared editable chooser: suggestions assist typing without becoming an allowlist. A saved unknown model remains editable; a catalog refresh preserves the real input, selection and composition. Escape diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 36bc5455f..849a0da27 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -3169,6 +3169,26 @@ SPA, not a relocatable-page or multi-instance panel framework. `.style.` assignments are review debt (a dynamic measured value may update a narrowly named custom property when that is the real runtime data flow). +- Containment: a control never widens its column, and horizontal overflow + lives in the wrapper that owns the wide content and declares + `overflow-x: auto` (code block, `.md-table-wrap`, tab strip, Costs table + cells) — never in a page scroll body, whose `overflow-y: auto` alone + already makes `overflow-x` compute to `auto`. The shared + `select.ui-control` recipe therefore clips its own value + (`overflow: hidden`): WebKit computes `overflow: visible` on a native + select, so an unclipped option label becomes scrollable overflow of that + page scroller. A grid track holding controls takes a minimum that yields + to its container — `minmax(0, …)`, or + `repeat(auto-fit, minmax(min(100%, Npx), 1fr))`; a fixed px minimum + rescued only by a viewport media query is review debt, because the + viewport does not know how wide the content column is. The global webkit + scrollbar recipe sizes both axes. Enforced by + `tests/test_web_typography_static.py::test_select_control_clips_its_value`, + its `::test_webkit_scrollbar_recipe_covers_both_axes` neighbour, and + `tests/test_ui_settings_overflow_browser.py`; two gaps stay open — the + wizard document loads `ui.css` without `style.css` and keeps native + scrollbars, and an element setting the standard + `scrollbar-width`/`scrollbar-color` opts out of the webkit recipe on Blink. - One semantic button variant expresses one action role: neutral Settings and onboarding controls use the existing `.btn.btn-default`; a one-action result row uses the named `.settings-action-row` contract (status first, @@ -3260,7 +3280,11 @@ stealing usable text space; use the shared responsive component before adding a page-specific layout. A visible change is inspected with vision in at least one relevant real consumer flow. A stored screenshot alone is not verification; mobile or WebKit is not a universal requirement and is -selected from risk. Review-only: scored by CHECKLISTS items 2(i) and 30 +selected from risk. Containment is the WebKit-sensitive exception — a native +select is not clipped there — so a change to a control recipe or a page +scroll body is verified on Playwright WebKit as well as Chromium, measuring +overflow on the scroll body's `scrollWidth` rather than on +`documentElement`. Review-only: scored by CHECKLISTS items 2(i) and 30 (`web_design_system`). ### Browser dialogs diff --git a/tests/test_ui_settings_overflow_browser.py b/tests/test_ui_settings_overflow_browser.py new file mode 100644 index 000000000..4a3c09c49 --- /dev/null +++ b/tests/test_ui_settings_overflow_browser.py @@ -0,0 +1,214 @@ +"""WebKit guard: Settings never grows a horizontal scrollbar. + +The owner's report was a thick red horizontal scrollbar across the bottom of +Settings -> Advanced in the packaged desktop shell (WKWebView). Blink's UA +stylesheet clips a native ``