100vw and the horizontal overflow you probably didn’t know about
If you use
width: 100vw on a website, there’s a good chance the horizontal scrollbar is visible for many users. Setting an element’s width to
100vw does tell the browser to span the entire viewport width, but what is considered the viewport? Many assume that
width: 100vw is the same as
width: 100%. This is true on a page that doesn’t scroll vertically, but what you might not realize is that the viewport actually includes the scrollbars, too. This isn’t a problem for anyone using a touch device or trackpad, where the scroll indicator overlays the content, but for folks who keep the vertical scrollbar visible, your website is busted.
Luckily, there are two easy ways to avoid this issue along with the humiliation of having a slight horizontal overflow on your website. First, set your OS preferences to always show scrollbars. On macOS, this is in
System Preferences > General > Show scroll bars: Always. Setting this preference might seem extreme, but trust me—it’s like taking the red pill. If you work on a team, these
100vw issues always seem to creep into the codebase. It’s better to be the one who notices first and fixes them, rather than receiving an unexpected looping gif of your prized website, scrolling side-to-side in a way that was never intended.
Setting the OS preference will only help you detect the issue, but to fix it, prioritize using
width: 100% whenever possible. If you catch yourself going to use
width: 100%, then think to yourself, “Oh, I can finally use
width: 100vw, which is pretty much the same as
width: 100%,” stop what you’re doing and remember this post. If
width: 100% is your friend, then
width: 100vw is the kid who only pretends to be your friend, so that he can swim in your pool. (I’ve never had a pool, but I know this kid exists from friends of mine who grew up with pools. Also, I am this kid)
Now, there are rare cases where
document.body.clientWidth. If these are equal, the scrollbar isn’t visible. If these are different, we can subtract the body width from the window width to get the width of the scrollbar:
const scrollbarWidth = window.innerWidth - document.body.clientWidth
We’ll want to perform this both on page load and on resize, in case someone resizes the window vertically and changes the overflow. Then, once we have the scrollbar width, we can assign it as a CSS variable:
From here, we can use
--scrollbarWidth along with our pool friend,
100vw, to create our own viewport variable:
--viewportWidth: calc(100vw - var(--scrollbarWidth));
I still use
100vw here because we don’t want to change this variable often if we don’t need to—especially since it’s a root-level variable that cascades through everything. If I didn’t care about performance, I’d just set
document.body.clientWidth on every resize event and call it a day, but if I truly didn’t care about performance, I probably wouldn’t care about the horizontal overflow either.
Now that you’re equipped to detect this problem and fix it, there’s one last thing you need to do—warn others when you notice horizontal overflow on their website. I specifically didn’t say “if you notice” because, I promise, you will see it everywhere now. Ever since enabling the OS preference to always show scrollbars, I can’t go a day without visiting a website that doesn’t have a horizontal overflow. I wish I could simply ignore it, but I can’t. Maybe someday, we’ll get a viewport unit that doesn’t factor in the scrollbars, but until then, we can only work around the current one.