Safari’s 100vh Problem

Jon Rose
RBI Tech
Published in
4 min readJun 17, 2020

Safari, being an Apple product, tends to “Think Different”. Given Apple’s dominant market share, especially in the US, it’s important that we understand these differences, and know how to work with them. In this blog post, we’re going to tackle the 100vh problem.

100vh

Quick recap. The vh unit, introduced with CSS3, allows you to specify the percent of the total viewport’s height. So 100vh would be 100% of the viewport. It's mobile responsive and has made building layouts much easier.

Navbar Problem

Let's try making a simple homepage with a footer for navigation.

In the main element is our content. We give it a height of 95vh or 95% of the total height. The footer gets the remaining 5% of the viewport. If we load this up on a desktop, it works exactly as expected.

html loaded on desktop browser

Checking Safari on mobile and we see:

Why?

This must be a bug, right? Well, no. This is actually a feature.

When working on Safari, the team worked hard to show the button bar only when necessary. Their goal was to give more space to the website whenever possible, which is why the button bar hides when scrolling. This lead to a tough decision about how the viewport’s height would be handled. Would it:

1. change the height every time the bar hides and shows

or

2. make the viewport height constant, and have the button bar cover part of the viewport

They opted for a constant height to avoid jankiness. This means that our navbar is in fact there, just hidden behind the button bar. When the button bar hides, we can see the navbar really was there.

html loaded on mobile browser with button bar hidden

Navbar Solution

So what do we do? Instead of placing our navbar by using height, we use positioning instead.

Checking it out on desktop and we can see nothing changed:

after our changes, desktop still works

And now on mobile, we finally get what we expect.

after our changes, desktop still works

Content Problem

The navbar example is a bit contrived. Hopefully you wouldn’t use height for positioning regardless of the 100vh issue. A more likely scenario is that your content gets cut off as a result. Let’s consider the following:

Like before, this works as expected on a desktop.

And again, like before, we have an issue on iOS.

Content Solution

So how do we fix this? Well, unfortunately, it’s time to turn to JavaScript 😱

Agreed, using JavaScript to address a CSS issue is less than ideal. The problem is, what we really need for that height is the innerHeight since it changes as the button bar hides and appears.

Don’t fret, the code is simple. When the document content has loaded, we need to set the value of the height for each section to the window’s innerHeight. Additionally, whenever there is a resize event (e.g. the button bar disappears), we need to update the height again;

Which results in:

ios height fixed with javascript

Great, it works, but it’s a bit janky. Well that’s the reality of whats going on here. That jankiness is the height of the element changing. Looking back at the decision the Safari team had to make about handling the viewport height, this is exactly the poor experience they wanted to avoid. Changing the height when the button bar hides and shows results in some unpleasant side-effects.

Conclusion

This wouldn’t be an issue if the button bar didn’t auto-hide, but Apple’s going to Apple, and over “magicify” everything.

Ok, so what do you do? Well, the least satisfying but likely best answer is that it’s up to you. Ideally you don’t find yourself in this situation. It’s likely a less than ideal user experience to have much content intentionally at the bottom of the view. I’m sure you can find additional workarounds (e.g. for React there’s Div100vh), but it’s worth keeping in mind that you are fighting with the browser at this point. A team of very smart engineers at Apple decided that the best route was to leave the viewport height a constant. I’d probably trust their judgment on this.

--

--