32

I'm trying to build a website that has lots of boxes that are of equal width and height. For example, I have a page that has two equal size boxes side by side.

The simple solution was to set the width and height to 50vw. This works great until there is a scroll bar. I've googled around for hours and can't understand why on earth vw and vh would include the scrollbars as part of the viewport.

Here's a sample of my issue

body {
    margin: 0;
    padding: 0;
}

.container {
    width: 100vw;
}

.box {
    float: left;
    width: 50vw;
    height: 50vw;
}

.red {
    background-color: red;
}

.green {
    background-color: green;
}

.lotta-content {
    height: 10000px;
}
<div class="container">
    <div class="box red"></div>
    <div class="box green"></div>
</div>
<div class="lotta-content"></div>

Notice the unwanted horizontal scrollbar.

One possible solution would be to use percentages for the widths, but vw for the height, but it won't ever be a perfect box which isn't the worst thing in the world, but still not ideal. Here's a sample:

body {
    margin: 0;
    padding: 0;
}

.container {
    width: 100%;
}

.box {
    float: left;
    width: 50%;
    height: 50vw;
}

.red {
    background-color: red;
}

.green {
    background-color: green;   
}

.lotta-content {
    height: 10000px;   
}
}
<div class="container">
    <div class="box red"></div>
    <div class="box green"></div>
</div>
<div class="lotta-content"></div>

