docs: tighten the right rail follow-up comments and drop a one-use helper

Comments that still spoke of the strip scrolling or of a lifted max-height say what the code does now; the tab and column comments are shorter; the changelog bullet is wrapped. panelDisclosure.js is back to its reviewed shape: railTabFor had one caller left after the header click handoff was reverted.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Fable 5.1 committed 2026-10-28 12:53:17 +04:00
1 parent 5cc63fefb0
commit e05d6a29b4
5 files changed
+35 -42

No files matched your search

+10 -9
View File
@@ -2,12 +2,13 @@
Imagery while shown) is a vertical tab strip with sideways labels instead of Imagery while shown) is a vertical tab strip with sideways labels instead of
an accordion of collapsed launcher cards: at most one panel is open in every an accordion of collapsed launcher cards: at most one panel is open in every
desktop HUD layout, and the open tab and its panel share one surface and desktop HUD layout, and the open tab and its panel share one surface and
outline, like a folder tab. On a short window the rail rises toward the HUD corner above it and the tabs share the room equally, clipping the longer labels, rather than scroll. A first-time visitor on a outline, like a folder tab. On a short window the rail rises toward the HUD
desktop finds Display open; a stored choice still wins. The panels' own corner above it, and the tabs then share the room equally rather than
collapse arrows are gone on desktop: the tab opens and closes a panel, and scroll. A first-time visitor on a desktop finds Display open; a stored
Escape inside one closes it and returns focus to its tab; the strip is a choice still wins. The panels' own collapse arrows are gone on desktop: the
keyboard-navigable tablist. The compact Radio launcher moves from Context's tab opens and closes a panel, Escape inside one closes it and returns focus
header to the bottom of the strip on desktop and always opens the compact to its tab, and the strip is a keyboard-navigable tablist. The compact Radio
player, also while Context is open; the player's Details button leads to the launcher moves from Context's header to the bottom of the strip on desktop
Radio section. Saved preferences, share links and the phone sheet shell are and always opens the compact player, also while Context is open; the
unchanged (gekh, #1425). player's Details button leads to the Radio section. Saved preferences, share
links and the phone sheet shell are unchanged (gekh, #1425).
+7 -8
View File
@@ -1488,8 +1488,8 @@ unnecessary style writes.
The compact Radio player is independent of Context: its launcher toggles it The compact Radio player is independent of Context: its launcher toggles it
whether Context is collapsed or open, and only an outside pointer, Escape, its whether Context is collapsed or open, and only an outside pointer, Escape, its
close button or its Details button closes it, never a layout pass. The close button or its Details button closes it, never a layout pass. The
expanded Enable's reveal of the directory and Play lands its smooth scroll reveal after the expanded Enable lands its smooth scroll even when a rail
even when a rail layout pass, measuring the panels, cuts it short. layout pass cuts it short.
The right rail skips panels hidden by other CSS (computed `display: none`), but The right rail skips panels hidden by other CSS (computed `display: none`), but
keeps the collapsed panels its own exclusive presentation hides, so the owner keeps the collapsed panels its own exclusive presentation hides, so the owner
rule can still give them back. rule can still give them back.
@@ -1510,12 +1510,11 @@ The strip is a vertical `role="tablist"`: Up/Down
wrap, Home/End jump, the open tab (else the first) is the tab stop, Escape wrap, Home/End jump, the open tab (else the first) is the tab stop, Escape
inside a panel returns focus to its tab, and the strip precedes the panels in inside a panel returns focus to its tab, and the strip precedes the panels in
the DOM so Tab from the open tab reaches the tool slot and then the panel. the DOM so Tab from the open tab reaches the tool slot and then the panel.
Labels are the panels' own translated header titles. The rail's lane shares Labels are the panels' own translated header titles. The lane's top follows the
the left rail's top; when it is too short for the strip, the rail climbs toward left rail's; a strip taller than the lane lifts the rail toward the HUD corner
the HUD corner above as far as the strip needs (`src/ui/rightPanelRail.js`), above (`src/ui/rightPanelRail.js`), and a lane still shorter is shared equally
and a lane still shorter is shared equally between the tabs: a label short between the tabs, so the long labels end up one height and clip; the strip
enough keeps its height, the long ones end up the same height and clip at the never scrolls. Under the tabs the strip's
end; the strip never scrolls. Under the tabs the strip's
column keeps a tool slot (`#right-rail-tools`) that holds the compact Radio column keeps a tool slot (`#right-rail-tools`) that holds the compact Radio
launcher on desktop: `src/ui/radioPanelPlacement.js` moves it there from launcher on desktop: `src/ui/radioPanelPlacement.js` moves it there from
Context's header, and back for the phone sheet, so the compact player stays one Context's header, and back for the phone sheet, so the compact player stays one
+3 -8
View File
@@ -13,13 +13,6 @@ function listenerScope() {
}; };
} }
/** The strip tab that controls a right-rail panel, if the panel has one. */
function railTabFor(panel) {
return panel.ownerDocument?.querySelector?.(
`[role="tab"][aria-controls="${panel.id}"]`,
);
}
/** Bind collapse buttons and bubbling Escape to an existing panel. */ /** Bind collapse buttons and bubbling Escape to an existing panel. */
export function bindPanelDisclosure({ export function bindPanelDisclosure({
panel, panel,
@@ -70,7 +63,9 @@ export function collapsePanelOnEscape(
panel.querySelector(`[data-dock-toggle-target="${panel.id}"]`) || panel.querySelector(`[data-dock-toggle-target="${panel.id}"]`) ||
panel.querySelector(`[data-collapse-target="${panel.id}"]`); panel.querySelector(`[data-collapse-target="${panel.id}"]`);
// A right-rail panel collapses out of view; its tab stays and takes focus. // A right-rail panel collapses out of view; its tab stays and takes focus.
const tab = railTabFor(panel); const tab = panel.ownerDocument?.querySelector?.(
`[role="tab"][aria-controls="${panel.id}"]`,
);
const escapedFromDisclosure = const escapedFromDisclosure =
event.target === disclosure || disclosure?.contains?.(event.target); event.target === disclosure || disclosure?.contains?.(event.target);
if (tab) tab.focus({ preventScroll: true }); if (tab) tab.focus({ preventScroll: true });
+3 -3
View File
@@ -174,8 +174,8 @@ export function layoutRightPanelRail({
.map((node) => [node, node.scrollTop || 0]) .map((node) => [node, node.scrollTop || 0])
.filter(([, top]) => top > 0); .filter(([, top]) => top > 0);
const naturalHeights = new Map(); const naturalHeights = new Map();
// The tab strip's column stands beside the panels at its own height; the // The tab strip's column stands beside the panels; the measuring override
// measuring override lifts its lane clamp too. // gives it its natural height.
const column = stack.querySelector?.(':scope > #right-rail-column'); const column = stack.querySelector?.(':scope > #right-rail-column');
let columnHeight = 0; let columnHeight = 0;
// Remove live height allocations through a synchronous CSS override. Keeping // Remove live height allocations through a synchronous CSS override. Keeping
@@ -222,7 +222,7 @@ export function layoutRightPanelRail({
if (!layout) return; if (!layout) return;
// A lane too short for the strip's column: the rail climbs toward the HUD // A lane too short for the strip's column: the rail climbs toward the HUD
// above, as far as the strip needs and the obstacles allow. A lane still // above, as far as the strip needs and the obstacles allow. A lane still
// shorter scrolls the strip (rightRailTabs.js). // shorter shrinks the tabs (styles/right-rail-tabs.css).
const shortfall = columnHeight - (layout.safeBottom - layout.safeTop); const shortfall = columnHeight - (layout.safeBottom - layout.safeTop);
if (shortfall > 0) layout = resolve(alignedTop - shortfall) || layout; if (shortfall > 0) layout = resolve(alignedTop - shortfall) || layout;
const { safeTop, safeBottom, maxHeight: availableHeight } = layout; const { safeTop, safeBottom, maxHeight: availableHeight } = layout;
+12 -14
View File
@@ -57,10 +57,10 @@
} }
} }
/* The strip's column: the tablist, then a tool slot that holds the compact /* The strip's column: the tablist, then a tool slot for the compact Radio
Radio launcher on desktop (radioPanelPlacement.js). It stands the lane's launcher (radioPanelPlacement.js). It stands the lane's full height, which
full height, which makes the strip's size definite so its tabs can share makes the strip's size definite for the tabs' share (.right-rail-tab); only
it (see .right-rail-tab); only the strip and the slot take the pointer. */ the strip and the slot take the pointer. */
#right-rail-column { #right-rail-column {
position: absolute; position: absolute;
top: 0; top: 0;
@@ -90,8 +90,7 @@
min-height: 0; min-height: 0;
/* Room for the open tab's bridge on the left. */ /* Room for the open tab's bridge on the left. */
padding-left: var(--right-rail-tabs-gap); padding-left: var(--right-rail-tabs-gap);
/* The rail climbs above its baseline to fit the column (rightPanelRail.js); /* Never a scrollbar: a lane too short shrinks the tabs (.right-rail-tab). */
a lane still shorter shrinks the tabs. The strip never scrolls. */
overflow: hidden; overflow: hidden;
pointer-events: auto; pointer-events: auto;
} }
@@ -168,17 +167,16 @@ body:not(.mobile-shell)
display: none; display: none;
} }
/* The strip clips overflow, so the focus ring draws inside the tab. */ /* The tab's mask covers its own box only: the focus ring draws inside it. */
#right-rail-tabs > .right-rail-tab:focus-visible { #right-rail-tabs > .right-rail-tab:focus-visible {
outline-offset: -3px; outline-offset: -3px;
} }
/* A short strip is shared between the tabs: a zero basis hands the strip's /* A short strip is shared out equally: the zero basis hands every tab the
height out in equal parts, a tab's own length caps its part, and what a same part, a tab's own length caps it, and a short label's leftover goes
short label leaves goes to the longer ones, which end up the same height. A to the longer ones; with room each tab is its own length. The basis is
strip with room gives every tab its own length. The basis is 0%, not 0: 0%, not 0, so the strip still sizes to its labels. The label clips at the
the strip's content size, which sizes the column, comes from the labels. A end, faded over the bottom padding. */
tab clips its label at the end, faded over the bottom padding. */
.right-rail-tab { .right-rail-tab {
position: relative; position: relative;
display: flex; display: flex;
@@ -231,7 +229,7 @@ body:not(.mobile-shell)
text-shadow: none; text-shadow: none;
} }
/* Short windows: tighter tabs before the strip has to scroll. */ /* Short windows: tighter tabs, so fewer labels clip. */
@media (max-height: 720px) { @media (max-height: 720px) {
#right-rail-tabs { #right-rail-tabs {
gap: 3px; gap: 3px;