CSS Scrollbar Styling
Customize scrollbar appearance, control scroll behavior with snap points and smooth scrolling, and manage overscroll effects — all with CSS.
scrollbar-width
The standard CSS property to control scrollbar thickness. Supported in Firefox and modern Chromium browsers.
.container { scrollbar-width: auto; /* default platform scrollbar */ scrollbar-width: thin; /* thinner scrollbar */ scrollbar-width: none; /* hide scrollbar (still scrollable) */ }
scrollbar-width: none hides the scrollbar but the content remains scrollable via keyboard, touch, and mouse wheel. However, it removes a visual cue, so use sparingly and provide alternative navigation hints.
scrollbar-color
Sets the thumb and track colors with a single property. Takes two color values: thumb first, then track.
.container { /* scrollbar-color: thumb-color track-color */ scrollbar-color: #6366f1 #1e1e2e; scrollbar-width: thin; } /* Auto = use system default colors */ .default { scrollbar-color: auto; } /* Using CSS variables for theming */ .themed { scrollbar-color: var(--scroll-thumb) var(--scroll-track); }
WebKit Scrollbar Pseudo-Elements
Chrome, Safari, Edge, and other WebKit/Blink browsers support detailed scrollbar styling with pseudo-elements. These offer much more control than the standard properties.
/* The entire scrollbar */ .container::-webkit-scrollbar { width: 10px; /* vertical scrollbar width */ height: 10px; /* horizontal scrollbar height */ } /* The scrollbar track (background) */ .container::-webkit-scrollbar-track { background: #1e1e2e; border-radius: 10px; } /* The draggable thumb */ .container::-webkit-scrollbar-thumb { background: #6366f1; border-radius: 10px; border: 2px solid #1e1e2e; /* creates visual gap */ } /* Thumb on hover */ .container::-webkit-scrollbar-thumb:hover { background: #818cf8; } /* Corner where both scrollbars meet */ .container::-webkit-scrollbar-corner { background: #1e1e2e; }
/* Standard (Firefox, modern Chrome 121+) */ .scrollable { scrollbar-width: thin; scrollbar-color: #6366f1 transparent; } /* WebKit fallback (older Chrome, Safari, Edge) */ .scrollable::-webkit-scrollbar { width: 8px; } .scrollable::-webkit-scrollbar-track { background: transparent; } .scrollable::-webkit-scrollbar-thumb { background-color: #6366f1; border-radius: 4px; }
Live Scrollbar Demos
Scroll inside each box to see different scrollbar styles in action.
This container has a thin, minimal scrollbar. The track is transparent and the thumb is a subtle indigo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra.
Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing.
This container has a vibrant pink/purple scrollbar with a tinted track background.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum.
Cras venenatis euismod malesuada. Nullam ac erat ante. Vivamus vel risus ac nisl sodales fermentum.
Suspendisse potenti. Fusce lacinia arcu et nulla. Nulla vitae massa quis enim semper malesuada.
Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat.
Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.
Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus.
This container uses a dark theme scrollbar with subtle gray tones that blend into the dark background.
Perfect for dark mode interfaces where the scrollbar shouldn't draw too much attention.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.
Totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.
Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam.
No visible scrollbar! But you can still scroll with mouse wheel, trackpad, or keyboard.
This is useful for custom scroll UIs, carousels, or sleek sidebar designs.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.
scrollbar-gutter
The scrollbar-gutter property reserves space for the scrollbar even when content doesn't overflow, preventing layout shifts when content grows.
.container { overflow: auto; /* auto = default, space only when scrollbar appears */ scrollbar-gutter: auto; /* stable = always reserve space for scrollbar */ scrollbar-gutter: stable; /* stable both-edges = reserve space on both sides (symmetrical) */ scrollbar-gutter: stable both-edges; }
scrollbar-gutter: stable on containers where content might dynamically grow to require scrolling. This prevents the annoying horizontal shift when the scrollbar appears.
scroll-behavior: smooth
Enables smooth animated scrolling for programmatic scrolls and anchor link navigation.
/* Apply to the scrolling container (usually html) */ html { scroll-behavior: smooth; } /* Or on specific containers */ .scroll-container { overflow-y: auto; scroll-behavior: smooth; } /* Respect user preference for reduced motion */ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
Scroll Snap
CSS Scroll Snap provides precise control over scroll positions, making carousels and paginated scrolling trivially easy without JavaScript.
/* On the scroll container */ .carousel { overflow-x: auto; scroll-snap-type: x mandatory; /* x|y|both mandatory|proximity */ scroll-padding: 0 1rem; /* offset snap position */ } /* On each child item */ .carousel-item { scroll-snap-align: start; /* start | center | end */ scroll-snap-stop: always; /* normal | always (prevents skipping) */ } /* scroll-snap-type keywords: mandatory — always snaps to a snap point after scrolling proximity — snaps only when near a snap point */
.fullpage { height: 100vh; overflow-y: auto; scroll-snap-type: y mandatory; } .fullpage-section { height: 100vh; scroll-snap-align: start; display: flex; align-items: center; justify-content: center; }
overscroll-behavior
Controls what happens when scrolling reaches the boundary of a scroll container. Prevents scroll chaining (the parent scrolling when a child reaches its limit) and the pull-to-refresh gesture.
.modal-body { overflow-y: auto; /* auto = default, scroll chains to parent */ overscroll-behavior: auto; /* contain = prevent scroll chaining, no bounce */ overscroll-behavior: contain; /* none = prevent chaining AND rubber-band/bounce effects */ overscroll-behavior: none; } /* Axis-specific */ .sidebar { overscroll-behavior-y: contain; /* only prevent vertical chaining */ overscroll-behavior-x: auto; /* allow horizontal chaining */ } /* Common use: prevent page behind modal from scrolling */ .modal-content { overflow-y: auto; overscroll-behavior: contain; }
overscroll-behavior: contain prevents the background page from scrolling. This is essential for good modal UX.
Gotchas
scrollbar-width, scrollbar-color) as the primary approach and WebKit pseudo-elements as a fallback for older browsers.
proximity for a less aggressive snap.
element.scrollTo() uses instant scrolling unless you pass { behavior: 'smooth' }. The CSS property affects anchor links and scrollIntoView().
::-webkit-scrollbar pseudo-elements but with some restrictions. Test thoroughly on Safari.
Pro Tips
scroll-padding-top to offset the snap position so content doesn't hide behind the header.
html { scroll-behavior: smooth; scroll-padding-top: 80px; /* height of fixed header */ } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
.container { overflow-y: auto; scrollbar-width: none; } .container:hover { scrollbar-width: thin; scrollbar-color: rgba(99,102,241,0.4) transparent; }
overscroll-behavior: contain to prevent the background from scrolling.