css

CSS *vh (dvh, lvh, svh) and *vw units

frehner

Anthony Frehner

Posted on November 1, 2021

CSS *vh (dvh, lvh, svh) and *vw units

Background

This background section is large; here's a handy link to the new unit section 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.

Expand for more info about the initial containing block

The initial containing block (ICB) definition can be found here, 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.


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:

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.

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. And in 2021, that proposal, with feedback and improvements, was accepted in the CSS spec as several new units!

The New CSS Units

The large, small, dynamic, and traditional vh units.

Large Viewport Units

The lvh & lvw units are defined 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 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 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) 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. I guess we'll see where it goes!

💖 💪 🙅 🚩
frehner
Anthony Frehner

Posted on November 1, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related