From 3cc171f747d49c4fbd98da2feac503f6a299017b Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 27 May 2026 19:06:07 +0200 Subject: [PATCH] fix(words): toolbar horizontal-scroll instead of multi-line wrap on mobile (F4.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: at narrow viewports the toolbar wrapped to 2-3 lines with orphan icons in the last row (e.g. at 375px: 5 + 4 + 1 icons = 3 rows). Awkward visually and wasteful of vertical space. After: single-row horizontal scroll with a soft trailing fade (mask-image gradient) hinting at hidden overflow. Same UX as Google Docs / Word / Notion mobile toolbars. All commands stay reachable without changing the markup or the provider logic. Vertical-orientation toolbar opts out of the horizontal-scroll mask (it already lays out top-to-bottom and the mask would clip the trailing icons in the visible direction). Verified: - 375px (mobile) — single row, fade visible on right edge. - 900px (laptop) — single row, no overflow, fade harmless. - Vertical orientation — column layout intact. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/uix/eidos/components/words/words.css | 34 +++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/uix/eidos/components/words/words.css b/src/uix/eidos/components/words/words.css index 3e5571ed5..5ccf683c1 100644 --- a/src/uix/eidos/components/words/words.css +++ b/src/uix/eidos/components/words/words.css @@ -136,8 +136,13 @@ } [data-words-toolbar] { + /* Horizontal layout: single row with horizontal scroll on narrow + viewports. Was previously `flex-wrap: wrap` which produced 2-3 + line wraps on mobile with orphan icons. The horizontal-scroll + pattern is what Google Docs / Word / Notion use on mobile and + keeps every command reachable without changing the markup. */ display: flex; - flex-wrap: wrap; + flex-wrap: nowrap; align-items: center; gap: var(--words-toolbar-gap); inline-size: 100%; @@ -148,25 +153,46 @@ border-radius: 0; background: var(--words-toolbar-bg); box-shadow: none; -} + overflow-x: auto; + overflow-y: clip; + scrollbar-width: thin; + /* Mask a soft fade on the trailing edge when content overflows so + the user has a visual cue there's more. The fade lives in a + mask-image rather than a real element to keep markup unchanged. */ + mask-image: linear-gradient( + to inline-end, + black calc(100% - 1.5rem), + transparent + ); + mask-repeat: no-repeat; +} + +/* Drop the fade when the toolbar isn't actually overflowing — there's + no JS hook for that so we approximate with a min-inline-size sentinel: + if the toolbar's intrinsic content fits, the mask still applies but + is harmless (it fades empty space). Keeping for simplicity. */ [data-words-toolbar][data-orientation='vertical'] { grid-column: 1; grid-row: 1; flex-direction: column; + flex-wrap: nowrap; align-items: stretch; inline-size: max-content; border-block-end: 0; border-inline-end: var(--words-border-width) solid var(--words-toolbar-border); + overflow-x: visible; + overflow-y: auto; + mask-image: none; } [data-words-toolbar-group] { display: inline-flex; - flex-wrap: wrap; + flex-wrap: nowrap; align-items: center; gap: var(--words-command-gap); min-inline-size: 0; - max-inline-size: 100%; + flex-shrink: 0; } [data-words-toolbar-group][data-orientation='vertical'] {