How to Style CSS Scrollbars: Complete Cross-Browser Guide

Default browser scrollbars can easily conflict with a site’s visual style, and customizing them relies on two different methods: the W3C standardized properties (scrollbar-width, scrollbar-color, scrollbar-gutter) and the -webkit-scrollbar pseudo-element family supported by Chromium and WebKit. The W3C published the CSS Scrollbars Level 1 specification in September 2018; both methods now have broad support in modern browsers.

You still need two sets of rules because the standardized properties and the -webkit-scrollbar approach overlap in capability without offering exactly the same features. Current compatibility information is available at caniuse.com/css-scrollbar.

This guide explains both methods, shows where their capabilities intersect and where they diverge, and includes ready-to-use patterns for common UI components including chat windows, sidebars, code blocks, and modals.

Key Takeaways

  • The CSS Scrollbars Level 1 standardized properties (scrollbar-width, scrollbar-color, scrollbar-gutter) are the W3C-specified approach and are supported in Chrome 121+, Firefox 64+, Edge 121+, and current Safari.
  • The -webkit-scrollbar pseudo-element approach is still necessary for rounded thumbs, custom track colors, and border-based padding in Chrome, Safari, and Chromium Edge.
  • Firefox does not implement -webkit-scrollbar pseudo-elements. Apply scrollbar-width and scrollbar-color for Firefox.
  • Apply @supports (scrollbar-color: auto) to detect standard property support and scope each block explicitly when isolation is needed.
  • scrollbar-gutter: stable avoids layout movement when a scrollbar appears or disappears by setting aside scrollbar gutter space ahead of time.
  • The scrollbar thumb color needs a contrast ratio of at least 3:1 against the track color to meet WCAG 2.1 Non-text Contrast requirements.

Prerequisites

To work through this guide, you should have:

  • Basic familiarity with vendor prefixes
  • An understanding of pseudo-elements
  • Knowledge of graceful-degradation techniques

Browser Compatibility

Feature Chrome Edge Firefox Safari
scrollbar-width 121 121 64 18.2†
scrollbar-color 121 121 64 18.2†
scrollbar-gutter 94 94 97 18.2†
::-webkit-scrollbar family Yes 79 Not supported Yes

† Safari support for the standard scrollbar properties reached Baseline “newly available” status in December 2024. Confirm current behavior against caniuse.com/css-scrollbar before publishing, as implementation notes are still being updated.

The standardized properties now work in all major browser engines. The ::-webkit-scrollbar pseudo-element family continues to be the only method for applying border-radius, custom track styling, or border-based thumb padding in Chromium and WebKit browsers.

The Standard CSS Scrollbars Specification

CSS Scrollbars Level 1 defines three properties that control scrollbar appearance: scrollbar-width, scrollbar-color, and scrollbar-gutter. These properties are now supported in Chrome 121+, Edge 121+, Firefox 64+, and current Safari.

scrollbar-width

scrollbar-width determines the rendered scrollbar size. Supported values are auto, thin, and none.

/* scrollbar-width: controls rendered size of the scrollbar */
/* Chrome 121+, Edge 121+, Firefox 64+, Safari - see compatibility table */
.scroll-area {
  scrollbar-width: thin; /* auto | thin | none */
  overflow: auto;
}

thin produces a narrower bar than the platform default; the browser and operating system decide the exact width and differs between environments. auto keeps the platform’s native size. none removes the visible bar while keeping scrolling available. The standard property cannot define a precise pixel width: for that you must use ::-webkit-scrollbar { width }, which is supported in Chromium and WebKit only.

Browsers apply a minimum thumb height so the handle remains easy to grab regardless of content length. In Chrome this floor is approximately 32px; in Firefox approximately 18px. A very thin scrollbar on a very long page will stop shrinking at that point.

scrollbar-color

scrollbar-color defines thumb and track colors with a two-value syntax. The first value controls the thumb, while the second controls the track. border-radius and padding cannot be configured with this property.

/* scrollbar-color: sets thumb and track colors */
/* see compatibility table */
.scroll-area {
  height: 200px;
  overflow: auto;
  scrollbar-color: blue orange; /* thumb track */
}

scrollbar-gutter

