Custom Scrollbars
Custom scrollbar styling lets you restyle a browser's scroll track and thumb to match your theme using the standard scrollbar-color and scrollbar-width properties, plus the WebKit ::-webkit-scrollbar pseudo-elements for finer control.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free HTML & CSS course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end you'll style any scrollbar to match your theme — in every browser — without hurting usability.
Think of a scrollbar like the volume slider on a hi-fi. The track is the groove it slides in, and the thumb is the knob you grab. Default scrollbars are the plain plastic knob the factory fitted; a custom scrollbar is the same mechanism with a knob that matches your gear. You're repainting the knob — not changing how it works — so keep it big enough to grab and easy to see.
A scrollbar has two parts you can style: the thumb (the bit you drag) and the track (the groove behind it). There are two ways to style them, and you need both.
The standard properties. scrollbar-width takes auto , thin , or none , and scrollbar-color takes two colours — thumb first, then track . These work in Firefox and modern Chrome (121+). They're simple but you only get colour and a coarse width.
The WebKit pseudo-elements. targets the whole bar, the handle, and the groove. They work in Chrome, Safari and Edge and give you exact pixel widths, border-radius, borders, gradients and :hover states — but Firefox ignores them completely.
The takeaway: write both . The standard properties cover Firefox; the WebKit pseudo-elements add the polish for everyone else.
1. Both APIs Together (Worked Example)
Read every comment, then run it. The same box gets a styled scrollbar in both Firefox and Chrome because it declares the standard properties and the WebKit pseudo-elements together.
2. Smooth Scrolling & Scroll-Snap
scroll-behavior: smooth animates jumps (handy for anchor links). scroll-snap-type makes a scroll container snap each child into place — the classic carousel feel. Note the horizontal bar uses height , not width .
3. Containing the Scroll with overscroll-behavior
When you scroll to the end of a panel, the page behind it normally takes over — that's "scroll chaining". overscroll-behavior: contain stops it, which is exactly what you want for chat windows and modals.
🎯 Your Turn #1 — Standard Properties
Fill in the blanks so the box gets a thin scrollbar with a purple thumb on a light track. This is the Firefox / standards approach — only two lines.
🎯 Your Turn #2 — WebKit Pseudo-Elements
Now do the same job the WebKit way. Decide whether each blank is width / height , thumb / track , and which :state gives hover feedback.
🏆 Mini-Challenge — Dark-Mode Chat Scrollbar
Support is faded now — you get only a comment outline. Build a subtle dark-mode scrollbar on .chat using both APIs, plus overscroll-behavior: contain . Check your work against the expected result in the comment.
💡 Pro tip: always pair the standard properties with the WebKit pseudo-elements so every browser gets a styled bar.
Practice quiz
In the standard scrollbar-color property, which order are the two colors given?
- track then thumb
- background then border
- thumb then track
- Both must be identical
Answer: thumb then track. scrollbar-color takes the thumb color first, then the track color.
Which values are valid for scrollbar-width?
- auto | thin | none
- small | medium | large
- 1px | 2px | 3px
- show | hide
Answer: auto | thin | none. scrollbar-width accepts the keywords auto, thin, or none.
Why won't ::-webkit-scrollbar rules style the scrollbar in Firefox?
- Firefox needs a plugin
- Firefox disables all scrollbars
- You must use !important
- The pseudo-elements are WebKit-only; Firefox uses the standard properties
Answer: The pseudo-elements are WebKit-only; Firefox uses the standard properties. Firefox ignores the WebKit pseudo-elements and styles scrollbars via scrollbar-width/scrollbar-color.
Which pseudo-element styles the draggable handle of a WebKit scrollbar?
- ::-webkit-scrollbar-track
- ::-webkit-scrollbar-thumb
- ::-webkit-scrollbar
- ::-webkit-scrollbar-handle
Answer: ::-webkit-scrollbar-thumb. ::-webkit-scrollbar-thumb styles the handle you drag; -track is the groove behind it.
For a horizontal scrollbar, which property sets its thickness on ::-webkit-scrollbar?
- height
- width
- thickness
- size
Answer: height. A horizontal bar's thickness is its height; vertical bars use width.
What does scroll-behavior: smooth do?
- Changes the scrollbar color
- Hides the scrollbar
- Animates programmatic and anchor-link scrolling instead of jumping
- Snaps elements into place
Answer: Animates programmatic and anchor-link scrolling instead of jumping. scroll-behavior: smooth animates jumps (anchor links, scrollIntoView); it doesn't change appearance.
What does overscroll-behavior: contain prevent?
- The scrollbar from appearing
- Scroll chaining to the page behind a panel
- Smooth scrolling
- Horizontal scrolling
Answer: Scroll chaining to the page behind a panel. overscroll-behavior: contain stops scroll chaining so the page behind a panel doesn't take over.
Which property makes a scroll container snap each child into place?
- scroll-behavior
- overscroll-behavior
- scrollbar-width
- scroll-snap-type
Answer: scroll-snap-type. scroll-snap-type (e.g. x mandatory) makes children snap into place; scroll-snap-align positions each child.
What is required for any scrollbar to appear in the first place?
- A color-scheme declaration
- Content that overflows a fixed-size box (height + overflow)
- A WebKit browser
- The scrollbar-width property
Answer: Content that overflows a fixed-size box (height + overflow). A scrollbar only shows when content overflows a box with a fixed size and overflow: auto/scroll.
Why is hiding the scrollbar on ordinary vertical content an accessibility problem?
- It slows the page down
- It breaks in Chrome
- Users lose the visual cue that there is more content to read
- It disables keyboard scrolling
Answer: Users lose the visual cue that there is more content to read. Hiding the bar (scrollbar-width: none) removes the cue that more content exists below.