Why does vw include the scrollbar as part of the viewport?

html { overflow-x: hidden; }

seems to work


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>

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.


I have a different answer, and feel the need to share my frustration

BECAUSE STANDARD-MAKERS ARE STUPID

(committees, in general, always are)

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)

I believe they seriously screwed the pooch on this one.