/* Scrollbar skin: the sole consumer of the four --dsw-alias-scrollbar-* * tokens. Without it every scrolling region renders the UA scrollbar, which * ignores the theme — a light native bar over the dark palette. * * The rules sit on `body`, not `html`: design-platform.css declares the * --dsw-alias-* tokens on `body` (and the dark overrides on * `body[data-ds-dark-theme]`), and custom properties only inherit downward, * so an `html` rule resolves them to the guaranteed-invalid value and * `scrollbar-color` falls back to `auto`. * * Surfaces pick their elevation by rebinding --dsh-scrollbar-thumb{,-hover}: * the l1 pair here is the base-surface default, and an elevated surface * (menu, popover, dialog) rebinds to the l2 pair on its own container. Both * rendering paths below read the indirection, so one rebind reaches whichever * path the engine took. */ body { --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l1); --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l1); } /* The two paths are mutually exclusive, and the gate is load-bearing rather than defensive. A non-`auto` `scrollbar-width` or `scrollbar-color` makes Chromium and Safari drop every `::-webkit-scrollbar*` rule for that element, including `::-webkit-scrollbar-thumb:hover` — measured in chromium as an 8px `::-webkit-scrollbar` width taking effect on its own and being ignored as soon as `scrollbar-width: thin` is added. Declaring both unconditionally therefore leaves the hover tokens with no rendering at all, because the engines that implement the hover pseudo-element are exactly the ones the standard properties silence, and Firefox has no hover pseudo-element to fall back on. `not selector(::-webkit-scrollbar)` is true only where the pseudo-element is unimplemented, so Firefox takes the standard path and WebKit-based engines take the pseudo-element path. An engine too old for the `selector()` function makes the condition invalid, which evaluates false and selects the pseudo-element path — the correct side for the pre-16.4 Safari that is the realistic case. */ @supports not selector(::-webkit-scrollbar) { /* Declared on every element rather than inherited from `body`. Inheriting would pass down the COLOUR already substituted at `body`, so a descendant rebinding --dsh-scrollbar-thumb could not change it; re-declaring makes each element substitute the variable as it sees it, which is what gives an elevated surface a working rebind. `scrollbar-width` is not an inherited property at all, so it needs the per-element declaration regardless. No hover counterpart exists on this path: `scrollbar-color` states one thumb colour and the engine derives its own hover treatment. */ body, body * { scrollbar-width: thin; scrollbar-color: var(--dsh-scrollbar-thumb) transparent; } } /* Not gated in turn: an engine that does not implement these pseudo-elements drops the rules as unknown selectors, so the gate would only restate what selector matching already does. Not inherited either, hence the unscoped selectors. */ ::-webkit-scrollbar { width: 8px; height: 8px; } /* Track stays transparent so the thumb reads against whatever surface scrolls under it; only the thumb carries a token colour. */ ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { border-radius: 4px; background: var(--dsh-scrollbar-thumb); } ::-webkit-scrollbar-thumb:hover { background: var(--dsh-scrollbar-thumb-hover); } /* Both scrollbars meeting in a corner: no separate token, so the corner matches the transparent track rather than the UA's opaque default. */ ::-webkit-scrollbar-corner { background: transparent; }