A View on CSS Viewport Units in 2022 (v*, sv*, lv*, dv*)

A View on CSS Viewport Units in 2022 (v*, sv*, lv*, dv*)When you’ve tested your website on Safari on an IOS device and used vw or vh, I’m sure you already came across this problem. Some Pieces of your content, that appear properly on other devices, are moved out of the screen. They just appear in the right position if the page is viewed on full screen. To solve this problem, 16 new viewport Units were introduced in the CSS Values and Units Level 4 specification. Let’s recapture the old viewport units and move the new ones into view!
Categoriescss viewport responsive web
Date
2022-07-27

What is a Viewport?

In computer graphics, a viewport, as the word already suggests, is the area that is currently viewed. In Browsers, the Viewport is the currently open browser window without the Browser UI itself.

A good example to visualize this is to open your Browsers console and type in

window.outerHeight

window.innerHeight

You will see two different values. The same applies to the width.

css viewport to pixels

The Outer height is the complete window height, including your Browser UI.

The InnerHeight is only the space available to your currently opened page.

This means, If you resize your viewport, these values will change according to it.

To work with the Viewport properly and abstract those length changes out, CSS initially provided us with 4 Viewport Units.

The good old CSS Viewport Units vh, vw, vmin and vmax

  • Viewport Height (vh). Relative to window.innerHeight. 1vh means 1% of window.innerHeight
  • Viewport Width (vw). Relative to window.innerWidth. 1vw means 1% of window.innerWidth
  • Viewport Minimum (vmin). This unit will be relative to the smaller value of innerHeight and innerWidth, in the above example 100vmin would be 929px
  • Viewport Maximum This unit will be relative to the bigger value of innerHeight and innerWidth, in the above example, 100vmax would be 969px since innerWidth is bigger.

The CSS Viewport units are responsive length units, which means that they will change when the Browser window resizes. This gives us developers an easy API to build robust, responsive UI. And then mobile devices came… More on this in the next chapters.

Remember: CSS Viewport Units are relative to the browser Window, and not to their Parent element!

As you can see above, in my case 100vh is equal to 929px. 100vw = 969px. The Big difference here is that my DevTools are open.

What is css viewport

To visualize this even better, here is a screenshot explaining the viewport width and the viewport height.

You can also see, since the dev tools are open on the right, that also the 100vw does not equal the full window width.

This will be the same if your users use fancy browsers like Vivaldi which has a toolbar on the left.

By now CSS viewport units sound pretty handy, and you can do many useful things like providing native experiences within the browser by setting

.nativeCard{
  height: 100vh;
  width: 100vw;
}

This would let our card cover the entire Browser view, providing the look and feel of a real native app inside the browser.

Doing this with two lines of code seems easy, right? Unfortunately, it isn't.

The Problem with the old 4 CSS Viewport Units

You have maybe seen this on some websites. It could happen that elements on some sites are hidden or only partly visible. For example, this article describes such a bug, worth $8 Million.

There was also a bug raised in WebKit. Closed with a funny but informative statement by Benjamin Poulain:

“This is completely intentional. It took quite a bit of work on our part to achieve this effect. :)

The base problem is this: the visible area changes dynamically as you scroll. If we update the CSS viewport height accordingly, we need to update the layout during the scroll. Not only that looks like shit, but doing that at 60 FPS is practically impossible in most pages (60 FPS is the baseline framerate on iOS).

It is hard to show you the "looks like shit" part, but imagine as you scroll, the contents moves and what you want on screen is continuously shifting.

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. Most website using viewport units were looking great most of the time.”

So to make the web not look like shit, the compromise was to make 100vh match the large view size, with the address bar hidden. With that background, totally understandable. But as with every compromise developers make, it introduces problems somewhere else. Somewhere, we don't want to have them.

To our luck, in 2021 w3 introduced additional viewport Units. sv, lv and dv*.**

The new CSS Viewport Units sv, lv and dv*

I first got in touch with the new viewport units when listening to one of my favorite podcasts, Syntax. I heard Wes and Scott talking about the new Viewport Units and immediately thought:

“Finally! This will solve a huge issue for me! Finally, some of our buttons won't be hidden by the Address bar anymore!” And it really worked. In the next sections, I will explain the theory behind it and use it in a simple example using the small and dynamic viewport units to build a nice native UI.

What are the new CSS Viewport units?

  • The “Large Viewport”: lvh / lvw / lvmin / lvmax

    The Large Viewport is basically the same as the “standard” viewport.

    It assumes that the page is opened in full-screen mode with the URL bar hidden.

  • The “Small Viewport”: svh / svw / svmin / svmax

    The Small viewport is assuming that the Address bar is always expanded.

  • The “Dynamic Viewport”: dvh / dvw / dvmin / dvmax

    The Dynamic viewport is the smart guy among those viewports. It will adjust itself automatically when the browser UI changes.

    When the Address bar is hidden, it will have the value of lvh.

    When the Address bar is shown, it will take the value of svh.

To me, the dynamic viewport is the most useful one. Since it will make your UI response to the viewport changes as a user would expect to.

To resize your page according to Browser UI changes, you can simply use:

.body{
    height: 100dvh;
  }

But also the small and the large Viewports have their use cases. One possible use case would be using viewports for font sizing.

