CSS *vh (dvh, lvh, svh) and *vw units
Tags: css
Published: 2021-11-01 17:23:21

## Background

This background section is large; here's a [handy link to the new unit section](#the-new-css-units) if you would like to skip ahead.

However, I think it can be useful to understand how we got where we are. Additionally, this information will be useful to understand when talking about the `dvh` and `dvw` units later. 

Note: while I'll focus mainly on the `vh` unit, please know that the `vw` unit had the same issues. It's just easier to talk about one of them.

### History of `vh`

The `vh` unit, as it originally existed, was defined as 

>Equal to 1% of the height of the initial containing block.

{% details Expand for more info about the initial containing block %}

The initial containing block (ICB) definition can be found [here](https://www.w3.org/TR/CSS2/visudet.html#containing-block-details), but I think the first bullet point summarizes it well (emphasis mine):

>The containing block in which the root element lives is a rectangle called the initial containing block. For continuous media, it has **the dimensions of the viewport** and is anchored at the canvas origin;

In other words, it's the same size as the viewport; or the same size as the content of your website that you can see without scrolling.

-----

{% enddetails %}

Which is very useful and good... that is, until mobile browsers started doing some tricks to maximize your phone's screen space. To see these tricks, open any webpage on your phone and scroll around some bit, while paying attention **not** to the _content_ of the webpage but the _browser's UI_ itself.

You may have noticed that the browser's UI _changes size_ as you scroll. And if the browser's UI and viewport changes size, then the question regarding `vh` became: 

>If `vh` is 1% of the initial containing block (ICB), but the ICB changes sizes as a user scrolls, what does `vh` _really equal_?

### First Solution, First Problem

The first and simplest answer to that question was "`vh` changes as the ICB changes." Logically, it would seem like that should be the final answer, too.

However, there's a problem with this solution:

Let's say you have a phone that has `100px` of screen space. When you load up a page, the browser UI takes up `15px` which leaves `85px` left for your website's content. 

However, your browser is Super Cool, so when you scroll down the browser UI only takes up `10px`, which leaves your website content with `90px`. Or, in table form:

|Browser UI State|Browser UI|`100vh` in pixels|
|---|---|---|
|See everything / maximized / first page load / on scroll upward|`15px`|`85px`|
|Small / minimized / on scroll downward|`10px`|`90px`|


On your Fancy Website™, you have 5 `<section style="height: 100vh">` elements on your page. When someone first loads the page, each `<section>` is `85px` tall. But as they begin to scroll down, the browser UI begins to change size which causes those section elements to grow by 5 pixels, to a total of `90px`! 

Now, let's say the user's now at the bottom of the page looking at the 5th section element. They decide they want to scroll up, which has the side effect of changing the browser UI to the maximum size. That causes `vh` to shrink, and your sections are now all `5px` smaller; when you're at the bottom of the page, that's a total of `20px` of difference (4 sections with `5px` each). 

Just by scrolling upwards a tiny bit, the _content_ of your page has jumped upwards 1/5 of your total screen space (100px total / 20px smaller)! That is a _very_ jarring experience, and honestly it sucked.

Imagine if you had used `vh` for things like `font-size`, and how that would look!

### Second Solution, Second Problem

With this problem in mind, in [2015 Safari / Webkit engineers decided to change the behavior of `vh` units](https://bugs.webkit.org/show_bug.cgi?id=141832):

>Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.
>
>From the data we had, using the larger view size was the best compromise.

In other words, a dynamic `vh` unit was not great, so they changed it to be a static unit equal to the size of the viewport _when the browser UI was its smallest_ (and the content / "view size" was the largest).

About a year later [Chrome / Blink engineers agreed and also updated `vh` units to do the same](https://groups.google.com/a/chromium.org/g/blink-dev/c/BK0oHURgmJ4).

Which is where we are now (as of the time of this writing) with `vh`. 

One of the problems with `vh` being the "largest view size" is that anything that is `height: 100vh` is now larger or overflows the screen when you first load a page. It's pretty difficult, using just CSS, to get content to fit the page _exactly_.

And so, in 2019, a [new CSS proposal was born](https://github.com/w3c/csswg-drafts/issues/4329). And in 2021, that proposal, with feedback and improvements, was [accepted in the CSS spec](https://github.com/w3c/csswg-drafts/issues/4329#issuecomment-863677668) as several new units!

## The New CSS Units

The [large](#large-viewport-units), [small](#small-viewport-units), [dynamic](#dynamic-viewport-units), and traditional [vh](#traditional-viewport-units) units.

### Large Viewport Units

The `lvh` & `lvw` units [are defined](https://www.w3.org/TR/2021/WD-css-values-4-20211016/#large-viewport-percentage-units) as:

>The large viewport-percentage units (lv*) are defined with respect to the large viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be retracted. 

In other words, the size when the browser UI is the smallest and the website content is the largest. `lvh` is essentially how the `vh` unit currently (at the time of this writing) acts.

### Small Viewport Units

The `svh` & `svw` units [are defined](https://www.w3.org/TR/2021/WD-css-values-4-20211016/#small-viewport-percentage-units) as:

>The small viewport-percentage units (sv*) are defined with respect to the small viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be expanded. 

Essentially, `svh` gives you units that you can use to fill the screen when the browser UI is at its largest, and the website content is at its smallest.

### Dynamic Viewport Units

The `dvh` & `dvw` units [are defined](https://www.w3.org/TR/2021/WD-css-values-4-20211016/#dynamic-viewport-percentage-units) as (with emphasis mine):

>The dynamic viewport-percentage units (dv*) are defined with respect to the dynamic viewport size: the viewport sized with dynamic consideration of any UA interfaces that are dynamically expanded and retracted. This allows authors to size content such that it can exactly fit within the viewport whether or not such interfaces are present.
>
>The sizes of the dynamic viewport-percentage units are not stable even while the viewport itself is unchanged. **Using these units can cause content to resize e.g. while the user scrolls the page. Depending on usage, this can be disturbing to the user and/or costly in terms of performance.**

While the `dvh` & `dvw` units _may_ sound good on paper, the caveats noted in the definition above (and in the [first problem & solution section above](#first-solution-first-problem)) actually lead me to believe that you should only use them in very rare and specific situations.  

### Traditional Viewport Units

With these new units, where does `vh` and `vw` sit? Interestingly enough, they are currently defined as:

>The UA-default viewport-percentage units (v*) are defined with respect to a UA-defined UA-default viewport size, which for any given document should be equivalent to the large viewport size, small viewport size, or some intermediary size.

Personally, I find this definition to be too vague, and [I have concerns about how this will affect users and developers](https://github.com/w3c/csswg-drafts/issues/6454#issuecomment-880808709). I guess we'll see where it goes!