mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
fix(ui): right rail strip never scrolls: tabs shrink in proportion and clip their labels
A lane too short for the tab strip, even after the rail has climbed toward the HUD corner, shrank the strip into a scrollbar. The tabs now give way in proportion to their labels (flex 0 1 auto, min-height 0) and clip them at the end with a short fade; the join keeps corners no larger than half of a shrunken tab. The strip's scroll handling in placeJoin, the scroll listener, railJoinPath's tab-less path and the scroller marker are gone with the scrolling. The expanded Radio Enable's reveal of the directory and Play lands its smooth scroll on scrollend when a rail layout pass, measuring the panels mid-scroll, puts the Context scroller's offset back and cancels it; the Radio browser gate stopped there since the strip-fit commit. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
bff1d0eb29
commit
84c204b7c4
7 files changed
+95
-157
No files matched your search
@@ -2,8 +2,7 @@
|
||||
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
|
||||
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 so the whole strip stays in view. A first-time visitor on a
|
||||
outline, like a folder tab. On a short window the rail rises toward the HUD corner above it and the tabs shrink, clipping their labels, rather than scroll. A first-time visitor on a
|
||||
desktop finds Display open; a stored choice still wins. The panels' own
|
||||
collapse arrows are gone on desktop: the tab opens and closes a panel, and
|
||||
Escape inside one closes it and returns focus to its tab; the strip is a
|
||||
|
||||
@@ -1487,7 +1487,9 @@ gives every owner-collapsed panel back. Stable Display allocation avoids
|
||||
unnecessary style writes.
|
||||
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
|
||||
close button or its Details button closes it, never a layout pass.
|
||||
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
|
||||
even when a rail layout pass, measuring the panels, cuts it short.
|
||||
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
|
||||
rule can still give them back.
|
||||
@@ -1511,8 +1513,8 @@ 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
|
||||
the left rail's top; when it is too short for the strip, the rail climbs toward
|
||||
the HUD corner above as far as the strip needs (`src/ui/rightPanelRail.js`),
|
||||
and only a lane still shorter scrolls the strip, with a thin scrollbar and the
|
||||
open tab kept in view. Under the tabs the strip's
|
||||
and a lane still shorter shrinks the tabs in proportion, clipping their
|
||||
labels at the end; the strip never scrolls. Under the tabs the strip's
|
||||
column keeps a tool slot (`#right-rail-tools`) that holds the compact Radio
|
||||
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
|
||||
|
||||
@@ -184,6 +184,12 @@ test('only the expanded Radio Enable gesture requests the contained post-enable
|
||||
assert.match(radioControls, /if \(revealAfterEnable\)\s*await this\._revealRadioControlsAfterExplicitEnable\(trigger\)/);
|
||||
assert.equal((radioBindings.match(/_revealRadioControlsAfterExplicitEnable\(trigger\)/g) || []).length, 1);
|
||||
assert.match(radioControlsSource, /async _revealRadioControlsAfterExplicitEnable\(trigger\)/);
|
||||
// A rail layout pass measuring the panels mid-scroll cancels the smooth
|
||||
// reveal; the helper lands it at the target when the scroll ends short.
|
||||
assert.match(
|
||||
radioControlsSource,
|
||||
/scroller\.addEventListener\(\s*'scrollend',[\s\S]*?Math\.abs\(scroller\.scrollTop - next\) >= 1\)\s*scroller\.scrollTop = next;/,
|
||||
);
|
||||
});
|
||||
|
||||
test('right-rail context entry is transactional: activation result gates the mode', () => {
|
||||
|
||||
@@ -106,6 +106,18 @@ export class RadioControls {
|
||||
top: next,
|
||||
behavior: reducedMotion ? 'auto' : 'smooth',
|
||||
});
|
||||
// A rail layout pass that measures the panels mid-scroll puts the scroll
|
||||
// offset back where it was, which cancels the smooth scroll: land it when
|
||||
// the scroll ends short.
|
||||
if (!reducedMotion)
|
||||
scroller.addEventListener(
|
||||
'scrollend',
|
||||
() => {
|
||||
if (!this.destroyed && Math.abs(scroller.scrollTop - next) >= 1)
|
||||
scroller.scrollTop = next;
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+44
-85
@@ -75,9 +75,8 @@ export function railTabPanels(rail) {
|
||||
*
|
||||
* @param {object} g
|
||||
* @param {{x0: number, y0: number, x1: number, y1: number}} g.panel
|
||||
* @param {{top: number, bottom: number, right: number}|null} g.tab The tab's
|
||||
* extent; its left edge is the panel's right edge. Null draws the panel
|
||||
* alone.
|
||||
* @param {{top: number, bottom: number, right: number}} g.tab The tab's
|
||||
* extent; its left edge is the panel's right edge.
|
||||
* @param {number} [g.corner] Panel corner radius, or the chamfer size.
|
||||
* @param {number} [g.tabRadius] The tab's outer corner radius.
|
||||
* @param {number} [g.fillet] Concave fillet radius at the junction.
|
||||
@@ -100,24 +99,21 @@ export function railJoinPath({
|
||||
const y0 = panel.y0 + inset;
|
||||
const x1 = panel.x1 - inset;
|
||||
const y1 = panel.y1 - inset;
|
||||
// No tab (it is scrolled out of the strip): the panel alone.
|
||||
const ty0 = tab ? tab.top + inset : NaN;
|
||||
const ty1 = tab ? tab.bottom - inset : NaN;
|
||||
const tx1 = tab ? tab.right - inset : NaN;
|
||||
const ty0 = tab.top + inset;
|
||||
const ty1 = tab.bottom - inset;
|
||||
const tx1 = tab.right - inset;
|
||||
if (style === 'chamfer') {
|
||||
const c = corner;
|
||||
const cx = (panel.x0 + panel.x1) / 2;
|
||||
// The tab's edges along the panel stay between the chamfers.
|
||||
const tyA = Math.max(ty0, y0 + c);
|
||||
const tyB = Math.max(tyA, Math.min(ty1, y1 - c));
|
||||
const edge = tab
|
||||
? [
|
||||
`L ${P(x1, tyA)}`,
|
||||
`L ${P(tx1, tyA)}`,
|
||||
`L ${P(tx1, tyB)}`,
|
||||
`L ${P(x1, tyB)}`,
|
||||
]
|
||||
: [];
|
||||
const edge = [
|
||||
`L ${P(x1, tyA)}`,
|
||||
`L ${P(tx1, tyA)}`,
|
||||
`L ${P(tx1, tyB)}`,
|
||||
`L ${P(x1, tyB)}`,
|
||||
];
|
||||
return [
|
||||
`M ${P(x0, y0 + c)}`,
|
||||
`L ${P(x0 + c, y0)}`,
|
||||
@@ -165,35 +161,28 @@ export function railJoinPath({
|
||||
return { fillet, corner: Math.min(R, Math.max(0, room - fillet)) };
|
||||
};
|
||||
move(x0 + R, y0);
|
||||
if (!tab) {
|
||||
line(x1 - R, y0);
|
||||
arc(R, 1, x1, y0 + R);
|
||||
line(x1, y1 - R);
|
||||
arc(R, 1, x1 - R, y1);
|
||||
const first = tab.top - panel.y0 < 0.5;
|
||||
const last = panel.y1 - tab.bottom < 0.5;
|
||||
const above = fit(ty0 - y0);
|
||||
const below = fit(y1 - ty1);
|
||||
if (first) {
|
||||
line(tx1 - r, y0);
|
||||
arc(r, 1, tx1, y0 + r);
|
||||
} else {
|
||||
const first = tab.top - panel.y0 < 0.5;
|
||||
const last = panel.y1 - tab.bottom < 0.5;
|
||||
const above = fit(ty0 - y0);
|
||||
const below = fit(y1 - ty1);
|
||||
if (first) {
|
||||
line(tx1 - r, y0);
|
||||
arc(r, 1, tx1, y0 + r);
|
||||
} else {
|
||||
line(x1 - above.corner, y0);
|
||||
arc(above.corner, 1, x1, y0 + above.corner);
|
||||
line(x1, ty0 - above.fillet);
|
||||
arc(above.fillet, 0, x1 + above.fillet, ty0);
|
||||
line(tx1 - r, ty0);
|
||||
arc(r, 1, tx1, ty0 + r);
|
||||
}
|
||||
line(tx1, ty1 - r);
|
||||
arc(r, 1, tx1 - r, ty1);
|
||||
if (!last) {
|
||||
line(x1 + below.fillet, ty1);
|
||||
arc(below.fillet, 0, x1, ty1 + below.fillet);
|
||||
line(x1, y1 - below.corner);
|
||||
arc(below.corner, 1, x1 - below.corner, y1);
|
||||
}
|
||||
line(x1 - above.corner, y0);
|
||||
arc(above.corner, 1, x1, y0 + above.corner);
|
||||
line(x1, ty0 - above.fillet);
|
||||
arc(above.fillet, 0, x1 + above.fillet, ty0);
|
||||
line(tx1 - r, ty0);
|
||||
arc(r, 1, tx1, ty0 + r);
|
||||
}
|
||||
line(tx1, ty1 - r);
|
||||
arc(r, 1, tx1 - r, ty1);
|
||||
if (!last) {
|
||||
line(x1 + below.fillet, ty1);
|
||||
arc(below.fillet, 0, x1, ty1 + below.fillet);
|
||||
line(x1, y1 - below.corner);
|
||||
arc(below.corner, 1, x1 - below.corner, y1);
|
||||
}
|
||||
line(x0 + R, y1);
|
||||
arc(R, 1, x0, y1 - R);
|
||||
@@ -239,10 +228,6 @@ export function createRightRailTabs({
|
||||
strip.setAttribute('role', 'tablist');
|
||||
strip.setAttribute('aria-orientation', 'vertical');
|
||||
strip.setAttribute('aria-label', t('Panels'));
|
||||
// The rail's measuring pass (rightPanelRail.js) lifts the column's lane
|
||||
// clamp, which would reset a scrolled strip; the marker has it put the
|
||||
// offset back.
|
||||
strip.setAttribute('data-rail-scroller', '');
|
||||
const tools = documentRef.createElement('div');
|
||||
tools.id = RIGHT_RAIL_TOOLS_ID;
|
||||
column.append(strip, tools);
|
||||
@@ -296,7 +281,7 @@ export function createRightRailTabs({
|
||||
typeof Observer === 'function' ? new Observer(() => sync()) : null;
|
||||
// Tab heights follow their labels (fonts, locale) and panel heights their
|
||||
// content and allocation: re-measure the join when the strip or a panel
|
||||
// changes size and on strip scroll.
|
||||
// changes size.
|
||||
const Resize = documentRef.defaultView?.ResizeObserver;
|
||||
const resizer =
|
||||
typeof Resize === 'function' ? new Resize(() => sync()) : null;
|
||||
@@ -325,28 +310,13 @@ export function createRightRailTabs({
|
||||
* extended down to cover the tab if that is shorter, but never past the
|
||||
* rail's lane (--right-stack-max-height).
|
||||
*/
|
||||
let shownTab = null;
|
||||
function placeJoin(tab, panel) {
|
||||
if (!tab) {
|
||||
rail.style.removeProperty('--rail-tab-bottom');
|
||||
join.hidden = true;
|
||||
shownTab = null;
|
||||
return;
|
||||
}
|
||||
// A short lane makes the strip scroll. A newly opened tab is brought into
|
||||
// view once; after that the strip scrolls as the user wishes and the join
|
||||
// follows the visible part of the tab, or the panel alone.
|
||||
const viewHeight = strip.clientHeight;
|
||||
const scrolls = Number.isFinite(viewHeight) && viewHeight > 0;
|
||||
if (scrolls && tab !== shownTab) {
|
||||
const above = tab.offsetTop - strip.scrollTop;
|
||||
const below =
|
||||
tab.offsetTop + tab.offsetHeight - (strip.scrollTop + viewHeight);
|
||||
if (above < 0) strip.scrollTop += above;
|
||||
else if (below > 0) strip.scrollTop += below;
|
||||
}
|
||||
shownTab = tab;
|
||||
const top = tab.offsetTop - (strip.scrollTop || 0);
|
||||
const top = tab.offsetTop;
|
||||
rail.style.setProperty('--rail-tab-bottom', `${top + tab.offsetHeight}px`);
|
||||
const computed = documentRef.defaultView?.getComputedStyle?.(rail);
|
||||
const corner = token(computed, '--right-rail-corner', 16);
|
||||
@@ -360,13 +330,8 @@ export function createRightRailTabs({
|
||||
// column's to the rail.
|
||||
const stripTop = (column.offsetTop || 0) + (strip.offsetTop || 0);
|
||||
const stripLeft = (column.offsetLeft || 0) + (strip.offsetLeft || 0);
|
||||
let tabTop = stripTop + top;
|
||||
let tabBottom = tabTop + tab.offsetHeight;
|
||||
if (scrolls) {
|
||||
tabTop = Math.max(tabTop, stripTop);
|
||||
tabBottom = Math.min(tabBottom, stripTop + viewHeight);
|
||||
}
|
||||
const visible = tabBottom - tabTop >= 1;
|
||||
const tabTop = stripTop + top;
|
||||
const tabBottom = tabTop + tab.offsetHeight;
|
||||
const geometry = {
|
||||
panel: {
|
||||
x0: panel.offsetLeft,
|
||||
@@ -374,20 +339,17 @@ export function createRightRailTabs({
|
||||
x1: panel.offsetLeft + panel.offsetWidth,
|
||||
y1: Math.max(
|
||||
panel.offsetTop + panel.offsetHeight,
|
||||
visible ? Math.min(lane, tabBottom + fillet + 4 + corner) : 0,
|
||||
Math.min(lane, tabBottom + fillet + 4 + corner),
|
||||
),
|
||||
},
|
||||
tab: visible
|
||||
? {
|
||||
top: tabTop,
|
||||
bottom: tabBottom,
|
||||
right: stripLeft + tab.offsetLeft + tab.offsetWidth,
|
||||
}
|
||||
: null,
|
||||
tab: {
|
||||
top: tabTop,
|
||||
bottom: tabBottom,
|
||||
right: stripLeft + tab.offsetLeft + tab.offsetWidth,
|
||||
},
|
||||
corner,
|
||||
tabRadius: visible
|
||||
? Math.min(tabRadius, (tabBottom - tabTop) / 2)
|
||||
: tabRadius,
|
||||
// A tab shrunk by a short strip keeps corners no larger than half of it.
|
||||
tabRadius: Math.min(tabRadius, (tabBottom - tabTop) / 2),
|
||||
fillet,
|
||||
style: chamfer ? 'chamfer' : 'round',
|
||||
};
|
||||
@@ -465,10 +427,8 @@ export function createRightRailTabs({
|
||||
event.preventDefault();
|
||||
visible[next].focus();
|
||||
};
|
||||
const onScroll = () => sync();
|
||||
strip.addEventListener('click', onClick);
|
||||
strip.addEventListener('keydown', onKeydown);
|
||||
strip.addEventListener('scroll', onScroll, { passive: true });
|
||||
observer?.observe(rail, { childList: true });
|
||||
rail.prepend(column);
|
||||
rail.append(join);
|
||||
@@ -487,7 +447,6 @@ export function createRightRailTabs({
|
||||
resizer?.disconnect();
|
||||
strip.removeEventListener('click', onClick);
|
||||
strip.removeEventListener('keydown', onKeydown);
|
||||
strip.removeEventListener('scroll', onScroll);
|
||||
column.remove();
|
||||
join.remove();
|
||||
rail.removeAttribute(RIGHT_RAIL_TABS_ATTRIBUTE);
|
||||
|
||||
@@ -213,11 +213,6 @@ test('builds one tab per visible rail panel, in rail order, labelled from the pa
|
||||
assert.deepEqual(f.tabs.tools.children, [], 'the slot starts empty');
|
||||
assert.equal(f.strip.id, RIGHT_RAIL_TABS_ID);
|
||||
assert.equal(f.strip.getAttribute('role'), 'tablist');
|
||||
assert.equal(
|
||||
f.strip.getAttribute('data-rail-scroller'),
|
||||
'',
|
||||
'the rail layout pass keeps the strip scroll offset across measuring',
|
||||
);
|
||||
assert.equal(f.strip.getAttribute('aria-orientation'), 'vertical');
|
||||
assert.equal(f.rail.getAttribute(RIGHT_RAIL_TABS_ATTRIBUTE), 'vertical');
|
||||
assert.deepEqual(
|
||||
@@ -372,7 +367,6 @@ test('publishes the open tab extent on the rail and keeps a join element under t
|
||||
offsetHeight: 300,
|
||||
offsetWidth: 330,
|
||||
});
|
||||
f.strip.scrollTop = 0;
|
||||
f.cctv.classList.remove('collapsed');
|
||||
f.tabs.sync();
|
||||
assert.deepEqual([...props], [['--rail-tab-bottom', '142px']]);
|
||||
@@ -382,13 +376,6 @@ test('publishes the open tab extent on the rail and keeps a join element under t
|
||||
glass.style.clipPath,
|
||||
/^path\('M 16 0 L 314 0 A 16 16 0 0 1 330 16 L 330 78 /,
|
||||
);
|
||||
f.strip.scrollTop = 20;
|
||||
f.tabs.sync();
|
||||
assert.equal(
|
||||
props.get('--rail-tab-bottom'),
|
||||
'122px',
|
||||
'strip scroll is subtracted',
|
||||
);
|
||||
f.cctv.classList.add('collapsed');
|
||||
f.tabs.sync();
|
||||
assert.deepEqual([...props], [], 'no open panel, no join');
|
||||
@@ -517,16 +504,16 @@ test('a rail panel hidden at mount gets its tab when its owner unhides it, from
|
||||
tabs.destroy();
|
||||
});
|
||||
|
||||
test('a newly opened tab is scrolled into the strip window once; the join follows the visible part', () => {
|
||||
test('a tab shrunk by a short strip joins with corners no larger than half its height', () => {
|
||||
const f = fixture();
|
||||
// The strip's offsets are relative to its column, the column's to the rail.
|
||||
Object.assign(f.tabs.column, { offsetTop: 0, offsetLeft: 320 });
|
||||
Object.assign(f.strip, { clientHeight: 100, scrollTop: 0, offsetLeft: 10 });
|
||||
Object.assign(f.strip, { offsetLeft: 10 });
|
||||
const tab = f.tabFor('global-context-panel');
|
||||
Object.assign(tab, {
|
||||
offsetTop: 150,
|
||||
offsetLeft: 0,
|
||||
offsetHeight: 56,
|
||||
offsetHeight: 12,
|
||||
offsetWidth: 42,
|
||||
});
|
||||
Object.assign(f.context, {
|
||||
@@ -537,49 +524,13 @@ test('a newly opened tab is scrolled into the strip window once; the join follow
|
||||
});
|
||||
f.context.classList.remove('collapsed');
|
||||
f.tabs.sync();
|
||||
assert.equal(
|
||||
f.strip.scrollTop,
|
||||
106,
|
||||
'opened: scrolled so the tab bottom meets the window',
|
||||
);
|
||||
assert.equal(f.rail.style.props.get('--rail-tab-bottom'), '100px');
|
||||
assert.equal(f.rail.style.props.get('--rail-tab-bottom'), '162px');
|
||||
const [, glass] = f.tabs.join.children;
|
||||
f.strip.scrollTop = 220;
|
||||
f.tabs.sync();
|
||||
assert.equal(
|
||||
f.strip.scrollTop,
|
||||
220,
|
||||
'a sync after the user scrolled leaves the strip alone',
|
||||
);
|
||||
assert.equal(
|
||||
glass.style.clipPath,
|
||||
"path('M 16 0 L 314 0 A 16 16 0 0 1 330 16 L 330 284 A 16 16 0 0 1 314 300 L 16 300 A 16 16 0 0 1 0 284 L 0 16 A 16 16 0 0 1 16 0 Z')",
|
||||
'the tab is out of view: the join is the panel alone',
|
||||
);
|
||||
f.strip.scrollTop = 190;
|
||||
f.tabs.sync();
|
||||
assert.match(
|
||||
glass.style.clipPath,
|
||||
/^path\('M 16 0 L 364 0 A 8 8 0 0 1 372 8 A 8 8 0 0 1 364 16 L 338 16 A 8 8 0 0 0 330 24 L 330 284 /,
|
||||
'a tab half in view joins with the visible 16px and smaller corners',
|
||||
/L 366 150 A 6 6 0 0 1 372 156 A 6 6 0 0 1 366 162 L 338 162 /,
|
||||
'a 12px tab gets 6px corners',
|
||||
);
|
||||
const cctvTab = f.tabFor('cctv-panel');
|
||||
Object.assign(cctvTab, {
|
||||
offsetTop: 86,
|
||||
offsetLeft: 0,
|
||||
offsetHeight: 56,
|
||||
offsetWidth: 42,
|
||||
});
|
||||
Object.assign(f.cctv, {
|
||||
offsetTop: 0,
|
||||
offsetLeft: 0,
|
||||
offsetHeight: 300,
|
||||
offsetWidth: 330,
|
||||
});
|
||||
f.context.classList.add('collapsed');
|
||||
f.cctv.classList.remove('collapsed');
|
||||
f.tabs.sync();
|
||||
assert.equal(f.strip.scrollTop, 86, 'another tab opens: brought into view');
|
||||
});
|
||||
|
||||
test('railJoinPath shrinks the fillet and corner to the room left, down to a shared edge', () => {
|
||||
@@ -630,7 +581,7 @@ test('railJoinPath shrinks the fillet and corner to the room left, down to a sha
|
||||
);
|
||||
});
|
||||
|
||||
test('the stylesheet keeps phones on border-box, folds the strip into narrow desktops, shows a scrollbar and draws the focus ring inside', () => {
|
||||
test('the stylesheet keeps phones on border-box, folds the strip into narrow desktops, shrinks tabs instead of scrolling and draws the focus ring inside', () => {
|
||||
const css = readStylesheet(new URL('../../style.css', import.meta.url));
|
||||
assert.match(
|
||||
css,
|
||||
@@ -640,8 +591,16 @@ test('the stylesheet keeps phones on border-box, folds the strip into narrow des
|
||||
css,
|
||||
/@media \(min-width: 721px\) and \(max-width: 760px\) \{\s*#right-context-rail\[data-rail-tabs\] \{\s*width: calc\(330px - var\(--right-rail-tabs-inset\)\);/,
|
||||
);
|
||||
assert.match(css, /#right-rail-tabs \{[^}]*scrollbar-width: thin;/);
|
||||
assert.doesNotMatch(css, /#right-rail-tabs::-webkit-scrollbar/);
|
||||
assert.match(css, /#right-rail-tabs \{[^}]*overflow: hidden;/);
|
||||
assert.doesNotMatch(
|
||||
css,
|
||||
/#right-rail-tabs \{[^}]*(overflow: [^;]*auto|scrollbar-width)/,
|
||||
);
|
||||
// The tabs give way in proportion and clip their labels at the end.
|
||||
assert.match(
|
||||
css,
|
||||
/\.right-rail-tab \{[^}]*flex: 0 1 auto;[^}]*align-items: flex-start;[^}]*min-height: 0;[^}]*overflow: hidden;[^}]*mask-image: linear-gradient\(to bottom, #000 calc\(100% - 6px\), transparent\);/,
|
||||
);
|
||||
// The rail layout pass measures the column's natural height to climb above
|
||||
// its baseline before the strip has to scroll.
|
||||
assert.match(
|
||||
|
||||
@@ -88,12 +88,9 @@
|
||||
min-height: 0;
|
||||
/* Room for the open tab's bridge on the left. */
|
||||
padding-left: var(--right-rail-tabs-gap);
|
||||
overflow: hidden auto;
|
||||
/* The rail climbs above its baseline to fit the column (rightPanelRail.js);
|
||||
a lane still shorter overflows the strip: a thin scrollbar says there is
|
||||
more, and the strip keeps the open tab in view (rightRailTabs.js). */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
|
||||
a lane still shorter shrinks the tabs. The strip never scrolls. */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#right-rail-tools {
|
||||
@@ -172,16 +169,20 @@ body:not(.mobile-shell)
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
/* A tab shrinks in proportion to its label when the strip is short, and
|
||||
clips the label at its end, faded over the bottom padding. */
|
||||
.right-rail-tab {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
flex: 0 1 auto;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
width: var(--right-rail-tabs-width);
|
||||
min-height: 40px;
|
||||
min-height: 0;
|
||||
padding: 6px 0;
|
||||
overflow: hidden;
|
||||
mask-image: linear-gradient(to bottom, #000 calc(100% - 6px), transparent);
|
||||
border: 0;
|
||||
border-radius: var(--right-rail-tab-radius);
|
||||
background: transparent;
|
||||
@@ -228,9 +229,9 @@ body:not(.mobile-shell)
|
||||
}
|
||||
|
||||
.right-rail-tab {
|
||||
min-height: 36px;
|
||||
padding: 4px 0;
|
||||
letter-spacing: 1.3px;
|
||||
mask-image: linear-gradient(to bottom, #000 calc(100% - 4px), transparent);
|
||||
}
|
||||
|
||||
#right-rail-tools {
|
||||
|
||||
Reference in new issue
Block a user