diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js index ec469f9b..482a089e 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js @@ -819,7 +819,9 @@ function elementById(id) { }; const reflowOverflow = () => { const nav = document.querySelector(".tab-bar-nav"); + const bar = actions.closest(".tab-bar"); nav?.classList.remove("nav-icons-only"); + bar?.classList.remove("bar-tight"); overflowOrder.forEach((selector) => { const element = menu.querySelector(selector); if (element) actions.insertBefore(element, wrap); @@ -833,6 +835,7 @@ function elementById(id) { menu.appendChild(element); } if (nav && !barFits()) nav.classList.add("nav-icons-only"); + if (bar && !barFits()) bar.classList.add("bar-tight"); wrap.hidden = menu.children.length === 0; if (wrap.hidden) closeMenu(); }; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css index 0e6904b3..993a4a86 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css @@ -1652,6 +1652,27 @@ small { color: var(--text-muted); } .tab-bar-nav.nav-icons-only .tab .tab-more-icon { display: block; } .tab-bar-nav.nav-icons-only .tab { padding: 0.45rem 0.55rem; } } +/* Last resort, past icons: something in the bar has to absorb the shortfall, + and by default that is the tab strip — it may shrink below its content, so + its tabs keep full width and run under the controls, unreachable. The + identity block takes it instead, ellipsised: a clipped station name is still + readable, a destination hidden under the controls is not. */ +.tab-bar.bar-tight .header-main { + flex-shrink: 1; + min-width: 0; +} +.tab-bar.bar-tight .header-text { + min-width: 0; +} +.tab-bar.bar-tight .title, +.tab-bar.bar-tight .subtitle { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.tab-bar.bar-tight .tab-bar-nav { + flex-shrink: 0; +} /* The selected destination is boxed, not underlined — the same treatment the mobile bottom nav already used, so one navigation model reads the same at every width. The transparent border is on the base so switching tabs moves diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/ui-core.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/ui-core.ts index a67dc939..4bf33772 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/ui-core.ts +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/ui-core.ts @@ -423,9 +423,11 @@ function elementById(id: string): T { }; const reflowOverflow = () => { const nav = document.querySelector(".tab-bar-nav"); + const bar = actions.closest(".tab-bar"); // Measure from the roomiest state every time, so the decision is a // function of the current widths alone and cannot ratchet. nav?.classList.remove("nav-icons-only"); + bar?.classList.remove("bar-tight"); overflowOrder.forEach((selector) => { const element = menu.querySelector(selector); if (element) actions.insertBefore(element, wrap); @@ -444,6 +446,10 @@ function elementById(id: string): T { // controls, so the destinations nearest the controls become unclickable. // Icon widths are fixed, so this always buys back the labels' width. if (nav && !barFits()) nav.classList.add("nav-icons-only"); + // Still short with the tabs down to icons: hand the squeeze to the + // identity block, which can ellipsise, rather than to the strip, which + // can only clip destinations out of reach. + if (bar && !barFits()) bar.classList.add("bar-tight"); wrap.hidden = menu.children.length === 0; if (wrap.hidden) closeMenu(); }; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/browser-smoke.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/browser-smoke.mjs index f7e0d727..a00b9b8b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/browser-smoke.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/browser-smoke.mjs @@ -213,16 +213,19 @@ try { assert.ok(menu.onTop, "menu is painted underneath the page"); // Wider text than this machine renders. The checks above passed on macOS - // while CI, whose system font is wider, put the tab strip 9px into the - // controls: the bar had run out of controls to move into the overflow menu - // and the tabs kept their full width anyway. Scaling the bar's own text - // reproduces that on any machine. The tabs carry their font size themselves, - // so the parent size alone does not cascade to them. + // while CI, whose system font is wider, put the tab strip into the controls: + // the bar had run out of moves and the tabs kept their full width anyway. + // Every piece of text in the bar is scaled, not just the tabs — the identity + // block is what runs out of room first at 1100px, where the bookmark gutters + // make the card narrower than it is at 900px. + const scale = 1.9; await page.addStyleTag({ content: ` - .tab-bar { font-size: 160%; } - .tab-bar .tab, .tab-bar select, .tab-bar button { font-size: 1.52rem; } + .tab-bar .title { font-size: ${1.05 * scale}rem !important; } + .tab-bar .subtitle { font-size: ${0.78 * scale}rem !important; } + .tab-bar .tab { font-size: ${0.95 * scale}rem !important; } + .tab-bar select, .tab-bar button { font-size: ${0.95 * scale}rem !important; } ` }); - for (const width of [1440, 1280, 1100]) { + for (const width of [1440, 1280, 1100, 900]) { await page.setViewportSize({ width, height: 900 }); await page.waitForTimeout(250); const crowded = await page.evaluate(() => {