Why does vw/vh include scrollbars as part of the viewport? Is there a better solution than my own? I'm looking for a pure CSS solution. I rather not have JavaScript.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Kevin
  • 651
  • 1
  • 5
  • 10
  • do you have included in head? – Dachi Apr 10 '15 at 01:14
  • @Dachi That won't make a difference. – Josh Crozier Apr 10 '15 at 01:14
  • http://www.w3.org/TR/css3-values/#viewport-relative-lengths says: "When the value of ‘overflow’ on the root element is ‘auto’, any scroll bars are assumed not to exist." `overflow: auto` is the default value on the root element. I think the decision was made to resolve the following issue, an infinite loop: the "dependency cycle" of "the scrollbar's presence or absence depending on the values of viewport units" – mems Jun 26 '15 at 15:54
  • @Dachi [Still good to include tho](https://www.youtube.com/watch?v=s5Huh_IrAv0) – Tristan Forward Apr 30 '21 at 02:39

4 Answers4

39

One simple (simplicistic) workaround is keeping the scrollbar always around and be dealt with it

html,body {margin:0;padding:0}
html{overflow-y:scroll}

(use overflow-x for a layout that uses vh)

Yaakov Ellis
  • 40,752
  • 27
  • 129
  • 174
ZJR
  • 9,308
  • 5
  • 31
  • 38
  • 21
    Unfortunately, making the scroll bar always there still doesn't fix the issue. 100vw whether the scrollbar is visible or not doesn't change the actual ending value of 100vw. Every browser also handles scrollbars differently. Safari and other Mac OS X browsers overlay them, Edge and I believe more recent IE browsers also overlay them. Whereas some will actually take up space in the viewport and move your content around. But thanks for the suggestion and I do share your frustration – Kevin Oct 11 '15 at 01:21
  • 3
    +1 for the frustration. Just discovered this too and it completely breaks my layout when a scrollbar is present. Luckily, my layout was such that I could replace `calc(100vw - 50px)` with `calc(100% - 50px)`, but unfortunately using percentages is a very partial solution compared to the so-far rock-solid `vw` units - not so rock-solid anymore... :-( On a more practical note though, maybe the right thing would be to file a PR or bug report or something? – Gilad Barner Oct 30 '16 at 14:52
  • The content inside the div that has `vh` and `vw` can't be truly centralized. This is so strange. –  Apr 08 '19 at 06:39
  • I have a solution here. It'll include the scrollbar width when you use 100vw, right? so if we can't make it right, then we can remove the scrollbar, make it invisible. like this: https://stackoverflow.com/questions/16670931/hide-scroll-bar-but-while-still-being-able-to-scroll – csdxf Aug 04 '22 at 10:17
  • 5
    Link to meta: https://meta.stackoverflow.com/questions/425340/ – TheMaster Jun 25 '23 at 16:00
11

It would be convenient if viewport units didn't include cause scrollbars but it is the display size (screen) after all. Have look at this solution with a pseudo element though:

http://www.mademyday.de/css-height-equals-width-with-pure-css.html

Makes for a square in your example as well:

https://jsfiddle.net/3z887swo/4/

.box {
    float: left;
    width: 50%;
}

.box::before {
    content: "";
    display: block;
    padding-top: 100%;
}

Edit - if anyone is wondering why this works (vertical padding responding to the original element's width)... that's basically how it's defined in the specification:

The percentage is calculated with respect to the width of the generated box's containing block, even for 'padding-top' and 'padding-bottom'.

http://www.w3.org/TR/CSS2/box.html#padding-properties


After coming across my own answer, I think it needed some refinement. Semantic ambiguity is why I replaced the word "include" with "cause" at the top. Because it's more the fact that vw units only take the viewport size into account - not including any scrollbar and causing overflow and a scrollbar in the other direction when its width is added to 100vw (making the total space that is needed the viewport plus scrollbar width, exceeding the screen).

As with the question here, the best way to handle vw units is likely to avoid them if you can because they just aren't very compatible with desktop browser (that don't have overlaying scrollbars).

I edited out the idea that included a CSS variable, however hopeful it seemed.

Shikkediel
  • 5,195
  • 16
  • 45
  • 77
2

This question is old, and answered well above, so I'm going to focus on obtaining scrollbar width to then be used to calc element widths, as that's why I landed here. Hopefully this will help other Googlers.

A sloppy CSS solution

I started writing the pure CSS solution based on the calculation below but once you start factoring in elements inside variable width containers, especially when they aren't 100% of the visible width, the calc functions start getting convoluted and unreadable.

For anybody interested, this calc on the root element (<html>) (assuming the doc is full width and no wider) will give you the scrollbar width or 0 when no scrollbar is displayed.

calc( 100vw - 100% );

A robust solution

Personally, I wouldn't battle CSS on this one. Use the right tool for the job:

(function get_scrollbar_width() {

    // Get window width including scrollbar.
    const withScrollBar = window.innerWidth;
    
    // Get window width excluding scrollbar.
    const noScrollBar = document.querySelector("html").getBoundingClientRect().width;
    
    // Calc the scrollbar width.
    scrollbarWidth = parseInt((withScrollBar - noScrollBar), 10) + 'px';

    // Update the CSS custom property value.
    let root = document.documentElement;
    root.style.setProperty('--scrollbar', scrollbarWidth);
    
})();
:root {
    --scrollbar: 0px;
}

body {
    overflow: scroll;
}

.demobox {
    display: grid;
    grid: auto / var(--scrollbar) max-content;
    width: calc(10em + var(--scrollbar) );
    margin: 0 auto;
}

.demobox > div {
    background: red;
}

.demobox > p {
    padding: 1em;
    text-align: center;
    width: 10em;
}
<div class="demobox">
    <div></div>
    <p>
        This red grid cell represents the scrollbar width as set
        on the CSS custom property by the JavaScript function.
    </p>
</div>
Jefferson
  • 89
  • 1
  • 8
-2
html { overflow-x: hidden; }

seems to work

Yleaxeman
  • 29
  • 1
  • 1
    Any idea why? Some context? How did you find this solution? Care for a snippet? What about the actual question (regarding `vw`)? – dakab Sep 13 '16 at 11:08
  • 3
    This doesn't fix the issue, it simply hides the horizontal scrollbar. Your content will still be wider than 100% of the viewport. – TheThirdMan Sep 06 '17 at 15:01