diff --git a/apps/desktop/src/renderer/maka-tokens.css b/apps/desktop/src/renderer/maka-tokens.css index 8c30aaa5db..b861518e4d 100644 --- a/apps/desktop/src/renderer/maka-tokens.css +++ b/apps/desktop/src/renderer/maka-tokens.css @@ -1228,6 +1228,39 @@ @layer base { * { box-sizing: border-box; border-color: var(--border); } + /* One scrollbar for the whole app (visual system 2.0 T5-F): the platform + thin bar, coloured with `--border-strong` over a transparent gutter. The + universal selector is intentional because `scrollbar-width` is not + inherited. This base-layer default yields to element-owned declarations + in the higher Astryx and product component layers, such as a TabList + strip's `none` (#2538). */ + * { + scrollbar-width: thin; + scrollbar-color: var(--border-strong) transparent; + } + + *::-webkit-scrollbar { + width: 10px; + height: 10px; + } + + *::-webkit-scrollbar-track, + *::-webkit-scrollbar-corner { + background: transparent; + } + + *::-webkit-scrollbar-thumb { + border: 2px solid transparent; + border-radius: var(--radius-pill); + background: var(--border-strong); + background-clip: content-box; + } + + *::-webkit-scrollbar-thumb:hover { + /* One step above --border-strong's 16%, scoped to the thumb only. */ + background-color: oklch(from var(--foreground) l c h / 0.28); + } + /* The root font-size stays at the browser default (16px) ON PURPOSE. It used to be pinned to 13px to express Maka's density, but the root is not a density knob: it is an implicit multiplier on every rem in the diff --git a/apps/desktop/src/renderer/styles/base.css b/apps/desktop/src/renderer/styles/base.css index 23b0804678..c6b0315f5e 100644 --- a/apps/desktop/src/renderer/styles/base.css +++ b/apps/desktop/src/renderer/styles/base.css @@ -126,36 +126,3 @@ button { transition: none !important; caret-color: transparent !important; } - -/* One scrollbar for the whole app (visual system 2.0 T5-F). Both reference - systems converged on the same recipe: a slim pill inset in a transparent - gutter, one step darker on hover, no painted track. The 2px transparent - border + content-box clip turns the 10px hit area into a 6px pill. The - few surfaces that hide or thin their own bars are more specific and keep - winning. */ -* { - scrollbar-width: thin; - scrollbar-color: var(--border-strong) transparent; -} - -*::-webkit-scrollbar { - width: 10px; - height: 10px; -} - -*::-webkit-scrollbar-track, -*::-webkit-scrollbar-corner { - background: transparent; -} - -*::-webkit-scrollbar-thumb { - border: 2px solid transparent; - border-radius: var(--radius-pill); - background: var(--border-strong); - background-clip: content-box; -} - -*::-webkit-scrollbar-thumb:hover { - /* One step above --border-strong's 16%, scoped to the thumb only. */ - background-color: oklch(from var(--foreground) l c h / 0.28); -} diff --git a/apps/desktop/stories/session-workbar.stories.tsx b/apps/desktop/stories/session-workbar.stories.tsx index 645e408027..366fc522b2 100644 --- a/apps/desktop/stories/session-workbar.stories.tsx +++ b/apps/desktop/stories/session-workbar.stories.tsx @@ -1087,6 +1087,31 @@ export const SeveralFacesAtColumnFloor: Story = { render: () => ( ), + play: async ({ canvasElement }) => { + // Astryx's TabList hides its own overflow scrollbar (`scrollbar-width: + // none`) and scrolls the strip instead. The app default must not override + // that: as a `*` rule in `layer(components)` it out-ranked the component + // layer and re-showed the bar (#2538). The app default now lives in the + // lower `base` layer, so it no longer competes with the strip's `none`. + const tablist = await within(canvasElement).findByRole('tablist'); + const nodes = [tablist, ...tablist.querySelectorAll('*')]; + const strip = nodes.find((node) => getComputedStyle(node).overflowX === 'auto'); + if (!strip) { + throw new Error('expected the tab strip to expose a horizontal scroll container'); + } + expect(getComputedStyle(strip).scrollbarWidth).toBe('none'); + + // Keeping the default universal is equally important: `scrollbar-width` + // does not inherit, so a root-only rule would leave this scrollport `auto`. + const reviewPanel = await waitFor(() => { + const element = canvasElement.querySelector( + '.maka-session-review-panel', + ); + if (!element) throw new Error('expected the review panel to render'); + return element; + }); + expect(getComputedStyle(reviewPanel).scrollbarWidth).toBe('thin'); + }, }; // Below 991px the column stacks under the conversation at full width. The