Phone layout: stop the radio controls running off the side #46
@@ -52,9 +52,12 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<svg class="tab-icon" aria-hidden="true"><use href="#icon-bookmark"/></svg>
|
||||
<span class="tab-label">Bookmarks</span>
|
||||
</button>
|
||||
<button class="tab" data-tab="digital-modes">
|
||||
<button class="tab" data-tab="digital-modes" aria-label="Digital modes">
|
||||
<svg class="tab-icon" aria-hidden="true"><use href="#icon-digital"/></svg>
|
||||
<span class="tab-label">Digital modes</span>
|
||||
<!-- A fifth of a phone screen does not hold "Digital modes"; the
|
||||
button keeps the full name for assistive tech. -->
|
||||
<span class="tab-label-short" aria-hidden="true">Digital</span>
|
||||
</button>
|
||||
<button class="tab" data-tab="map">
|
||||
<svg class="tab-icon" aria-hidden="true"><use href="#icon-map"/></svg>
|
||||
|
||||
@@ -1717,6 +1717,8 @@ small { color: var(--text-muted); }
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tab-label { display: block; }
|
||||
/* Only the bottom nav is short of room. */
|
||||
.tab-label-short { display: none; }
|
||||
.tab-more-icon { display: none; }
|
||||
.about-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1rem; }
|
||||
.about-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: 0.5rem; padding: 0; overflow: hidden; }
|
||||
@@ -4028,10 +4030,6 @@ body[data-operator-layout="broadcast"] #cw-bar-overlay {
|
||||
box-shadow: inset 0 1px 0 color-mix(in srgb, #ffffff 8%, transparent);
|
||||
}
|
||||
.tab-icon, .tab-more-icon { display: block; }
|
||||
/* Shorten long tab labels to keep bottom nav compact */
|
||||
.tab[data-tab="bookmarks"] .tab-label { font-size: 0.6rem; }
|
||||
.tab[data-tab="digital-modes"] .tab-label { font-size: 0.6rem; }
|
||||
.tab[data-tab="statistics"] .tab-label { font-size: 0.6rem; }
|
||||
.tab[data-tab="statistics"], .tab[data-tab="recorder"],
|
||||
.tab[data-tab="settings"], .tab[data-tab="about"] { display: none; }
|
||||
.mobile-more-btn { display: flex; }
|
||||
@@ -4045,7 +4043,42 @@ body[data-operator-layout="broadcast"] #cw-bar-overlay {
|
||||
color: var(--accent-text);
|
||||
background: color-mix(in srgb, var(--accent-green) 10%, transparent);
|
||||
}
|
||||
.tab .tab-label, .tab[data-tab] .tab-label { font-size: 0.75rem; }
|
||||
/* The bottom nav divides the bar between its five destinations, so a tab is
|
||||
about a fifth of the screen and the label has to live inside that. Sharing
|
||||
the width evenly beats sizing each tab to its own label, which gave
|
||||
"Digital modes" the same room as "Map" and clipped it mid-word. */
|
||||
.tab-bar-nav .tab {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
padding-left: 0.15rem;
|
||||
padding-right: 0.15rem;
|
||||
}
|
||||
.tab .tab-label, .tab[data-tab] .tab-label {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
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. */
|
||||
.tab[data-tab="bookmarks"] .tab-label,
|
||||
.tab[data-tab="statistics"] .tab-label {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
/* "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. */
|
||||
.tab[data-tab="digital-modes"] .tab-label { display: none; }
|
||||
.tab[data-tab="digital-modes"] .tab-label-short {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
.decoder-tab-select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user