100vh in Safari on iOS

When working with Viewport Units there’s this longstanding and extremely annoying bug in Safari on iOS where it does not play nice with the vh unit. Setting a container to 100vh for example will actually result in an element that’s a wee bit too high: MobileSafari ignores parts of its UI when calculating 100vh.


Image by Max Schmitt

🤔 New to Viewport Units? Ahmad Shadeed has got you covered.

As Apple basically gives us the finger on this – stating that it works as intended (which we all can disagree on) – we have to rely on workarounds. In the past I’ve used like Viewport Units Buggyfill or Louis Hoebregts’ CSS Custom Properties Hack to fix this behavior. I was glad to see that Matt Smith recently found a way to have MobileSafari render an element at 100vh using CSS:

As I replied on Twitter: Nice, but I’d rather have MobileSafari fix the vh unit, as using -webkit-fill-available for this will only work to achieving 100vh.

If you want to achieve a perfect 50vh for example, using -webkit-fill-available won’t work as you can’t use -webkit-fill-available in calc(). Above that it won’t work when the targeted element is nested somewhere deep in your DOM tree with one its parents already having a height set.

Come ‘on Safari, stop being the new IE6 …

UPDATE 2020.05.16 Apparently this -webkit-fill-available workaround can negatively impact the Chrome browser:

Given this I guess the recommended workaround right now still remains Louis Hoebregts’ CSS Custom Properties Hack:

.my-element {
  height: 100vh;
  height: calc(var(--vh, 1vh) * 100);
}
const setVh = () => {
  const vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
};

window.addEventListener('load', setVh);
window.addEventListener('resize', setVh);

The ultimate guide to CSS Viewport Units

Ahmad Shadeed has done an extensive write-up on Viewport Units:

In this article, we will learn about CSS viewport units and how to use them, along with some use-cases and solutions for common issues. Let’s start and dig in!

It’s great to see that it’s an all-in-one article, covering use cases such as Viewport Unit Based Typography and Breaking Elements out of Their Containers.

Regarding Mobile Safari (iOS) not properly calculating 100vh the article mentions Louis Hoebregts’ solution which uses CSS Custom Properties to correctly set 1vh. In addition to that I can say I’ve had good results using Viewport Units Buggyfill in the past. A shame Apple still hasn’t fixed this issue.

CSS Viewport Units →

Rendering Sites Fullscreen in Safari on iPhone X / Introducing “User Agent Variables”

What the …?

By default, the new iPhone X in landscape mode will contain sites in the so called “safe area”, resulting in white bars being rendered on either side of the site (src).

The color, white by default, can be tweaked by altering the background-color on the <body> element. Do note that it’s only background-color though: it doesn’t take gradients/background images into account, so you won’t jump very far with this …

Cover it up!

By adding viewport-fit=cover to the viewport meta tag, it’s possible to make the site stretch out beyond the safe area so that it takes up the full width of the device (src)

<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">

Embracing the notch

Whilst the use of viewport-fit=cover indeed stretches out the site, it also has a side effect of “the notch” overlapping with the site’s content.

To cater for this, Apple has proposed the concept of “User Agent Variables”, accessible via the constant() function in CSS.

This function has been renamed to env()

The current list of proposed User Agent Variables is:

  • user-font-size: User’s requested font size
  • user-background-color: User’s requested background color
  • user-foreground-color: User’s requested foreground color
  • safe-area-inset-top: Inset, as a <length> from the top of the viewport to the title-safe content area.
  • safe-area-inset-right: Inset, as a <length> from the right of the viewport to the title-safe content area.
  • safe-area-inset-left: Inset, as a <length> from the left of the viewport to the title-safe content area.
  • safe-area-inset-bottom: Inset, as a <length> from the bottom of the viewport to the title-safe content area.

Using these safe-area-inset-* constants as the padding of a site, overlap of the notch can be prevented.

body {
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}

Additionally, these values can be used for elements that have position: absolute; / position: fixed; applied.

#element {
  position: fixed;

  /* default position */
  top: 0;

  /* “safe” position (iOS 11) */
  top: env(safe-area-inset-top);
}

Phew.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

Making viewport units work properly in Mobile Safari

A typical issue with the well supported Viewport Relative Units (you know: vh, vw, vmin, and vmax) that bothers me a lot is that MobileSafari (Safari on iOS) takes the height of the address bar into account for 100vh.

Take a look at the footer of that first block in the screenshot below: since its container exceeds 100% of the viewport’s height – even though said container is set to be 100vh in height – the date at the bottom bleeds out of the viewport:

viewport-units-buggyfill-without

Viewport Units Buggyfill is a script that fixes that kind of bad browser implementations. With Viewport Units Buggyfill applied, all is fine and dandy:

viewport-units-buggyfill-with

Next to initializing the script on load, on also needs to listen for the resize event in case – for example – the tabs bar get shown/hidden.

import * as viewportUnitsBuggyfill from 'viewport-units-buggyfill';

// …

// Initialize viewportUnitsBuggyfill
viewportUnitsBuggyfill.init();

// Also hook viewportUnitsBuggyfill to resize event (if it was initialized)
if (document.getElementById('patched-viewport')) {
    window.addEventListener('resize', viewportUnitsBuggyfill.refresh, true);
}

Viewport Units Buggyfill →

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

Viewports Visualisation App

PPK:

Instead of the work I was supposed to do I spent about a day and a half on the alpha version of a viewports visualisation app.

It’s already been very useful to me, since figuring out how the viewports actually work is necessary for full understanding. I hope it does the same for you.

It also contains an example of absolute, fixed, and device-fixed position so that you understand the difference.

If you haven’t read up on the Layout Viewport and Visual Viewport, PPK also got you covered with his previously released “A Tale of Two Viewports”.

Viewports Visualisation App →
A Tale of Two Viewports →