A container that receives content dynamically, such as a chat feed, a comment list, or a filter dropdown, can move its content sideways as soon as a scrollbar appears. scrollbar-gutter: stable avoids that movement by reserving gutter space before the scrollbar is drawn. The stable both-edges variant reserves equal space on both sides, which helps the layout stay visually centered in containers where visual symmetry is important, such as a card grid or a centered dialog. Supported values are auto, stable, and stable both-edges.

/* scrollbar-gutter: reserves space for the scrollbar to prevent layout shift */
/* Chrome 94+, Edge 94+, Firefox 97+, Safari - see compatibility table */
.scroll-area {
  scrollbar-gutter: stable;
  overflow: auto;
}

Styling Scrollbars in Chrome, Edge, and Safari

Scrollbar customization in Chrome, Edge, and Safari can be done with the vendor-prefixed -webkit-scrollbar pseudo-element family. The example below uses the three pseudo-elements that are used most often:

body::-webkit-scrollbar {
  width: 12px;               /* width of the entire scrollbar */
}
body::-webkit-scrollbar-track {
  background: orange;        /* color of the tracking area */
}
body::-webkit-scrollbar-thumb {
  background-color: blue;    /* color of the scroll thumb */
  border-radius: 20px;       /* roundness of the scroll thumb */
  border: 3px solid orange;  /* creates padding around scroll thumb */
}

The border on ::-webkit-scrollbar-thumb uses the same color as the track, which visually insets the thumb and creates a padding effect. When the track color changes, update the thumb border to the same color, otherwise the inset effect is lost and the border becomes a visible ring around the thumb.

This code works in current releases of Chrome, Edge, and Safari. The -webkit-scrollbar pseudo-elements are non-standard and are not part of the W3C standards track. The CSS Scrollbars Level 1 properties (scrollbar-width, scrollbar-color, scrollbar-gutter) are the W3C-standardized approach and the preferred standards-based direction. Both approaches can be used in the same stylesheet using feature queries.

For reference, the complete pseudo-element family is listed below:

seudo-Element Description
::-webkit-scrollbar Sets the width (vertical) or height (horizontal) of the scrollbar
::-webkit-scrollbar-track Styles the track, the channel the thumb moves within
::-webkit-scrollbar-track-piece Styles the portion of the track not covered by the thumb
::-webkit-scrollbar-thumb Styles the draggable thumb; supports border-radius and border
::-webkit-scrollbar-button Styles the arrow buttons at each end of the scrollbar
::-webkit-scrollbar-corner Styles the corner where vertical and horizontal scrollbars meet

Hiding the arrow buttons at each end and styling the scrollbar corner are common needs that ::-webkit-scrollbar alone does not address:

/* Hide arrow buttons at each end of the scrollbar */
/* Chrome, Safari, Edge (Chromium) */
::-webkit-scrollbar-button {
  display: none;
}
/* Style the corner where both scrollbars meet */
/* Chrome, Safari, Edge (Chromium) */
.code-block::-webkit-scrollbar-corner {
  background: #1a1a1a;
}

Thumb Hover and Active States

To give the thumb interactive feedback, style ::-webkit-scrollbar-thumb:hover and ::-webkit-scrollbar-thumb:active. The standard scrollbar-color property does not provide equivalent hover or active states, so this refinement is Chromium and WebKit only.

/* Thumb darkens on hover - Chrome, Safari, Edge (Chromium) only */
.scroll-area::-webkit-scrollbar-thumb {
  background-color: #888;
}
.scroll-area::-webkit-scrollbar-thumb:hover {
  background-color: #555;
}
.scroll-area::-webkit-scrollbar-thumb:active {
  background-color: #333;
}

Building Future-Proof Scrollbar Styles

A single stylesheet can support both engine families. Each browser silently ignores rules for selectors it does not implement, so placing the standardized properties and the webkit pseudo-elements in two sequential blocks is all that is needed:

/* Standard properties: Chrome 121+, Edge 121+, Firefox 64+ */
body {
  scrollbar-width: thin;
  scrollbar-color: blue orange;
}
/* Webkit pseudo-elements: Chrome, Edge, Safari */
body::-webkit-scrollbar {
  width: 12px;
}
body::-webkit-scrollbar-track {
  background: orange;
}
body::-webkit-scrollbar-thumb {
  background-color: blue;
  border-radius: 20px;
  border: 3px solid orange;
}

