Working around the 100vh problem in Mobile Safari

100vh looks like the obvious way to make a section fill the screen. In Mobile Safari, it can instead extend behind the browser controls because the viewport height does not always match the currently visible area.

I originally expected viewport units to replace the JavaScript used for full-height sections. Safari’s changing address bar makes that less straightforward. This was a well-known WebKit issue when this article was written, so here are the workarounds that were available then.

Start with -webkit-fill-available

body {
  min-height: 100vh;
  min-height: -webkit-fill-available;
}

The first declaration remains a fallback. Safari can use the second value to fill the available space instead of the larger viewport.

Fixed device media queries

@media all and (device-width: 768px) and (device-height: 1024px) and (orientation:portrait) {
    .foo {
        height: 1024px;
    }
}

/* iPad with landscape orientation. */
@media all and (device-width: 768px) and (device-height: 1024px) and (orientation:landscape) {
    .foo {
        height: 768px;
    }
}

/* iPhone 5
You can also target devices with aspect ratio. */
@media screen and (device-aspect-ratio: 40/71) {
    .foo {
        height: 500px;
    }
}

This can work for a tightly controlled device target, but I would avoid it on a general website. New screen sizes quickly turn it into a list you have to maintain.

Viewport Units Buggyfill

Viewport Units Buggyfill was another option. It finds viewport units in the page’s styles, resolves them against the current viewport dimensions, and injects the calculated CSS.

It solves more cases, but it also adds JavaScript and more moving parts. I would use it only when the CSS workaround is not enough.

Use window.innerHeight

The direct JavaScript option is to set the height from window.innerHeight and update it when the viewport changes:

window.onresize = function() {
    document.body.style.height = `${window.innerHeight}px`;
}
window.onresize();

This is more reliable than hard-coding devices, although a production version should consider how often resize events fire. For most pages I would try -webkit-fill-available first and keep JavaScript as the fallback when the design truly needs an exact visible height.