From 77b283cb78724e9b07714dcbb93313410144ae0e Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Thu, 6 Aug 2026 19:41:45 +0200 Subject: [PATCH] [fix](trx-frontend-http): size the nav's labels against the screen, not the font CI cut "Bookmarks" off at 360 px where this machine had ten pixels to spare. How wide a platform draws a word varies by more than ten per cent, so a fixed 0.6rem label is a bet on the machine it was measured on -- the same bet ui-core's own comment warns about where it explains why the tab strip reflows by measurement rather than at a width. The long labels are sized with `clamp(0.46rem, 2.2vw, 0.62rem)` now. A tab is a fifth of the viewport, so what fits in it follows the viewport; tying the label to the same thing leaves better than a fifth of the tab spare at every phone width, and holds with the text drawn 30% wider than it is here. The test asked the wrong question too. "Does the label fit" is a question about font metrics, and it will keep answering differently on different machines. It now asserts what actually matters: a label stays inside its own tab, and if it is too long for it, it ends in an ellipsis rather than being cut through a letter. Both hold whatever width the platform draws the words at. Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/style.css | 16 ++++++---- .../frontend/tests/mobile-layout.mjs | 30 +++++++++++++++---- 2 files changed, 35 insertions(+), 11 deletions(-) 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 99ebc68d..f0e0311f 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 @@ -4061,12 +4061,18 @@ body[data-operator-layout="broadcast"] #cw-bar-overlay { text-overflow: ellipsis; font-size: 0.75rem; } - /* The long ones, smaller so they fit whole rather than ellipsised. These - used to sit above the rule that sets the size for all of them — same - specificity, so the later one won and nothing was ever shortened. */ + /* The long ones, sized against the screen rather than at a fixed size. + A tab is a fifth of the viewport, so what fits in it is a function of the + viewport — and only roughly of the font size, since how wide a platform + renders a word varies by ten per cent or more between machines. A fixed + 0.6rem fit here with ten pixels to spare and clipped in CI. + + (These rules also used to sit above the one that sets the size for all + labels — identical specificity, later in the file, so nothing was ever + shortened.) */ .tab[data-tab="bookmarks"] .tab-label, .tab[data-tab="statistics"] .tab-label { - font-size: 0.6rem; + font-size: clamp(0.46rem, 2.2vw, 0.62rem); } /* "Digital modes" does not fit at any size worth reading, so the nav shows the short form and the button carries the full one as its label. */ @@ -4077,7 +4083,7 @@ body[data-operator-layout="broadcast"] #cw-bar-overlay { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - font-size: 0.6rem; + font-size: clamp(0.46rem, 2.2vw, 0.62rem); } .decoder-tab-select { display: block; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/mobile-layout.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/mobile-layout.mjs index f4326a2a..ba0f8793 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/mobile-layout.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/mobile-layout.mjs @@ -61,8 +61,14 @@ try { `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". + // rather than five glyphs — and a label used to overflow its tab and run + // into the next one: "Bookmarks igital mode". + // + // What is asserted is that a label stays inside its own tab and, if it is + // too long for it, ends in an ellipsis. Not that it fits: how wide a + // platform draws a word varies by more than ten per cent, so "it fits" + // passes on the machine it was written on and fails on the next one — + // which is what a fixed font size did here. const nav = await page.evaluate(() => { const bar = document.querySelector(".tab-bar-nav"); return [...bar.querySelectorAll(".tab")] @@ -70,16 +76,28 @@ try { .map((tab) => { const label = [...tab.querySelectorAll(".tab-label, .tab-label-short")] .find((span) => getComputedStyle(span).display !== "none"); + const style = label ? getComputedStyle(label) : null; + const labelBox = label?.getBoundingClientRect(); + const tabBox = tab.getBoundingClientRect(); return { text: (label?.textContent ?? "").trim(), - clipped: label ? label.scrollWidth > label.clientWidth + 1 : false, + escapes: labelBox + ? labelBox.left < tabBox.left - 1 || labelBox.right > tabBox.right + 1 + : false, + truncates: style?.textOverflow === "ellipsis" && style?.overflow !== "visible", + empty: !labelBox || labelBox.width < 1, }; }); }); 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(", ")}`); + const escaped = nav.filter((tab) => tab.escapes).map((tab) => tab.text); + assert.deepEqual(escaped, [], + `labels overflowing their tab at ${width}px: ${escaped.join(", ")}`); + const untruncatable = nav.filter((tab) => !tab.truncates).map((tab) => tab.text); + assert.deepEqual(untruncatable, [], + `labels that would be cut rather than ellipsised at ${width}px: ${untruncatable.join(", ")}`); + const blank = nav.filter((tab) => tab.empty).map((tab, index) => tab.text || `#${index}`); + assert.deepEqual(blank, [], `destinations with no label at ${width}px: ${blank.join(", ")}`); } // A tab whose visible label is shortened for the nav still has to say what