Do not use the * universal selector for scrollbar rules; because it affects every element on the page, including nested scroll containers that you may not want to customize. Limit rules to body for page-wide styling or to a specific class for an individual component.

Blink and WebKit browsers ignore rules they do not recognize and apply -webkit-scrollbar rules. Firefox ignores rules it does not recognize and applies CSS Scrollbars rules. This two-block approach works because each browser skips unsupported selectors.

In Chromium, one extra precedence rule applies: after any ::-webkit-scrollbar pseudo-element is declared for an element, Chromium uses the webkit styling for that element instead of scrollbar-color and scrollbar-width on it. This behavior applies at the element level in Chromium and is why the two-block pattern works cleanly: Firefox takes the standardized properties and skips the webkit rules; Chromium takes the webkit rules and skips the standardized properties.

When explicit scoping is necessary, use @supports feature queries:

/* Standard properties where supported: Chrome 121+, Edge 121+, Firefox 64+ */
@supports (scrollbar-color: auto) {
  .scroll-area {
    scrollbar-width: thin;
    scrollbar-color: #2563eb #e5e7eb;
  }
}
/* Webkit pseudo-elements where supported: Chrome, Safari, Edge (all Chromium) */
@supports selector(::-webkit-scrollbar) {
  .scroll-area::-webkit-scrollbar {
    width: 10px;
  }
.scroll-area::-webkit-scrollbar-track {
    background: #e5e7eb;
  }
.scroll-area::-webkit-scrollbar-thumb {
    background-color: #2563eb;
    border-radius: 8px;
    border: 2px solid #e5e7eb;
  }
}

The @supports version makes the scoping explicit and avoids cross-engine rule bleed. Use the simpler pattern when targeting modern browsers only; use @supports when you need to guarantee isolation.

Overlay and Classic Scrollbars

If custom scrollbar rules seem to do nothing, the operating system may be using overlay scrollbars. Overlay scrollbars sit on top of the content as a semi-transparent thumb with no track, become visible only during scrolling, and do not consume layout space. macOS defaults to overlay scrollbars for most users; Windows defaults to classic (always-visible) scrollbars.

In overlay mode, scrollbar-color and webkit track styling often produce no visible result, because the track is not rendered and the thumb appearance is managed by the OS. This is a frequent reason for “why is my styling doing nothing on macOS Safari?” reports and is related to the open questions around Safari scrollbar-color behavior on recent macOS versions.

A useful way to think about it is this: classic scrollbars, as seen on Windows or on macOS when the system preference is set to always show scrollbars, can respond to both the standardized properties and the webkit pseudo-elements. Overlay scrollbars generally do not. Test both scrollbar modes before release, and do not expect the custom appearance to be visible for every user on every platform.

Mobile and Touch Scrollbar Behavior

Custom scrollbar styling mainly affects desktop environments. On iOS, Safari uses momentum-based overlay scrollbars during touch scrolling and does not use -webkit-scrollbar styling to them. On Android, Chrome’s scrollbar support is incomplete and can differ by version. In both cases, any styled scrollbar will fall back to the platform default without an error.

Do not make a styled scrollbar the only navigation cue for touch users. Any component that requires custom scrollbar visibility for usability should provide another visual cue at the edge of the scroll container, such as a fade gradient:

/* Touch-friendly cue: fade the bottom edge to hint at more content */
.scroll-container {
  overflow-y: auto;
  -webkit-mask-image: linear-gradient(to bottom, black calc(100% - 24px), transparent);
          mask-image: linear-gradient(to bottom, black calc(100% - 24px), transparent);
}

Dark-Mode Theming with Custom Properties

Store scrollbar colors in CSS custom properties and switch them in a prefers-color-scheme: dark media query. This centralizes the color values and lets the scrollbar follow the active theme automatically.

/* Theme scrollbar colors once, swap them for dark mode */
:root {
  --sb-thumb: #888;
  --sb-track: #f0f0f0;
}
@media (prefers-color-scheme: dark) {
  :root {
    --sb-thumb: #555;
    --sb-track: #1e1e1e;
  }
}
.scroll-area {
  scrollbar-color: var(--sb-thumb) var(--sb-track);
}
.scroll-area::-webkit-scrollbar-thumb {
  background-color: var(--sb-thumb);
}
.scroll-area::-webkit-scrollbar-track {
  background: var(--sb-track);
}

