A while back I worked on a project with an existing seven year old codebase and it was my task to refresh the design of it without a rebuild. This meant that I found a few constraints and one of them was allowing full bleed components (like a newsletter banner) which didn’t exist before.
Luckily, Andy Bell shared how to create a full bleed CSS utility which was what I ended up using. Towards the end of the article, in the section how the “full-bleed” utility works, Andy points out that using that CSS method might allow the possibility of having horizontal scrollbars and suggests using
“overflow-x: hidden” in the
body tag to fix it.
Since this all worked fine and as expected, I overlooked the reason why having to hide the overflow was necessary. Tepy Thai explains why 100vw causes horizontal scrollbar:
When you set an element's width to 100vw, the element's width now will be the whole view width of the browser and the important part is 100vw does not include the vertical scrollbar's width into its calculation at all. Therefore, when there is a vertical scrollbar, the total width will be the sum of the element's width and the vertical scrollbar's width, which causes the overflow on the x-axis and thus the horizontal scrollbar.
And then last week I was presented with a problem. Another developer in this project messaged me and asked if we could delete the
overflow-x: hidden from the
body. They were trying to build a feature that uses
position: sticky and as it turns out, there is a ticket reporting that position sticky inside overflow hidden doesn’t work.
A solution could be to do a refactor and create a full-bleed layout using CSS grid like Joshua Comeau suggests. Due to a variety of constraints a refactor wasn’t possible.
After making the overflow visible, I needed to fix the horizontal scrollbar and that led me to an article from Jonnie Hallman called “100vw and the horizontal overflow you probably didn’t know about”. My solution was based on the one presented in that article.
I’m not feeling 100% confident over my solution - but it works!
Solution (I think)
//remove small horizontal scrollbar when a block is full bleed
var scrollbarWidth = window.innerWidth - document.body.clientWidth;
var halfScrollbarWidth = scrollbarWidth / 2;
margin-left I needed to take into account the possible existence of the scrollbar. After some poking, it looked like half of the width of the scrollbar would fix that.
--viewportWidth: calc(100vw - var(--scrollbarWidth));
/* finalHalfScrollbar: value must be negative */
--finalHalfScrollbar: calc(var(--halfScrollbarWidth) * -1);
width: calc(100vw - 15px);
margin-left: calc(50% - 50vw - var(--finalHalfScrollbar, -7px));
There's a good chance there's a better solution for this but I did learn something regardless and do enjoy when CSS makes me scratch my head!