This is a self Q&A
If you've ever tried to use 100vh
in CSS on iOS you will have found that it isn't actually 100vh when the browser chrome is expanded. It's a well documented bug that Apple decided was actually a feature! This is a good read to explain the bug.
So what is the best way to get around this "feature"? Ideally the answer requires no JavaScript (but that seems unlikely), should be clean, not require a bunch of inline styles, and ideally can be opted into in CSS (sometimes you might want the default 100vh).
Set a root CSS var like so in your stylesheet:
// CSS vars
:root {
--real100vh: 100vh;
}
Then in JavaScript, on load (or jQuery ready) and on resize, you want to run this code:
set100vhVar() {
// If less than most tablets, set CSS var to window height.
let value = "100vh"
if (this.winWidth <= 1024) {
value = `${window.innerHeight}px`
}
document.documentElement.style.setProperty("--real100vh", value)
}
Now you can simply use the CSS: height: var(--real100vh);
wherever you want 100vh
to actually be the real 100vh
on mobile, and this will simply work!
It looks better if you also add a transition: height 0.4s ease-in-out;
on the same element, so it doesn't snap when you scroll down on mobile.
The advantage of using a CSS var to do this is that you can override this whenever you like, for example you might want certain breakpoints to be height: 500px
, and this is hard to do if you use an inline style. You can also use this inside calc()
, like height: calc(var(real100vh) - 100px);
which is useful for fixed headers.
If you use Vue/Nuxt, take a look at how we have implemented that here.