Hide a Scrollbar While Keeping Scroll Functionality

To hide a scrollbar while keeping scrolling active, the implementation differs by browser.

Hide Scrollbar in Chrome and Safari

/* Hide scrollbar in Chrome, Safari, and Chromium-based Edge */
/* Scroll functionality is preserved */
.hide-scrollbar::-webkit-scrollbar {
  display: none;
}

Hide Scrollbar in Firefox

/* Hide scrollbar in Firefox */
/* Scroll functionality is preserved */
.hide-scrollbar {
  scrollbar-width: none;
}

Cross-Browser Hide Scrollbar Pattern

/* Cross-browser: hide scrollbar while preserving scroll */
/* Apply overflow: auto or scroll to the same element */
.hide-scrollbar {
  overflow: auto;
  scrollbar-width: none;      /* Firefox and standard */
  -ms-overflow-style: none;   /* IE and pre-Chromium Edge (legacy) */
}
.hide-scrollbar::-webkit-scrollbar {
  display: none;              /* Chrome, Safari, Edge (Chromium) */
}

Do not remove visible scrollbars from areas that rely on them for navigation unless the element can be operated from the keyboard (focusable and scrollable with arrow keys or Page Up/Page Down). See the Accessibility Considerations section for WCAG guidance.

How to Style a Vertical Scrollbar with CSS

Both the standardized properties and the webkit pseudo-elements use the width axis for vertical scrollbars. Apply overflow-y: auto on the container to enable scrolling, then style the result with scrollbar-width and scrollbar-color for cross-browser sizing, plus ::-webkit-scrollbar { width } for precise control in Chromium and WebKit.

Vertical Scrollbar Width and Color

/* Vertical scrollbar: standard properties */
/* Chrome 121+, Edge 121+, Firefox 64+, Safari - see compatibility table */
.vertical-scroll {
  overflow-y: auto;
  height: 300px;
  scrollbar-width: thin;
  scrollbar-color: #555 #f0f0f0;
}
/* Vertical scrollbar: webkit pseudo-elements */
/* Chrome, Safari, Edge (Chromium) */
.vertical-scroll::-webkit-scrollbar {
  width: 8px;
}
.vertical-scroll::-webkit-scrollbar-track {
  background: #f0f0f0;
}
.vertical-scroll::-webkit-scrollbar-thumb {
  background-color: #555;
}

Rounded Thumb Example with Code

border-radius on the thumb depends on the ::-webkit-scrollbar-thumb pseudo-element. It cannot be set through the standard scrollbar-color property.

/* Rounded thumb: Chrome, Safari, Edge (Chromium) only */
.rounded-scroll::-webkit-scrollbar {
  width: 8px;
}
.rounded-scroll::-webkit-scrollbar-track {
  background: #f0f0f0;
  border-radius: 4px;
}
.rounded-scroll::-webkit-scrollbar-thumb {
  background-color: #0a84ff;
  border-radius: 20px;
  border: 2px solid #f0f0f0; /* border matches track to create visual padding */
}

The thumb supports any valid CSS background value, including gradients. Because CSS treats a gradient as an image, define it with background or background-image, not background-color. For example, background: linear-gradient(to bottom, #0a84ff, #0055cc) produces a gradient thumb in Chromium and WebKit.

How to Style a Horizontal Scrollbar with CSS

With the -webkit-scrollbar pseudo-element, the horizontal scrollbar is controlled by height rather than width. width controls the vertical scrollbar.

/* Horizontal scrollbar: standard properties */
/* Chrome 121+, Edge 121+, Firefox 64+, Safari - see compatibility table */
.horizontal-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  white-space: nowrap;
  scrollbar-width: thin;
  scrollbar-color: #0a84ff #e0e0e0;
}
/* Horizontal scrollbar: webkit pseudo-elements */
/* Chrome, Safari, Edge (Chromium) */
/* Use height, not width, for the horizontal bar */
.horizontal-scroll::-webkit-scrollbar {
  height: 6px;
}
.horizontal-scroll::-webkit-scrollbar-track {
  background: #e0e0e0;
}
.horizontal-scroll::-webkit-scrollbar-thumb {
  background-color: #0a84ff;
  border-radius: 10px;
}

