[fix](trx-frontend-http): let the bottom nav's labels fit inside its tabs
CI / lint (pull_request) Successful in 2m17s
CI / test (pull_request) Successful in 8m22s
CI / frontend (pull_request) Failing after 1m46s
CI / reuse (pull_request) Successful in 2s

The bottom nav keeps its labels under the icons -- that is what makes it
navigation rather than five glyphs -- but the labels did not fit the
tabs.  On a 360 px screen "Bookmarks" and "Digital modes" were cut off
mid-word and ran into each other: "Bookmarks igital mode".

The stylesheet already meant to handle it.  Three rules shrink the long
labels, and a rule twenty lines further down sets the size for all of
them; identical specificity, later in the file, so the blanket rule won
and nothing was ever shortened.  Those rules now come after it.

Beyond that, tabs sized themselves to their own labels, so "Map" and
"Digital modes" were given the same room.  They divide the bar evenly
now, which is the shape of every bottom nav, and a label that still runs
long ellipsises rather than being cut through a letter.

"Digital modes" does not fit at any size worth reading, so the nav shows
"Digital" and the button carries the full name as its accessible label;
the short form is hidden from assistive tech, which reads the button's
name instead.

The phone layout test now checks that no label in the nav is cut off at
430, 390 or 360 px, and that the shortened tab still says what it is to
something that reads the page rather than looks at it.

Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
sjg
2026-08-06 19:14:31 +02:00
parent 14ad6e241b
commit 6284747339
3 changed files with 76 additions and 6 deletions
@@ -59,8 +59,42 @@ try {
// else into the overflow menu.
assert.ok(layout.rigSelect <= 112,
`the rig picker is ${layout.rigSelect}px wide at ${width}px`);
// The bottom nav keeps its labels — that is what makes it navigation
// rather than five glyphs — so the labels have to fit inside a tab, which
// is a fifth of a phone. They were being cut off mid-word: "igital mode".
const nav = await page.evaluate(() => {
const bar = document.querySelector(".tab-bar-nav");
return [...bar.querySelectorAll(".tab")]
.filter((tab) => tab.getBoundingClientRect().width > 0)
.map((tab) => {
const label = [...tab.querySelectorAll(".tab-label, .tab-label-short")]
.find((span) => getComputedStyle(span).display !== "none");
return {
text: (label?.textContent ?? "").trim(),
clipped: label ? label.scrollWidth > label.clientWidth + 1 : false,
};
});
});
assert.ok(nav.length >= 4, `the bottom nav has ${nav.length} destinations at ${width}px`);
const clipped = nav.filter((tab) => tab.clipped).map((tab) => tab.text);
assert.deepEqual(clipped, [],
`labels cut off at ${width}px: ${clipped.join(", ")}`);
}
// A tab whose visible label is shortened for the nav still has to say what
// it is to anything that reads the page rather than looks at it.
const named = await page.evaluate(() => {
const tab = document.querySelector('.tab[data-tab="digital-modes"]');
const short = tab.querySelector(".tab-label-short");
return {
label: tab.getAttribute("aria-label"),
shortHidden: short?.getAttribute("aria-hidden"),
};
});
assert.equal(named.label, "Digital modes", "the shortened tab lost its full name");
assert.equal(named.shortHidden, "true", "the short label is read out as well as shown");
// Hiding the map's filters leaves the bar, and the bar has to still be
// there to bring them back — it carries the only button that does.
await page.setViewportSize({ width: 1400, height: 900 });