jonosellier
Posted on October 29, 2021
As of Chromium 114 (June 2023) overlay scrollbar functionality has been removed and therefore this guide is no longer valid. While I understand wanting to conform to a standard, it is unfortunate that this functionality has been removed. Being unable to have a native scrollbar overlay content at the developer's discretion negatively affects the number of ways one can lay content out without UI items jumping around due to scrollbar space requirements changing.
Why even use overlay scrollbars?
Overlay scrollbars are great because they don't affect the width of the content as they appear and disappear. When I discovered this trick, I was specifically looking to solve the problem of content reflow as a page went from <100vh
to >100vh
. The other nice thing with overlay scrollbars is that they can more seamlessly get out of the way when you aren't using them.
Great, how do I do it?
With overflow: overlay
. Seems too easy right? Well it's Chromium only (so Edge, Brave, Chrome) so it can't be your only overflow option. Ideally, you want to wrap it in @supports(overflow: overlay)
with a fallback to auto
or scroll
. This is what it looks like on Windows without any further styling:
You'll notice that the scrollbars literally just go over the content. We'll need to make the scrollbars look a bit better:
- Transparent background so we can see the content it overlays
- Semitransparent scrollbar for the same reason
- Small enough it doesn't get in the way
- Big enough for a mouse to click and grab (because our grandfathers deserve to scroll how they're used to)
So all we have to do is use some scrollbar CSS (which is supported on Chromium too) to make it look a bit better...
Apply the following CSS...
body {
overflow: overlay;
}
*::-webkit-scrollbar {
display: block;
width: 16px;
}
*::-webkit-scrollbar-button {
display: none;
}
*::-webkit-scrollbar-track {
background-color: #00000000;
}
*::-webkit-scrollbar-track-piece {
background-color: #00000000;
}
*::-webkit-scrollbar-thumb {
background-color: #00000040;
border: 1px solid #ffffff40;
border-radius: 24px;
}
...and we get this:
You'll see that the scrollbar is wide enough to grab with a mouse (it's as wide as it is on Windows, 16px
) but a bit too wide as a pure indicator. Those last 2 points are important but seem to be a bit contradictory. We could make the scrollbar get bigger on hover but you need to hit that small target before it gets bigger so you might as well keep it small. Unless we...
(Ab)use shadows to make things more usable
If we set the shadow behind the scrollbar to be some solid inset shadow and a large, transparent border, then we can make the bar itself transparent, so the only thing we see is the inset shadow, that is smaller than the bar itself due to the border.
*::-webkit-scrollbar-thumb {
background-color: #00000000;
border: 5px solid transparent;
border-radius: 24px;
box-shadow: 4px 0px 0px 4px #00000040 inset;
}
*::-webkit-scrollbar-thumb:hover {
background-color: #00000040;
border: 0px solid transparent;
box-shadow: none;
}
The result we get is a bar that looks 6px wide but is really 16px:
And when we hover near the "smaller bar" (within 5px), it looks just the same as before with the same pointer target size since the element itself is not any smaller.
Now if we extract that color to a CSS variable we can go further!
Going even further
We gotta extract that scrollbar color to a variable, let's call it --scrollbar-color
.
Then let's add a few more CSS rules to change this scrollbar color based on whether or not the page/element is focused. This is the new CSS that uses variables and :hover
, etc. selectors:
* {
--scrollbar-color: #00000000;
}
*:hover, *:focus, *:focus-within {
--scrollbar-color: #00000040 !important;
}
*::-webkit-scrollbar-thumb {
background-color: #00000000;
border: 5px solid transparent;
border-radius: 24px;
box-shadow: 4px 0px 0px 4px var(--scrollbar-color) inset;
}
*::-webkit-scrollbar-thumb:hover {
background-color: var(--scrollbar-color);
border: 0px solid transparent;
box-shadow: none;
}
This will make your scrollbar invisible unless you hover over the element (or page).
But what if we want it to look like a mobile phone's indicator that disappears unless we are currently scrolling? Well for that we finally need JavaScript
Finally needing JavaScript
We got pretty far with CSS but it's time to listen for events, scroll events. Below we add a class to body
whenever we are currently scrolling.
var barTimeout;
document.body.onscroll = () => {
if(barTimeout){
clearTimeout(barTimeout); //clear to reset
}
barTimeout = setTimeout(() => {
document.body.classList.remove('scrolling');
}, 500); //0.5s delay
document.body.classList.add('scrolling');
};
Then we adjust our CSS a little to instead change the --scrollbar-color
on body.scrolling
:
body {
--scrollbar-color: #00000000;
}
body.scrolling {
--scrollbar-color: #00000040 !important;
}
Final touches for production
Just make sure to wrap all this CSS in @supports(overflow: overlay)
and so on! It will work in this current state on 79% of browsers we want to target* but we need to support Safari and Firefox still!
* Since we are emulating mobile device scrollbars, we can only look at desktop browsers
The final product
Available on JSFiddle with hover effect here and scroll effect here
Let me know if this helps you out in any way! 🚀
Posted on October 29, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.