Practical Apply Cases

The patterns below are limited to specific classes so only the intended container is affected, not the entire page. Every pattern contains both standardized properties and webkit pseudo-elements.

Chat Window or Message Feed

A chat feed’s scrollbar should remain subtle until the user interacts with it. A transparent track keeps the track visually unobtrusive at rest; a narrow thumb that darkens on hover makes the control more noticeable when needed. The hover state requires ::-webkit-scrollbar-thumb:hover and is Chromium and WebKit only.

/* Chat window: narrow scrollbar, transparent track, hover darkening */
/* Standard: Chrome 121+, Edge 121+, Firefox 64+ | Webkit: Chrome, Safari, Edge */
.chat-window {
  height: 400px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: #b0b0b0 transparent;
}
.chat-window::-webkit-scrollbar {
  width: 6px;
}
.chat-window::-webkit-scrollbar-track {
  background: transparent;
}
.chat-window::-webkit-scrollbar-thumb {
  background-color: #b0b0b0;
  border-radius: 10px;
}
.chat-window::-webkit-scrollbar-thumb:hover {
  background-color: #888; /* darkens on hover so the bar surfaces on demand */
}

Sidebar Navigation

A sidebar that supports a switchable light/dark theme should store scrollbar colors as component-level CSS custom properties, not as hard-coded hex values. This allows one theme change to update the scrollbar without editing the component rules.

/* Sidebar navigation: dark theme, colors via CSS custom properties */
/* Standard: Chrome 121+, Edge 121+, Firefox 64+ | Webkit: Chrome, Safari, Edge */
.sidebar {
  --sb-thumb: #555;
  --sb-track: #1e1e1e;
width: 240px;
height: 100vh;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--sb-thumb) var(--sb-track);
}
.sidebar::-webkit-scrollbar {
  width: 5px;
}
.sidebar::-webkit-scrollbar-track {
  background: var(--sb-track);
}
.sidebar::-webkit-scrollbar-thumb {
  background-color: var(--sb-thumb);
  border-radius: 4px;
}

Code Editor Block

Code areas often scroll in both directions and are commonly displayed with a dark theme. Setting equal width and height on ::-webkit-scrollbar keeps the two scrollbar axes equally thick. The ::-webkit-scrollbar-corner colors the area where the two bars intersect; without this rule, the corner may appear as a white square against a dark interface.

/* Code editor block: dark scrollbar on both axes with corner fill */
/* Standard: Chrome 121+, Edge 121+, Firefox 64+ | Webkit: Chrome, Safari, Edge */
.code-block {
  overflow: auto;
  max-height: 300px;
  scrollbar-width: thin;
  scrollbar-color: #4a4a4a #1a1a1a;
}
.code-block::-webkit-scrollbar {
  width: 8px;
  height: 8px; /* height controls the horizontal bar */
}
.code-block::-webkit-scrollbar-track {
  background: #1a1a1a;
}
.code-block::-webkit-scrollbar-thumb {
  background-color: #4a4a4a;
  border-radius: 4px;
  border: 2px solid #1a1a1a; /* border matches track; insets the thumb visually */
}
.code-block::-webkit-scrollbar-corner {
  background: #1a1a1a; /* fills the gap where both bars meet */
}

Modal or Dialog Overlay

When dialog content exceeds the available height, a scrollbar a scrollbar may appear and move the text horizontally. Adding scrollbar-gutter: stable sets aside gutter width before overflow occurs, which prevents the layout from jumping even when the scrollbar is not yet visible.

/* Modal content area: scoped scrollbar, gutter reserved to prevent layout shift */
/* Standard: Chrome 121+, Edge 121+, Firefox 64+ | Webkit: Chrome, Safari, Edge */
.modal-body {
  max-height: 60vh;
  overflow-y: auto;
  scrollbar-gutter: stable; /* reserves gutter so content does not shift on overflow */
  scrollbar-width: thin;
  scrollbar-color: #999 #f5f5f5;
}
.modal-body::-webkit-scrollbar {
  width: 6px;
}
.modal-body::-webkit-scrollbar-track {
  background: #f5f5f5;
}
.modal-body::-webkit-scrollbar-thumb {
  background-color: #999;
  border-radius: 6px;
}

How to Conditionally Hide/Show Scrollbars

