CSS Scrollbar Styling: scrollbar-color and scrollbar-width
CSS now styles scrollbars natively with scrollbar-color and scrollbar-width, replacing years of vendor-prefixed hacks. Here's how they work.
Scrollbar styling in CSS lets you control the color and thickness of a scroll container’s scrollbar directly, using the scrollbar-color and scrollbar-width properties, without touching the browser’s default track-and-thumb rendering pipeline through hacks or images. It replaced a long era of non-standard, vendor-specific pseudo-elements that worked differently — or not at all — across browsers.
The two standard properties
scrollbar-width controls thickness. It takes one of three keywords:
.scroll-box {
scrollbar-width: auto; /* default, platform-native width */
scrollbar-width: thin; /* a narrower scrollbar */
scrollbar-width: none; /* hides it, but scrolling still works */
}
scrollbar-color sets the thumb and track colors as a space-separated pair:
.scroll-box {
scrollbar-color: #6366f1 #e5e7eb; /* thumb-color track-color */
}
Set to auto, the browser uses its platform default. There’s no separate hover or active state property — browsers apply their own subtle hover feedback automatically on top of whatever base colors you set.
Why this replaced the old approach
Before these properties shipped broadly, styling a scrollbar meant reaching for ::-webkit-scrollbar and its related pseudo-elements (::-webkit-scrollbar-thumb, ::-webkit-scrollbar-track, and so on) — a WebKit-specific extension that Chromium-based browsers also adopted. It worked, but it wasn’t standardized: Firefox never implemented it, so any site relying on it needed a completely separate fallback (or no styling at all) for non-Chromium browsers. It also required a fair amount of boilerplate for something conceptually simple — setting a color and a width.
scrollbar-color and scrollbar-width are standard CSS properties supported across modern engines, so the same two lines work everywhere without a fallback branch. The tradeoff is control: the ::-webkit-scrollbar family let you style corner pieces, buttons, and apply gradients or border-radius to the thumb, which the standard properties don’t expose. For most UI needs — a subtler scrollbar that matches your color scheme instead of the OS default — the standard properties are enough.
Using custom properties for theming
Because scrollbar-color takes plain color values, it composes naturally with CSS custom properties and dark-mode variables:
:root {
--scrollbar-thumb: #94a3b8;
--scrollbar-track: transparent;
}
@media (prefers-color-scheme: dark) {
:root {
--scrollbar-thumb: #475569;
}
}
* {
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
scrollbar-width: thin;
}
Applying it globally on * (or html) is the common pattern, since scrollbars can appear on any scrollable element, not just the document itself — long code blocks, modal bodies, and sidebar navs all pick up the same theme automatically.
Scrollbar-gutter: reserving space to avoid layout shift
A related property, scrollbar-gutter, addresses a different but adjacent problem: on desktop browsers where scrollbars take up layout space, content can shift horizontally the instant a scrollbar appears or disappears (for example, when a modal opens and the body becomes non-scrollable). Setting scrollbar-gutter: stable reserves the scrollbar’s space permanently, even when there’s nothing to scroll yet, so the layout doesn’t jump:
html {
scrollbar-gutter: stable;
}
This matters most in Core Web Vitals terms for pages where content height changes dynamically — infinite scroll feeds, expandable accordions — since an appearing scrollbar can register as a layout shift.
Hiding scrollbars without breaking scrolling
scrollbar-width: none hides the scrollbar visually while keeping the element scrollable by touch, mouse wheel, or keyboard. This is common in horizontally scrolling carousels or tab strips where a visible scrollbar would look out of place. It’s worth pairing with a visible affordance — like a partial next item peeking into view, or explicit next/previous buttons — so the ability to scroll isn’t hidden along with the scrollbar. Relying purely on overflow combined with a hidden scrollbar (see CSS overflow) is fine functionally, but accessibility guidance still favors giving users some visual cue that more content exists.
Browser support and fallback behavior
Both properties degrade gracefully: a browser that doesn’t recognize scrollbar-color simply ignores the declaration and falls back to its native scrollbar rendering. There’s no need for @supports guards or JavaScript feature detection — worst case, older browsers just show the default scrollbar instead of your themed one, which was already the pre-CSS-styling behavior.
The takeaway
scrollbar-color and scrollbar-width give you cross-browser scrollbar theming in two lines of standard CSS, covering thumb color, track color, and thickness without vendor prefixes. For finer visual control — rounded thumbs, gradients, custom buttons — the older ::-webkit-scrollbar pseudo-elements are still there as a Chromium-only enhancement layer, but the standard properties should be your default starting point.
Tagged
Keep reading
Takina · · 4 min read What Is CSS Subgrid? Nested Grid Alignment Explained
CSS subgrid lets a nested grid item inherit its parent's track sizing, so children can align to the same columns or rows across unrelated containers.
Takina · · 4 min read CSS field-sizing Property Explained
The CSS field-sizing property lets form controls like textareas grow to fit their content automatically, without JavaScript resize listeners.
Takina · · 4 min read The CSS color-scheme Property, Explained
CSS color-scheme tells the browser which themes a page supports so native form controls, scrollbars, and UI render in dark mode without extra CSS.