By that, you can make your fonts perfectly fit on every screen size, but you don't want to make the font bigger when the user goes into full screen. So you can set the heading, for example to 15 svh, to let it take up 15% of the available space and the test to 10 svh to take 10%.

Since you used svh, it will not resize the text when the user goes into full screen.

How to use the new CSS Viewport Units, an Example

As an example for the viewport units, let's build a dead simple website, where you have some text in the middle of the page, Text at the top and at the bottom.

The Text should always adapt to the user's screen size, and always cover the entire screen.

If nothing should be hidden, scrolling is also forbidden, and the page should work on all browsers. So if the new Units are not supported, the page should still look ok, else it will look amazing.

Let's create the basic HTML for it:

<template>
  <div>
    <v-card class="card">
      <v-container>
        <div class="topText"> This text should be clearly visible at the top!</div>
        <div id="supported"/>
        <div class="title">Dynamic viewport</div>
        <div class="subTitle">Playground to try around with the new css viewport units and see how they will solve our viewport bugs!</div>
        <div class="bottomText"> This text should be clearly visible at the bottom!</div>
      </v-container>
    </v-card>
  </div>
</template>

There is nothing fancy about this, a simple Vue component with vuetify, to also try if this works with CSS frameworks.

Foremost, we want the card to take up the full screen.

.card{
    height: 100dvh;
  }

Next, we want to place our headline right in the center, with the subtitle just below it.

The test should fit nicely onto each screen and also be relative to the viewport size.

So if you make the browser window smaller, also the font should be smaller.

But if you hide the Address bar, it should not get bigger.

.bottomText{
    bottom: 1lvh;
    font-size: 5svmin;
  }

  .topText{
    bottom: 1lvh;
    font-size: 5svmin;
  }

  .title{
    position: absolute;
    top: 45dvh;
    font-size: 8svmin;
  }
  .subTitle{
    position: absolute;
    top: 55dvh;
    font-size: 4svmin;
  }

Here you can see some screenshots of the UI we build in different environments:

Small and Dynamic Viewport on the iPad

With The address bar expanded, dvh = svh

With the address bar hidden, dvh = lvh

Small and Dynamic Viewport on the iPhone

With The address bar expanded, dvh = svh

With the address bar hidden, dvh = lvh

Small and Dynamic Viewport on Windows/ Firefox small

The text is smaller since the whole viewport is smaller.

Small and Dynamic Viewport on the Windows Firefox large

As you can see the text increases and the headline is in the center!

Small and Dynamic Viewport on Chrome:

As you can see, there is currently no browser support. 😞

Browser support for lv sv and dv*

Currently, it is supported by the newest version of Safari and Firefox. There is no support for chrome by now.

You can check the current status on caniuse.

How to use new CSS Viewport Units in cross-browser

As only the newest Browsers are currently supported, how do we make our page not crash when opened on an older one. Or in a Chromium-based one.

You can use the CSS @supports Query.

This handy little guy will execute a part of your CSS only if the passed feature is supported.

So to make your websites also work on browsers that don't support a given feature, write first your CSS for the browsers that don't support it. And then wrap the modern stuff into this query.

So to make this example also work in Chrome, we use a variant with the good old vh. Then we rewrite it for the browsers who already support it inside @supports(height: 100dvh) { .

For our example, this will look like the following:

.card{
  height: 100vh;
  width: 100vw;
  background-color: darkslateblue;
}

.title{
  position: absolute;
  top: 45vh;
  font-size: 8vh;
}
.subTitle{
  position: absolute;
  top: 55vh;
  font-size: 4vh;
}
.bottomText{
  position: absolute;
  bottom: 10px;
  left: 10px;
 }

.topText{
  position: absolute;
  top: 10px;
  left: 10px;
}

#supported::after {
  content: "Dynamic Viewport units are not supported by your Browser!";
  background-color: rgb(187, 39, 40);

  display: block;
  margin: 5px 0;
  padding: 5px;
  border: 1px solid #ccc;
  position: absolute;
  top: 15vh;
}

/* If the browser supports dynamic viewport ->
set the height og the card to 100dvh instead of 100vh
This will keep the texts inside our viewports*/
@supports(height: 100dvh) {
  .card{
    height: 100dvh;
  }

  .bottomText{
    bottom: 1lvh;
    font-size: 5svmin;
  }

  .topText{
    bottom: 1lvh;
    font-size: 5svmin;
  }

  .title{
    position: absolute;
    top: 45dvh;
    font-size: 8svmin;
  }
  .subTitle{
    position: absolute;
    top: 55dvh;
    font-size: 4svmin;
  }

/*  also adapt the info text*/
  #supported::after {
    content: "Dynamic Viewport units are supported by your Browser!";
    background-color: #374b2c;

    display: block;
    margin: 5px 0;
    padding: 5px;
    border: 1px solid #ccc;
    position: absolute;
    top: 15dvh;
  }
}

Conclusion

Seven years after the first bug report, we finally have a clean fix that works everywhere.

Since the dv* implementation is up to the different browsers, I hope that they implement it all similarly to make this unit the unit to go when it comes to positioning elements relative to screens.

Happy positioning,

Alex

Recommended Products for you
recommended for you