CSS media queries or JavaScript can be used to show or hide scrollbars conditionally. For example, you may hide scrollbars on smaller screens where content fits within the viewport, and show them on larger screens when content overflows.

@media (max-width: 768px) {
  body {
    overflow: hidden; /* Disables scrolling entirely on smaller screens - not a scrollbar-hiding technique */
  }
}
@media (min-width: 769px) {
  body {
    overflow: auto; /* Shows the scrollbar on larger screens */
  }
}

To remove only the visible bar while keeping scrolling enabled, use the cross-browser .hide-scrollbar pattern from Hide a Scrollbar While Keeping Scroll Functionality. At the chosen breakpoint, apply that class to the scroll container so those rules affect only the required container:

/* Small viewports: add hide-scrollbar to the element (class rules defined above) */
@media (max-width: 768px) {
  .scroll-container {
    overflow: auto;
  }
}

Use JavaScript to toggle the class when the content does not overflow:

// Hide the scrollbar only when content does not overflow
const el = document.querySelector('.scroll-container');
el.classList.toggle('hide-scrollbar', el.scrollHeight <= el.clientHeight);

Using CSS Scrollbars with JavaScript

Interactions The simplest way to implement smooth scrolling is with CSS, requiring no JavaScript:

/* Smooth scrolling with CSS only - no JavaScript required */
html {
  scroll-behavior: smooth;
}
// Smooth scroll to a target element on click (no library required)
document.getElementById('scroll-to-section').addEventListener('click', () => {
  document.getElementById('target-section').scrollIntoView({ behavior: 'smooth' });
});

Common Errors and Debugging

1. Page Width Shifts When a Scrollbar Appears

When a container changes from fitting its content to overflowing, a classic scrollbar appears and typically consumes about 15–17px of layout width on Windows. The content area becomes narrower, text rewraps, and the layout shifts. This is especially noticeable on containers set to full viewport width and on dynamically loaded lists where the scrollbar appears after the initial render.

A direct fix is scrollbar-gutter: stable, which allocates the scrollbar gutter before the bar is shown, so the content width remains stable:

CSS without gutter reservation (layout shifts when scrollbar appears):

.feed {
  overflow: auto;
  /* no scrollbar-gutter: content reflows when the bar appears */
}

CSS with gutter reservation (layout stays stable):

.feed {
  overflow: auto;
  scrollbar-gutter: stable; /* reserves gutter; content width does not change */
}

If the scrollbar should always remain visible regardless of content length, overflow: scroll provides similar stability without requiring scrollbar-gutter, because scrollbar space is permanently reserved.

2. Scrollbar Disappearing with overflow: hidden Unintentionally

If a scrollbar disappears unexpectedly, check whether a parent element uses overflow: hidden. That setting disables overflow scrolling entirely on the element and its children.

Incorrect HTML and CSS:

<div style="overflow: hidden;">
  <div style="overflow: auto;">
    <!-- Content that requires a scrollbar -->
  </div>
</div>

Corrected HTML and CSS:

<div>
  <div style="overflow: auto;">
    <!-- Content that requires a scrollbar -->
  </div>
</div>

3. Double Scrollbars from Nested overflow

Double scrollbars can appear when one scrollable container is nested inside another. This usually happens because overflow: auto (or scroll) set on both the parent and the child. A direct fix is to scroll on a single element, typically the inner content wrapper, and keep the parent at its default overflow behavior:

Symptom: nested overflow on parent and child:

/* Symptom: two scrollbars from nested overflow */
.outer { overflow: auto; }   /* parent scrolls */
.inner { overflow: auto; }   /* child also scrolls: double bars */

Fix: scroll on one element only:

/* Fix: scroll on one element only */
.outer { overflow: visible; }
.inner { overflow: auto; }

Accessibility Considerations

Color Contrast Requirements

WCAG 2.1 Success Criterion 1.4.11 (Non-text Contrast) requires at least a 3:1 contrast ratio between a UI component and adjacent colors. For a scrollbar, the thumb color must achieve at least 3:1 contrast against the track color.

  • Thumb #767676 on track #ffffff: approximately 4.5:1. Passes.
  • Thumb #cccccc on track #ffffff: approximately 1.6:1. Fails.

Apply the WebAIM Contrast Checker to verify scrollbar color pairs before publishing.

