Fun with Viewport Units

Avatar of Miriam Suzanne
Miriam Suzanne on (Updated on )

Viewport units have been around for several years now, with near-perfect support in the major browsers, but I keep finding new and exciting ways to use them. I thought it would be fun to review the basics, and then round-up some of my favorite use-cases.

What are viewport units?

Four new “viewport-relative” units appeared in the CSS specifications between 2011 and 2015, as part of the W3C’s CSS Values and Units Module Level 3. The new units – vw, vh, vmin, and vmax – work similarly to existing length units like px or em, but represent a percentage of the current browser viewport.

  • Viewport Width (vw) – A percentage of the full viewport width. 10vw will resolve to 10% of the current viewport width, or 48px on a phone that is 480px wide. The difference between % and vw is most similar to the difference between em and rem. A % length is relative to local context (containing element) width, while a vw length is relative to the full width of the browser window.
  • Viewport Height (vh) – A percentage of the full viewport height. 10vh will resolve to 10% of the current viewport height.
  • Viewport Minimum (vmin) – A percentage of the viewport width or height, whichever is smaller10vmin will resolve to 10% of the current viewport width in portrait orientations, and 10% of the viewport height on landscape orientations.
  • Viewport Maximum (vmax) – A percentage of the viewport width or height, whichever is larger10vmax will resolve to 10% of the current viewport height in portrait orientations, and 10% of the viewport width on landscape orientations. Sadly, and strangely, vmax units are not yet available on Internet Explorer or Edge.

While these units are derived from viewport height or width, they can all be used everywhere lengths are accepted – from font-size to positioning, margins, padding, shadows, borders, and so on. Let’s see what we can do!

Responsive Typography

It’s become very popular to use viewport units for responsive typography – establishing font-sizes that grow and shrink depending on the current viewport size. Using simple viewport units for font-size has an interesting (dangerous) effect. As you can see, fonts scale very quickly – adjusting from unreadably small to extra large in a very small range.

This direct scaling is clearly too dramatic for daily use. We need something more subtle, with minimums and maximums, and more control of the growth rate. That’s where calc() becomes useful. We can combine a base size in more steady units (say 16px) with a smaller viewport-relative adjustment (0.5vw), and let the browser do the math: calc(16px + 0.5vw)

By changing the relationship between your base-size and viewport-relative adjustment, you can change how dramatic the growth-rate is. Use higher viewport values on headings, and watch them grow more quickly than the surrounding text. This allows for a more dynamic typographic scale on larger screens, while keeping fonts constrained on a mobile device – no media-queries required. You can also apply this technique to your line-height, allowing you to adjust leading at a different rate than the font-size.

body {
  // font grows 1px for every 100px of viewport width
  font-size: calc(16px + 1vw);
  // leading grows along with font,
  // with an additional 0.1em + 0.5px per 100px of the viewport
  line-height: calc(1.1em + 0.5vw);
}

For me, this is enough complexity. If I need to constrain the top-end for rapid-growth headings, I can do that with one single media-query wherever the text becomes too large:

h1 {
  font-size: calc(1.2em + 3vw);
}

@media (min-width: 50em) {
  h1 {
    font-size: 50px;
  }
}

Suddenly I wish there was a max-font-size property.

Others have developed more complex calculations and Sass mixins to specify the exact text-size ranges at specific media-queries. There are several existing CSS-Tricks articles that explain the technique and provide snippets to help you get started:

I think that’s overkill in most cases, but your milage will absolutely vary.

Full-Height Layouts, Hero Images, and Sticky Footers

There are many variations on full-height (or height-constrained) layouts – from desktop-style interfaces to hero images, spacious designs, and sticky footers. Viewport-units can help with all of these.

In a desktop-style full-height interface, the page is often broken into sections that scroll individually – with elements like headers, footers, and sidebars that remains in place at any size. This is common practice for many web-apps these days, and vh units make it much simpler. Here’s an example using the new CSS Grid syntax:

See the Pen Full-height CSS Grid by Miriam Suzanne (@mirisuzanne) on CodePen.

A single declaration on the body element, height: 100vh, constrains your application to the height of the viewport. Make sure you apply overflow values on internal elements, so your content isn’t cut off. You can also achieve this layout using flexbox or floats.Note that full-height layouts can cause problems on some mobile browsers. There’s a clever fix for iOs Safari, that we use to handle one of the most noticeable edge-cases.

Sticky-footers can be created with a similar technique. Change your body height: 100vh to min-height: 100vh and the footer will stay in place at the bottom of your screen until it’s pushed down by content.

See the Pen Sticky-Footer with CSS Grid by Miriam Suzanne (@mirisuzanne) on CodePen.

Apply vh units to the height, min-height, or max-height of various elements to create full-screen sections, hero images, and more. In the new OddBird redesign, we constrained our hero images with max-height: 55vh so they never push headlines off the page. On my personal website, I went with max-height: 85vh for a more image-dominated look. On other sites, I’ve applied min-height: 90vh to sections.

Here’s an example showing both a max-height heroic kitten, and a min-height section. Combining all these tricks can give you some powerful control around how your content fills a browser window, and responds to different viewports.

Fluid Aspect Ratios

It can also be useful to constrain the height-to-width ratio of an element. This is especially useful for embeded content, like videos. Chris has written about this before. In the good-old-days, we would do that with %-based padding on a container element, and absolute positioning on the inner element. Now we can sometimes use viewport units to achieve that effect without the extra markup.

If we can count on the video being full-screen, we can set our height relative to the full viewport width:

/* full-width * aspect-ratio */
.full-width {
  width: 100vw;
  height: calc(100vw * (9/16));
}

That math doesn’t have to happen in the browser with calc. If you are using a pre-processor like Sass, it will work just as well to do the math there: height: 100vw * (9/16). If you need to constrain the max-width, you can constrain the max-height as well:

/* max-width * aspect-ratio */
.full-width {
  width: 100vw;
  max-width: 30em;
  height: calc(100vw * (9/16));
  max-height: calc(30em * (9/16));
}

Here’s a demonstration showing both options, with CSS custom properties (variables) to make the math more semantic. Play with the numbers to see how things move, keeping the proper ratio at all times:

See the Pen Fluid Ratios with Viewport Units by Miriam Suzanne (@mirisuzanne) on CodePen.

Chris takes this one step farther in his pre-viewport-units article, so we will too. What if we need actual HTML content to scale inside a set ratio – like presentation slides often do?

We can set all our internal fonts and sizes using the same viewport units as the container. In this case I used vmin for everything, so the content would scale with changes in both container height and width:

See the Pen Fluid Slide Ratios with Viewport Units by Miriam Suzanne (@mirisuzanne) on CodePen.

Breaking the Container

For years now, it’s been popular to mix constrained text with full-width backgrounds. Depending on your markup or CMS, that can become difficult. How do you break content outside of a restricted container, so that it fills the viewport exactly?

Again, viewport units can come in handy. This is another trick we’ve used on the new OddBird site, where a static-site generator sometimes limits our control of the markup. It only takes a few lines of code to make this work.

.full-width {
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}

There are more in-depth articles about the technique, both at Cloud Four and here on CSS Tricks.

Getting Weird

Of course, there’s much more you can do with viewport units, if you start experimenting. Check out this pure CSS scroll-indicator (made by someone named Mike) using viewport units on a background image:

See the Pen CSS only scroll indicator by Mike (@MadeByMike) on CodePen.

What else have you seen, or done with viewport units? Get creative, and show us the results!