PRIMITIVE
Scrollbar
The theme's own scrollbar on an element that already scrolls. Thin, themed, native — and gone entirely on touch, where the system's own bar is already right.
One class apart
Scroll both boxes. On a phone or a tablet they look identical, and that is the design rather than something missing.
The operating system's own scrollbar, on a surface it has never seen. On Linux and Windows it is a light trough with a pair of arrow buttons; on macOS it is an overlay bar that fades away when you stop.
Nothing here is broken. It is simply the only part of the page the theme does not reach.
Keep scrolling. There is deliberately more text than fits, in both boxes, because a scrollbar that never appears documents nothing at all.
<!-- Two identical boxes, one class apart. Scroll either one.
On a touch screen they are identical, deliberately. -->
<div class="hairline-grid" style="--cell: min(100%, 260px)">
<div class="hairline-cell">
<div class="section-head">
<span class="section-head-kicker">WITHOUT</span>
<span class="section-head-rule"></span>
</div>
<div class="flow" style="block-size: 150px; overflow: auto">
<p>
The operating system's own scrollbar, on a surface it has never seen. On
Linux and Windows it is a light trough with a pair of arrow buttons; on
macOS it is an overlay bar that fades away when you stop.
</p>
<p>
Nothing here is broken. It is simply the only part of the page the theme
does not reach.
</p>
<p>
Keep scrolling. There is deliberately more text than fits, in both
boxes, because a scrollbar that never appears documents nothing at all.
</p>
</div>
</div>
<div class="hairline-cell">
<div class="section-head">
<span class="section-head-kicker">WITH SCROLLBAR</span>
<span class="section-head-rule"></span>
</div>
<div class="flow scrollbar" style="block-size: 150px; overflow: auto">
<p>
The same box, one class added, nothing else changed. The bar is thin,
the thumb is the line colour this system draws its borders with, and the
trough is whatever happens to be behind it.
</p>
<p>
Put the pointer over this box and the thumb brightens. Tab to it — a
scroll container with no focusable children is in the tab order — and it
brightens the same way, because a reader arriving by keyboard never
hovers anything.
</p>
<p>
It does not make anything scrollable. This box was already a scroller;
the class only decides what its bar looks like.
</p>
</div>
</div>
</div>Reference
Read from primitives/scrollbar.css when this page was built, so it cannot fall behind the stylesheet.
.scrollbar--ds-scroll-thumb--ds-scroll-thumb-hover--ds-scroll-trackNotes
<!-- The element must ALREADY scroll. This class only decides
what its bar looks like — it sets no overflow of its own. -->
<div class="gallery-track scrollbar">…</div>A phone draws an overlay scrollbar that already looks right, costs no layout and fades on its own. There is nothing here worth improving and a great deal worth breaking. So the whole rule sits inside @media (pointer: fine), and this class is a no-op on every touch device — measured on Pixel 7, iPhone 14 and iPad, all of which report pointer: fine as false and keep scrollbar-width: auto.
It is not a width query, so the breakpoint-free rule is intact. prefers-reduced-motion in base.css is the same shape: a question about the reader, never about the window.
Why not ::-webkit-scrollbar
It is the better-known technique, it offers far more control — a radius, a width in pixels, margins, corners — and it was measured and rejected on all three engines. Chromium 151, Firefox 153 and WebKit 26.5, headed, reading pixels rather than documentation.
scrollbar-color, painted nothing at all. You cannot have both.::-webkit-scrollbar rule carrying a width flips Firefox from an overlay bar to a classic one: it takes 12px of layout away from the content and paints a native grey bar, ignoring every colour given to it. The value is ignored; only the rule's presence matters.Where it goes
On the scroller itself. Both properties inherit, so putting it on a wrapper themes every scroll container inside — occasionally useful, and never what you want on the shell.
The gallery's lightbox carries it, which is the reason it exists. Nothing else in the library scrolls today — modal-body is overflow: auto but no specimen is long enough to show a bar. The first modal with more content than height wants this class beside modal-body, for exactly the reason the lightbox did.
What the caller sets
Nothing, normally. A theme moves all three at once, and the trough is transparent so a scroller on a raised surface needs no rule of its own.
--ds-scroll-thumb: var(--ds-rule-strong); /* at rest */
--ds-scroll-thumb-hover: var(--ds-ink-faint); /* hover, focus-within */
--ds-scroll-track: transparent; /* the trough */scrollbar-width takes auto, thin or none and no length at all. In Chromium thin measured 10px against auto's 15px, so the content box gains 5px when this class is added. That is a layout change rather than a repaint, and it is the one thing to know before adding it to something already tuned.
Hiding a scrollbar removes the only indication that there is more to see. In the gallery it would delete one of the three documented ways to reach every picture — the arrows, the swipe, and the bar. A variant with no consumer is not shipped here, and a variant that removes an affordance needs a better argument than tidiness.