Respecting User Scrollbar Preferences

Some users set their operating system to display scrollbars at all times, or have accessibility settings that affect scrollbar rendering. Do not use CSS to override operating-system accessibility preferences.

When hiding a scrollbar with scrollbar-width: none or ::-webkit-scrollbar { display: none }, make sure the element is keyboard accessible: it must be focusable and respond to arrow keys, Page Up, and Page Down so keyboard-only users can navigate the content. Do not hide a scrollbar when it is the element’s only visible navigation cue.

FAQ

1. What is the difference between -webkit-scrollbar and the CSS Scrollbars standardized properties?

The -webkit-scrollbar method relies on vendor-prefixed pseudo-elements supported in Chrome, Safari, and Chromium-based Edge. It allows detailed customization such as border-radius on the thumb and border-based padding around the thumb. The CSS Scrollbars standard (scrollbar-width, scrollbar-color) is the W3C-specified approach, supported in Firefox and newer versions of Chrome and Edge. Both approaches can be used in the same stylesheet using @supports feature queries.

2. Does CSS scrollbar styling work in Firefox?

Yes. Firefox implements scrollbar-width and scrollbar-color from the CSS Scrollbars Level 1 specification starting in version 64. Firefox does not implement the -webkit-scrollbar pseudo-element family. Write Firefox styles using the standardized properties only.

3. How do I hide a scrollbar in CSS without disabling scrolling?

Set overflow: auto on the container, then use ::-webkit-scrollbar { display: none } for Chrome and Safari, and scrollbar-width: none for Firefox. Do not use overflow: hidden because it turns scrolling off instead of only hiding the visible scrollbar.

4. Can I use border-radius on a scrollbar thumb?

Yes, but only with the ::-webkit-scrollbar-thumb pseudo-element. Set border-radius directly on ::-webkit-scrollbar-thumb. This capability is not provided by the standard scrollbar-color property.

5. Does scrollbar styling affect page layout?

By default, scrollbars occupy layout space. The scrollbar-gutter property determines whether that space remains reserved while the scrollbar is hidden. Setting scrollbar-gutter: stable keeps content from reflowing when a scrollbar appears or disappears.

6. Are custom CSS scrollbars accessible?

Custom scrollbar designs need to satisfy WCAG 2.1 color contrast requirements. The thumb color against the track background must have a contrast ratio of at least 3:1 for non-text UI components. Avoid completely removing scrollbars on elements that require them for navigation unless a keyboard-accessible alternative is available.

7. Does -webkit-scrollbar work in Edge?

Yes. Microsoft Edge uses the Chromium engine and implements the full -webkit-scrollbar pseudo-element family. CSS written for Chrome also works in Edge without changes.

8. How do I apply a custom scrollbar to only one element?

Limit the pseudo-element selector to a specific class or ID:

/* Scoped custom scrollbar: applies only to .my-container */
.my-container::-webkit-scrollbar {
  width: 8px;
}
.my-container {
  scrollbar-width: thin;
  scrollbar-color: #555 #f0f0f0;
}

For standardized properties, apply scrollbar-width and scrollbar-color directly on the selector for that element.

Conclusion

This guide explained how to customize scrollbars with the CSS Scrollbars Level 1 standardized properties (scrollbar-width, scrollbar-color, scrollbar-gutter) and the -webkit-scrollbar pseudo-element family. It covered cross-browser compatibility, the @supports feature detection pattern, vertical and horizontal scrollbar examples, the appropriate technique for hiding a scrollbar while preserving scroll functionality, and scoped examples for common interface patterns including chat windows, sidebars, code blocks, and modals.

You can now create scrollbar styles for Chrome, Firefox, Safari, and Edge without unnecessary duplication, use @supports for standards-first CSS while adding a webkit layer for visual details, limit custom scrollbar rules to individual components instead of styling the whole page, and choose color combinations that meet WCAG 2.1 contrast requirements.

For further work with CSS pseudo-elements and layout properties, review A CSS Selector Reference for more detail about the selectors and pseudo-elements used throughout this tutorial, How To Prevent Line Breaks Using CSS for methods that keep content on one line with white-space: nowrap, which is useful with horizontal scrolling containers, and CSS Flexbox for layout methods that work with scrollbar-gutter to prevent content reflow.

Source: digitalocean.com

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: