[fix](trx-frontend-http): drop the tabs to icons when the bar runs out of room
CI / test (push) Successful in 8m15s
CI / frontend (push) Failing after 31s
CI / reuse (push) Successful in 2s
CI / lint (push) Successful in 2m17s

CI put the tab strip 9px into the controls at 1440px on a run that
passed locally: its system font is wider, and the bar had no move left
to make.  Controls are moved into the overflow menu until the bar fits,
but once all of them were in the menu nothing else gave — the nav may
shrink below its content, so the tabs kept full width and ran under the
controls, leaving the destinations nearest them unclickable.  Labels
dropping to icons was the other half of the answer, but it hung off a
max-width:1360px media query and so was unavailable at 1440px.

That class now goes on by measurement, as the last step after the menu
is exhausted, which is the same reasoning the controls' own fit test
already uses: how much fits depends on the rig name and on how wide the
platform draws the labels, not on the viewport.  The class is cleared
before measuring so the decision cannot ratchet, and icon widths are
fixed, so it always buys back the labels' width.

Labels now stay put between 1100px and 1360px while they fit, with the
style picker and theme toggle behind the overflow menu instead.

The suite could not have caught this: it passed on the fonts of the
machine that wrote it.  The layout section now repeats its fit check
with the bar's text scaled up, which reproduces a wider system font
anywhere — with this fix reverted it fails on macOS too.

Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
sjg
2026-08-03 21:36:55 +02:00
parent 657f952a2c
commit 23cc0db1fa
4 changed files with 52 additions and 5 deletions
@@ -818,6 +818,8 @@ function elementById(id) {
return needed <= bar.clientWidth;
};
const reflowOverflow = () => {
const nav = document.querySelector(".tab-bar-nav");
nav?.classList.remove("nav-icons-only");
overflowOrder.forEach((selector) => {
const element = menu.querySelector(selector);
if (element) actions.insertBefore(element, wrap);
@@ -830,6 +832,7 @@ function elementById(id) {
wrap.hidden = false;
menu.appendChild(element);
}
if (nav && !barFits()) nav.classList.add("nav-icons-only");
wrap.hidden = menu.children.length === 0;
if (wrap.hidden) closeMenu();
};
@@ -1562,11 +1562,17 @@ small { color: var(--text-muted); }
}
.tab-bar-nav .tab { flex: 0 0 auto; }
/* Icons before scrolling: every tab already carries one, and four icons plus
* More always fit, so the strip never has to hide a destination. */
@media (max-width: 1360px) and (min-width: 701px) {
.tab-bar-nav .tab .tab-label { display: none; }
.tab-bar-nav .tab .tab-icon, .tab-bar-nav .tab .tab-more-icon { display: block; }
.tab-bar-nav .tab { padding: 0.5rem 0.6rem; }
* More always fit, so the strip never has to hide a destination. Applied by
* measurement (ui-core's reflowOverflow) rather than at a viewport width: how
* much fits depends on the rig name and on how wide the platform renders the
* labels, so the same width fits on one machine and clips on another.
* Bounded below 761px, where the strip becomes the bottom nav and keeps its
* labels under the icons. */
@media (min-width: 761px) {
.tab-bar-nav.nav-icons-only .tab .tab-label { display: none; }
.tab-bar-nav.nav-icons-only .tab .tab-icon,
.tab-bar-nav.nav-icons-only .tab .tab-more-icon { display: block; }
.tab-bar-nav.nav-icons-only .tab { padding: 0.45rem 0.55rem; }
}
/* The selected destination is boxed, not underlined — the same treatment the
mobile bottom nav already used, so one navigation model reads the same at