Does Safari 15 finally fix viewport height?

Written by Luke Channings on June 11, 2021

The design for Safari 15 has been very controvercial, and has changed significantly since the beta that this article was based on Sadly, one of the casualties of the evolving design was the main thrust of this article: env(safe-area-inset-bottom) is no longer dynamically the height of the address bar in the final release.

TL;DR : No , but if you subtract env(safe-area-inset-bottom) from 100vh you'll get what you want .

Safari 15's UI is a radical departure from the previous version — and from web browsers in general — but does it fix the viewport height problem?

What is the viewport height problem again?

Mobile Safari has had problems related to 100vh not behaving like web developers expect 1 2 pretty much since the beginning. The main crux of the issue is that Mobile Safari's UI Chrome shrinks when you scroll, and expands again when you activate it. That means 100vh ("100% the height of the viewport") can't be a static number.

Let's start by understanding the definition of the vh unit 3 :

vh is defined as Equal to 1% of the height of the initial containing block . — Anthony Frehner

And here's the best explanation of the 100vh issues in Mobile Safari that I've seen so far,

The core issue is that mobile browsers (I’m looking at you, Chrome and Safari) have a “helpful” feature where the address bar is sometimes visible and sometimes hidden, changing the visible size of the viewport. Rather than adjusting the height of 100vh to be the visible portion of the screen as the viewport height changes, these browsers instead have 100vh set to the height of the browser with the address bar hidden. The result is that the bottom portion of the screen will be cut off when the address bar is visible, thus defeating the purpose of 100vh to begin with. — David Chanin , Avoid 100vh On Mobile Web

Let's put this new Safari to the test

I have a simple HTML page based on the example given in David's article. It has a header at the top and a button at the bottom, all wrapped in a 100vh container.

an image showing the aforementioned button hidden below the bottom UI controls in iOS 14's Mobile Safari

Safari's new floating address bar is displayed above our test button, which is more-or-less exactly the same behaviour as iOS 14.

So - Safari 15 does not change the behavour of 100vh 😢.

So what's the solution then?

It makes sense to me that the WebKit team wouldn't change the behaviour of the viewport unit, it's already well defined.

Do you remember when Apple introduced env() and safe-area-inset so that web developers could avoid their content being shown behind the notch 4 ?

Well in Safari 14, safe-area-inset-bottom is 0px whether the UI chrome is active or inactive, which is something that has annoyed me for a while.

safe-area-inset-bottom is 0px when the UI chrome is inactive in Safari 15 on iOS, and then the height of the collapsed chrome minus the height of the expanded chrome when the bar is expanded.

That means that to get a button to float at the bottom of the page, always above the UI Chrome, all you have to do is use calc(100vh - env(safe-area-inset-bottom)) .

Wrapping up

So not only does safe-area-inset-bottom work in Safari 15, it's animated !

I've been hoping that something to remedy the viewport height bug was coming since Jen Simmons (who joined the Safari / WebKit team in June 2020) was asking for feedback regarding viewport height issues.

Hey everyone who’s been frustrated that VH units in CSS don’t do what you need… can you describe your usecase? What layout are you creating? With which layout mechanism? What do you need? Screenshots & sample code appreciated. — Jen Simmons ( @jensimmons ) May 15, 2021
Have a feeling I’m going to be talking about Environment Variables a lot this week. They are really cool & supported! https://developer.mozilla.org/en-US/docs/Web/CSS/env() https://caniuse.com/css-env-function padding-bottom: calc(1rem + env(safe-area-inset-bottom)); -or- height: calc(100vh - env(safe-area-inset-bottom)); — Jen Simmons ( @jensimmons ) June 7, 2021
  • https://bugs.webkit.org/show_bug.cgi?id=141832 ↩
  • https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ ↩
  • https://github.com/w3c/csswg-drafts/issues/4329 ↩
  • https://webkit.org/blog/7929/designing-websites-for-iphone-x/ ↩

The trick to viewport units on mobile

Avatar of Louis Hoebregts

Viewport units have always been controversial and some of that is because of how mobile browsers have made things more complicated by having their own opinions about how to implement them.

Case in point: should the scrollbar be taken into account for the vw unit? What about a site’s navigation or page controls — should those count in the calculation? Then there are physical attributes of the devices themselves (hello, notch !) that can’t be overlooked.

First, a little context

The spec is pretty vague about how viewport units should be calculated. With mobile devices, we’re often concerned with the vertical height, so let’s look specifically at viewport height ( vh ):

vh unit Equal to 1% of the height of the initial containing block.

So yeah, no clear guidance there when it comes to handling device and browser-specific differentiations.

vh was initially calculated by the current viewport of your browser. If you opened your browser and started to load a website, 1vh was equal to 1% of your screen height, minus the browser interface.

But! If you start scrolling, it’s a different story. Once you get past a piece of the browser interface, like the address bar, the vh value would update and the result was an awkward jump in the content.

Safari for iOS was one of the first mobile browsers to update their implementation by choosing to define a fixed value for the vh based on the maximum height of the screen. By doing so, the user would not experience jumps on the page once the address bar went out of view. Chrome’s mobile browser followed suit around a year ago .

As of this writing, there is a ticket to address this in Firefox Android .

While using a fixed value is nice, it also means that you cannot have a full-height element if the address bar is in view. The bottom of your element will be cropped.

height 100vh safari

CSS Custom Properties: The trick to correct sizing

The idea struck me that CSS Custom Properties and a few lines of JavaScript might be the perfect way to get the consistent and correct sizing I needed.

In JavaScript, you can always get the value of the current viewport by using the global variable window.innerHeight . This value takes the browser’s interface into account and is updated when its visibility changes. The trick is to store the viewport value in a CSS variable and apply that to the element instead of the vh unit.

Let’s say our CSS custom variable is --vh for this example. That means we will want to apply it in our CSS like this:

OK, that sets us up. Now let’s get the inner height of the viewport in JavaScript:

We told JavaScript to grab the height of the viewport and then drilled it down into 1/100th of that total so we have a value to assign as our viewport height unit value. Then we politely asked JS to create the CSS variable ( --vh ) at the :root .

As a result, we can now use --vh as our height value like we would any other vh unit, multiply it by 100 and we have the full height we want.

There is another fix for this that has come along more recently. Matt Smith documents it here . The trick is min-height: -webkit-fill-available; on the body as a progressive enhancement over 100vh , which should work on iOS devices.

Whoa, there! One more little detail.

While our work might look done at this point, those of you with an astute eye for detail may have caught that the JavaScript fires but never updates the size of our element when the viewport’s height changes. Go ahead and try resizing the demo above.

We can update the value of --vh by listening to the window resize event. This is handy in case the user rotates the device screen, like from landscape to portrait, or the navigation moves out of view on scroll.

⚠️ Updating the value of --vh will trigger a repaint of the page and the user may experience a jump as a result. Because of this, I’m not advising that this trick should be used for every project or to replace all usage of the vh unit but only when you may need your users to have an exact viewport unit value.

Also, you may want to implement a debounce method for the resize event to avoid triggering to many events while the user is resizing their browser’s window. You can learn more about it with this article: Debouncing and Throttling Explained Through Examples

You can now resize the demo above and notice that the CSS variable is updated accordingly.

While I recently used this technique on a project and it really helped, you should always think twice when replacing the browser’s default behaviors. (For example, this comes up a lot with ::focus .) Also, browsers tend to update very fast these days, so beware that today’s solution may not work tomorrow.

In the meantime, I hope this article helps! 👋

Here’s a proposal for vhc and vwc units that may be a savior in all this.

You should really use some kind of throtteling when listening to the resize event especially if it is triggering a repaint — for example like documented here: https://devdocs.io/dom_events/resize

Thanks for your feedback, I added a small note the debounce technique but I didn’t want to add extra code into the demos to keep them clear ✌️

Haven’t tried that yet but I struggled with that problem for months! Thank you very much will try this to fix my website soon.

Could you elaborate your use case? You need an in-flow full-height element at the top of the page?

I’ve read somewhere (I think, on nngroup.com) that it’s best to make such an element slightly smaller, so that the user knows that they can scroll down. (Apparently, some users will assume that there is no content below.)

That’s exactly the use case where I needed it. The first screen of the website was supposed to be full-height on mobile but we got the bottom cropped (which was the client’s logo). You could also use this trick if you have a modal that should be 100vh and you don’t want your users to loose the bottom part because of the browser’s interface.

I was struggling with this exact issue a month ago and came up with similar solution :D

I wasn’t doing –vh in root but in that element that needed vh unit only and with jQuery as the project was in jQuery. But the concept is the same.

Similar fix width modal overlay, when body tag overflow hidden:

in js: function getScrollbarWidth() { return window.innerWidth – document.documentElement.clientWidth; } document.documentElement.style.setProperty(‘–scrollbar-width’, ${getScrollbarWidth()}px );

in css: body.modal-opened { padding-right: calc(var(–scrollbar-width)); }

Just a side node – probably it’s better to call variable –vh100, as long it is “100vh”, not a single unit.

If you only need full-height elements, yes you could skip the calc part and set the variable to 100% of window.innerHeight. But if you need an element to be 50vh or else, you can use the variable and multiple it like so: height: calc(var(--vh, 1vh) * 50);

A really nice solution.

However there is a problem – if any script execution fails, JS fails to load or loading takes a long time, you’re going to have an unusable site.

Add a .js class to the body and make the calc height apply only when JS has loaded – The 100vh is both the fallback and non-js/slow loading version

There is already a fallback in the CSS in case the JavaScript doesn’t run. In this line: height: calc(var(--vh, 1vh) * 100); there is var(--vh, 1vh) where 1vh is a fallback. This is not really mentioned in the article but CSS Custom Properties can have fallback if the property is not defined. You can read more about this here: https://developer.mozilla.org/en-US/docs/Web/CSS/var You could also add a default value on the root in your CSS.

I use the following code, never had an issue with 100vh not actually occupying the whole height.

This gets rid of the default behaviour. To me it looks like the issue you are having is caused by it.

After that IIjust manually add margins and padding as needed.

Screenshot from Chrome Mobile

I got that working on chrome on my mobile device, but it is not working for Safari :(

Louis, please ignore my first comment, after looking into issue myself I have discovered more than I had hoped for. I always assumed that viewport height would be.. you know viewport height. Not the the mess it actually is.

So I have been researching a bit.. it appears that only solution that is somewhat reliable is the one you write about in your post maybe with some media queries… I am currently looking into it.

Meanwhile I made a little demo which seems to work fine, sort of… Ill try to use orientationchange event listener to handle the change of orientation and manually adjust height of pages which are below first 2 screen heights, because the URL bar will be always hidden at that point.

http://www.patriklegard.com/app

THANK YOU – this issue has irritated me for ages, and it seems obvious now but it actually never occurred to me to solve the problem this way using innerheight.

I wouldn’t recommend using the resize event though since the height of the element is then forced to change as you scroll on mobile(especially evident on safari ios). Meaning if there’s a background image on the element that is set to cover it makes the background position change, and will also affect any absolute positioned things inside that element too.

To avoid this issue I let the script only update my vh var when the resize is substantial enough(or in this case any landscape mode, mostly the desktop users)

I am running into this issue on an aside with a sticky footer. I always want the footer to be visible since it contains the cancel and submit buttons but depending on scrolling it might show correcrly and it might not. Do you have any suggestions on a sticky footer in an aside on a mobile device?

Just in case somebody else runs into this issue, apparently in Chrome, window.innerHeight doesn’t return the correct viewport height if you’re in Device Mode in Dev Tools. I was trying to use this method on a personal website of mine but was stumped when I went into device mode to check how it looks on iOS and the console log showed a different value for innerHeight then the device viewport height. Firefox and Safari showed correct values but Chrome did not.

I found this article which seems to clarify the reason: https://developers.google.com/web/updates/2017/09/visual-viewport-api

I needed to console log window.visualViewport.width if I wanted Chrome to use the visual viewport of the device. But if you’re actually on your mobile device innerHeight works fine, it’s just that when you’re emulating a mobile device on your laptop in Chrome dev tools innerHeight is not going to work as you may expect.

oops I meant window.visualViewport.height

Just nitpicking but might as well swap that let for const .

100VH Viewport Safari Fix using Intrinsic Sizing

About the use of using -webkit-fill-available.

The idea behind -webkit-fill-available – at least at one point – was to allow for an element to intrinsically fit into a particular layout, i.e., fill the available space for that property.

At the moment intrinsic values like this aren’t fully supported by the CSSWG. However, the above problem is specifically in WebKit, which does support -webkit-fill-available.

The issue, and the fix

The orange div, set to height:100vh, goes under the webkit's browser bottom bar.

The black footer, supposedly stuck to the bottom of the viewport, being masked by the browser bottom bar.

Using -webkit-fill-available, the viewport height is calculated as expected.

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

I'm a footer, set to Position:Sticky to the bottom, and I should be seen entirely on webkit browsers on mobile.

  • Web Development

Fixing the iOS Toolbar Overlap Issue with CSS Viewport Units

One of the most exciting aspects of web development in recent years has been the continued growth and improvement of CSS. Flexbox and grid revolutionized how we build webpage layouts. Custom properties (aka, CSS variables) opened up new possibilities for theming. Newer selectors like :has() , :is() , and :where() have lead to more powerful and concise CSS. Container queries are now a reality and looking ahead, native CSS nesting is on its way ( decisions concerning its syntax notwithstanding).

But all of these new and upcoming features means that CSS is becoming increasingly complicated. Inevitably, then, some new features might fall through the cracks and get overlooked. (Which is why I’m of the opinion that “full-time CSS engineer” ought to be a thing, but that’s a topic for another post.) Speaking personally, I’m still pretty ignorant concerning the benefits of newer color formats like hwb() and lch() . Which brings me to viewport units.

Put simply, viewport units allow you to size a page’s elements relative to the size of the browser’s viewport , which contains everything that is currently visible on a webpage. (The viewport is basically the browser window minus any UI elements like the navigation and search bar.)

Consider this very simple example:

The vh stands for “viewport height,” so an element set to 100vh will be 100% of the viewport’s height. If that element’s height is set to 50vh , then it’ll be 50% of the viewport’s height, and so on. The 100vh is often used when you want an element to fill up the entire browser window. For instance, I use this technique on special features (like my recent David Zindell post ) to make their headers fill up the entire viewport with a splashy image. This gives them some extra visual oomph that sets them apart from my “normal” posts.

Not All Viewports Are the Same

This approach works well except for one pretty prominent scenario. If you’re viewing such a layout in Safari on an iOS device, that 100vh element fills up the viewport, but its bottom portion is then covered by a toolbar that includes the next/previous navigation and other controls. (See Figure A.)

Note: Although I’m focusing on iOS Safari, this issue also occurs in iOS Chrome. It doesn’t occur in other iOS browsers like Brave, DuckDuckGo, Firefox, and Opera. (More on that in a moment.) I haven’t tested this in any Android browsers.

In other words, Safari doesn’t seem to take its own UI into consideration when drawing its viewport. Thus, a 100vh element doesn’t behave the way it seems like it should, i.e., filling up the space between the URL bar and the bottom toolbar. (Remember that a browser viewport is the browser window minus any UI elements.)

There are, of course, reasons for why Apple opted for this approach. And reading the developer’s explanation  — the viewport’s height changes dynamically because any toolbars disappear or minimize when you scroll — they seem perfectly valid. But that doesn’t mean I liked how it looked. It was hard to believe that this was still an issue the Year of Our Lord 2023.

Various Google searches returned different solutions, including some JavaScript-based workarounds . Using JavaScript to fix visual layout issues, however, always feels hack-y to me. Call me old-fashioned, but I like to keep my CSS and JavaScript nice and separate, and reserved for those things that they do best (e.g., CSS for layout, JavaScript for interactivity).

That aforelinked article also pointed me to Matt Smith’s article about -webkit-fill-available , which seemed promising at first. Unfortunately, it wasn’t applicable to my situation. I didn’t want the post header to simply fill up the entire available space because I also needed to take into account the height of my site’s header, which contains the logo, nav, and search.

Here’s what my original CSS looked like:

The site header is 6 rems high, so I use the calc function to subtract that from the 100vh to dynamically calculate the post header’s new height. But, as pointed out before, iOS doesn’t respond to 100vh the way you might think it would. What I really needed was a new type of CSS unit — and fortunately, I found it.

New Viewport Units

Back in November, Google’s Web.dev blog covered three new viewport units: the “large,” “small,” and “dynamic” viewport units . These units were created specifically to work with viewports whose size might change due to dynamic toolbars —  which was the exact problem I was facing .

  • The “large” viewport units assume that any dynamic toolbars (e.g., Safari’s bottom bar) are retracted and hidden , and calculate the viewport’s size accordingly. (This is akin to Safari’s aforementioned default behavior.)
  • The “small” viewport units assume that any dynamic toolbars are expanded and visible , and calculates the viewport’s size accordingly.
  • The “dynamic” viewport units sit in-between the “large” and “small” units, and react automatically to the dynamic toolbar’s behavior.

At first glance, a “dynamic” viewport unit seemed like the solution. After all, who doesn’t like a web design that automatically responds, all on its own, to a given situation? With that thought in mind, I updated my CSS:

In addition to the original selector, I added a feature query via @supports that basically says if the browser recognizes and supports the height: 100dvh declaration, then run the following CSS. (This is an example of progressive enhancement , i.e., starting with the basics and then adding on more advanced code that modern browsers will recognize.) That CSS is virtually identical to my original CSS, except I’m now using 100dvh instead of 100vh . (The dvh stands for “dynamic viewport height.”)

The first time I loaded the page, the problem seemed to be solved: the post header now filled up the space between Safari’s toolbars without anything cut off or hidden. But then I scrolled a little bit.

When you scroll down in iOS, the browser’s toolbars disappear or reduce in size, thus increasing the height of the browser’s viewport. Conversely, scrolling back to the top causes the toolbars to reappear or return to their original size, thus decreasing the viewport’s height. This behavior caused some distracting (IMO) changes to the post header: the background image expanded while the text shifted down in response to the additional height.

Interestingly, this “dynamic” approach is the behavior employed by the iOS versions of Brave, DuckDuckGo, Firefox, and Opera. In other words, toolbar overlap appears to be a non-issue for them, at least as far as Opus is concerned.

So after giving it some more thought, I replaced 100dvh with 100svh  — i.e., the “small” viewport height — which assumes that any toolbars are always expanded.

Here’s my final code:

You can see the results — that is, the entire post header — in Figure B. Upon scrolling, the post header doesn’t take advantage of the increased viewport height, so it’s not a truly “full-height” element. However, it doesn’t have any weird shifting, either, but looks the same all the time. And I always prefer such stability in my web designs.

For what it’s worth, Firefox, Brave, et al . ignore the 100svh setting altogether, and instead, always stick with the “dynamic” handling of the viewport and post header heights. That’s a little frustrating, but since they represent a relatively minuscule amount of Opus ’ overall traffic, I’m not going to sweat it.

Final Thoughts

Along with the aforementioned color formats, viewport units are one of those aspects of CSS that has always felt rather abstract to me. (Then again, I still have trouble wrapping my mind around how srcset works, and that’s used all the time for responsive images.) The problems they seek to address have often seemed rather niche to me, compared to the issues that I’m trying to solve 95% of the time.

Of course, now I have to eat a little crow because I found myself in just such a “niche” situation. Which is to say, I’m glad that really smart people have spent time thinking through these situations, rarefied as they might seem, to find and propose potential solutions.

I’m also glad that browser makers are quick to implement them; browser support for these new viewport units is pretty good, with Opera being the only major holdout. (Which means that I’ll probably remove the @supports feature query in the not-too-distant future and use the 100svh as the default CSS.)

Finally, while Safari’s behavior was initially frustrating, I do believe they made the better choice concerning how to handle dynamic toolbars and viewport heights now that I’ve seen how Firefox et al . handle them. I’d rather have part of the design covered up by default (but fixable, if needed, with the right CSS) then see the page rearrange itself as you scroll. The latter behavior is unexpected and thus distracting, two things that can create a poorer user experience — which is something I try to avoid in every aspect of my designs.

DEV Community

DEV Community

Leonid Fenko

Posted on Jul 13, 2021

Stretching body to full viewport height: the missing way

Suppose you're making a sticky footer or centering some content relative to the viewport. You want to stretch the body element to the full height of the browser viewport while also letting it grow even further to match its content. This task was surely solved a bazillion times, and it should be as easy as pie. Right? Right?

The state-of-the-art way

Sure! Applying min-height: 100vh to the body element should do the trick. Here, 100vh means that the initial body height will take 100% of the viewport height, whereas the use of min-height instead of height will let the body element grow even more if necessary. Isn't it exactly what we need?

Well... Almost. If we open such a page in a typical mobile browser (such as iOS Safari or Android Chrome ), it will be scrollable regardless of the size of its content. Even if the page has no content at all, its bottom will still disappear beneath the bottom UI panel of the browser!

The reason for this is fairly simple. UI elements in these browsers shrink after the scroll, providing additional space for the actual content. A height of 100vh corresponds to the maximum possible viewport height. Since the initial viewport height is smaller, the body element with a min-height of 100vh initially exceeds the viewport height regardless of its content.

Mobile browser scroll demo

The known fix for this issue looks like this:

This solution has a minor glitch in Chrome: when the browser height increases, the body height stays the same, getting out of sync with the viewport height. Aside from that, this approach solves the issue.

However, we now have to fix the html height. If that's the case, shouldn't we use an older, more robust solution?

The old-school way

Since we couldn't avoid fixing the html height, let's try the good old way that involves passing a height of 100% from the html element.

Let's apply min-height: 100% to the body element, where 100% is the full height of its parent (namely, html ). A percentage height on a child requires its parent to have a fixed height, so we have to apply height: 100% to the html element, thereby fixing its height to the full viewport height.

Since the percentage height of the html element in mobile browsers is calculated relative to the minimal viewport height, the above-mentioned scroll issue doesn't bug us anymore!

This solution is not as pretty as the 100vh one, but it's been used since time immemorial, and it will work, that's for sure!

Well... Not quite. Apparently, the gradient applied to such a body element will be cut at the html height (in other words, at the viewport height, or, to be more precise, at the minimal viewport height).

It happens because of the fixed html height, and it doesn't matter whether it's height: 100% or height: -webkit-fill-available .

Broken gradient demo

Of course, this can be "fixed" by applying the gradient to the body content, but that's just not right . The page background should be applied to the body element, and the html element should stretch to its content. Can we achieve that?

The missing way

I suggest another way of stretching the body element to the full viewport height without the above-mentioned issues. The core idea is to use flexbox, which enables a child element to stretch even to a parent with non-fixed dimensions while retaining the ability to grow further.

First, we apply min-height: 100% to the html element to stretch it to the full minimal viewport height. Then we use display: flex and flex-direction: column to turn it into a flex-container with a vertical main axis. Finally, we apply flex-grow: 1 to the body element, thereby stretching it to the html height.

The align-self property of the body element implicitly has the stretch value, so the body width already matches the html width.

Now both html and body can stretch to their content, and, since we're using the percentage height, there are no issues with mobile browsers whatsoever. Neat!

It should be obvious that the flexbox-based solution works for any depth. It can easily be used in cases where the content is being rendered to an element inside the body , and not the body element itself. It's a typical scenario with React or Vue , for example.

As you might've noticed, the direction of the main axis of the flex-container shouldn't matter. I just think that the vertical axis is more elegant in this case, and I didn't really test the other variant. I don't see how it can possibly break, but who knows.

The flexbox-based solution doesn't work in IE. Not at all. But you don't support it anyway, do you?

Top comments (5)

pic

Templates let you quickly answer FAQs or store snippets for re-use.

peuf54 profile image

  • Joined May 6, 2023

Hi, Thanks for this article ! Very helpful for me =)

I just have a quick question. In the last solution, why did you apply a flex-grow: 1 to the body ? I thought that flex-grow were used only when we have more than 1 child.

fenok profile image

You're welcome!

We need flex-grow: 1 even though body is the only (visible) child of html , because without it body won't stretch to html (because the default is flex-grow: 0 , which means "don't stretch this flex item across the main axis").

adambaney profile image

Thank you, Leonid! This is just what I was needing!

tigrank profile image

  • Joined Dec 18, 2021

But you don't support it anyway, do you?

Definitely not, lol. Thanks for the idea.

jonnydungeons profile image

  • Joined Feb 6, 2024

Why would you ever apply flex properties to html and body? I do not believe this is correct.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink .

Hide child comments as well

For further actions, you may consider blocking this person and/or reporting abuse

muhammadsaim profile image

Setting Up Your GoLang Environment

Muhammad Saim - Jun 7

joeskills profile image

Simplest guide to Next.js APIs

Code With Joseph - Jun 7

adevnadia profile image

I tried React Compiler today, and guess what... 😉

Nadia Makarevich - Jun 10

andylarkin677 profile image

Development and Futures Trading: A Dynamic Duo in the Crypto World

Andy Larkin - May 24

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Handling iOS Safari toolbar for full height web content

Handling iOS Safari toolbar for full height web content

Using the dvh unit for content that adapts to the viewport height.

Sabhya Verma's photo

I recently ran into an issue where I had some content to display that spans the full height of the device and the code was using height: 100vh to achieve the same. When testing on iOS Safari, you'll see there's an issue with this:

100vh on iOS Safari adds a scroll bar and cuts off bottom content

As seen in the above screenshots from Safari on an iPhone, using 100vh introduces a scrollbar when the Safari toolbar is visible and cuts off the bottom content.

This is because the vh unit computes the viewport size without factoring in the toolbar height. Thus, the user either needs to scroll down for the address bar to automatically disappear or manually hide it for all content to be displayed.

Unfortunately, there's no way to programmatically hide the toolbar on iOS Safari using JavaScript at the time of writing. However, there is a solution for the content to adapt with the toolbar using the dvh unit.

The dvh unit stands for dynamic viewport height and enjoys support from all the latest browsers . This unit factors in the viewport that's truly visible to users.

In case, you want to work with earlier versions of browsers that don't support dvh , you can make use of the @supports CSS rule .

Here's an example of how you'd set up a class called full-viewport-height that adapts according to the browser viewport using the dvh unit but falls back to vh if the browser doesn't support it:

Using this full-viewport-height on our container now, the result looks as expected:

100dvh on iphone safari adapts to full viewport height

The content using 100dvh now spans the whole height of the web page even if the Safari toolbar is visible and on scrolling down or hiding it, the content adapts to fill the whole page making sure your content always covers the viewport as expected.

You can find the code sandbox for the above examples at vh-sandbox and dvh-sandbox .

A rather geeky/technical weblog, est. 2001, by Bramus

100vh in Safari on iOS

Update 2021.07.08: There are new viewport units on the way that will finally solve this issue. 100dvh is the one you’re looking for.

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 tall: MobileSafari ignores parts of its UI when calculating 100vh .

height 100vh safari

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

Apple/WebKit’s stance is that it works as intended , although it’s not what I (and many other developers) expect. As a result we have to rely on workarounds.

In the past I’ve used 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:

🔥 TIL a #CSS trick to handle that annoying mobile viewport bug with `100vh` in WebKit (iOS Safari)! #WebDev #ProTip — Matt Smith (@AllThingsSmitty) April 25, 2020

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:

Ugh, while this works on iOS Safari and in-app browsers like the one in Google Hangouts, it breaks in Chrome, since Chrome honors `-webkit-fill-available` (and consequently doesn't ignore it). Demo: https://t.co/Rx0VSoxe0e . pic.twitter.com/z3MKEcgUAz — Thomas Steiner (@tomayac) April 29, 2020

Given this it’s recommended to selectively ship -webkit-fill-available to only Safari using a @supports rule that tests for -webkit-touch-callout support:

Alternatively you can still use Louis Hoebregts’ CSS Custom Properties Hack , which uses JavaScript:

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997) , he fell in love with the web and has been tinkering with it ever since (more …) View more posts

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

' src=

  • Pingback: Failing function detection - Abu Sayed
  • Pingback: CSS in 2022 | WIT블로그

Leave a comment

Cancel reply.

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.

This site uses Akismet to reduce spam. Learn how your comment data is processed .

CSS fix for 100vh in mobile WebKit

Not long ago there was some buzz around how WebKit handles 100vh in CSS, essentially ignoring the bottom edge of the browser viewport. Some have suggested not using 100vh , others have come up with different alternatives to work around the problem. In fact, this issue goes further back a few years when Nicolas Hoizey filed a bug with WebKit on the subject (the short of it: WebKit says this is “intentional” 🧐 ).

The other day I was doing some work with a basic flexbox layout – header, main, sticky footer – the kind we’ve all seen and used many times before:

I began running some browser tests on my iPhone, and that’s when I noticed that my sticky footer wasn’t looking so sticky:

iPhone screen showing sticky footer below Safari browser's menu bar.

The footer was hiding below Safari’s menu bar. This is the 100vh bug (feature?) that Nicolas originally uncovered and reported. I did a little sleuthing – hoping that maybe by now a non-hacky fix had been found – and that’s when I stumbled upon my own solution (btw, it’s totally hacky):

🔥 TIL a #CSS trick to handle that annoying mobile viewport bug with `100vh` in WebKit (iOS Safari)! #WebDev #ProTip pic.twitter.com/lefD0Klqzd — Matt Smith (@AllThingsSmitty) April 25, 2020

Using -webkit-fill-available

The idea behind -webkit-fill-available – at least at one point – was to allow for an element to intrinsically fit into a particular layout, i.e., fill the available space for that property. At the moment intrinsic values like this aren’t fully supported by the CSSWG.

However, the above problem is specifically in WebKit, which does support -webkit-fill-available . So with that in mind, I added it to my ruleset with 100vh as the fallback for all other browsers.

And now the sticky footer is right where I want it to be in mobile Safari!

iPhone screen showing sticky footer at the bottom of the viewport above Safari browser's menu bar

Does this really work?

The jury seems to be out on this. I’ve had no problems with any of the tests I’ve run and I’m using this method in production right now. But I did receive a number of responses to my tweet pointing to other possible problems with using this (e.g., the effect on rotating devices, Chrome not completely ignoring the property, etc.).

Will -webkit-fill-available work in every scenario? Probably not, cuz let’s be honest: this is the web, and it can be damn hard to build. But, if you’re having a problem with 100vh in WebKit and you’re looking for a CSS alternative, you might want to try this.

See the Pen CSS Fix for 100vh Mobile ViewPort Bug by Matt Smith ( @AllThingsSmitty ) on CodePen .

Navigation Menu

Search code, repositories, users, issues, pull requests..., provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications You must be signed in to change notification settings

Add -webkit-fill-available for 100vh classes to fix iOS bottom navbar #4515

@Mathias-Syversen

Mathias-Syversen May 31, 2021

Beta Was this translation helpful? Give feedback.

Replies: 11 comments · 10 replies

{{editor}}'s edit, alvis feb 4, 2022, adamwathan feb 5, 2022 maintainer.

@ghost

ghost Apr 13, 2022

@longfellowone

longfellowone Apr 29, 2022

Spencerjsmall nov 19, 2022, mcgrealife nov 19, 2022.

@marc0u

marc0u Apr 25, 2023

@dinesh-hills

dinesh-hills Jun 26, 2023

@jacobbinnie

jacobbinnie Aug 6, 2023

@dokgu

dokgu Aug 19, 2023

Karelric dec 5, 2022, shivekkhurana feb 18, 2023, kanchansapkota27 apr 12, 2023, ms2d apr 19, 2023.

@woutersamaey

woutersamaey Jul 13, 2023

@LeeSangMin12

LeeSangMin12 Jan 10, 2024

Gaborvecsei aug 11, 2023, ianstreator oct 24, 2023.

@alvoscares

alvoscares Jun 6, 2024

Rortan134 jun 7, 2024.

@agileago

agileago Jun 9, 2024

@Mathias-Syversen

  • Numbered list
  • Unordered list
  • Attach files

Select a reply

On Safari 16.6 im getting strange colors when using mix-blend-mode: difference

On Safari 16.6 I'm getting strange colors when using mix-blend-mode: difference

Safari Screenshot: ( https://i.sstatic.net/cWYEDfug.png )

On Firefox, Chrome and mobile Safari it looks as expected: ( https://i.sstatic.net/3K00yA2l.png )

iMac (2017 – 2020)

Posted on Jun 11, 2024 3:48 AM

Loading page content

Page content loaded

There are no replies.

IMAGES

  1. 100vh issue in Safari (Fix for Viewport Height on Mobile devices)

    height 100vh safari

  2. Corriger le problème de hauteur 100% (100vh) sur mobile

    height 100vh safari

  3. CSSのみ:height 100vhとiPhone safariメニューバーの問題は-webkit-fill-availableで解決

    height 100vh safari

  4. CSSのみ:height 100vhとiPhone safariメニューバーの問題は-webkit-fill-availableで解決

    height 100vh safari

  5. [iOS]height:100vhの要素がSafariのツールバーに被る場合

    height 100vh safari

  6. CSSのみ:height 100vhとiPhone safariメニューバーの問題は-webkit-fill-availableで解決

    height 100vh safari

VIDEO

  1. Fixing 100vh in Webflow

  2. AcKcHuLLy 100vh != 100vh

  3. ZX60-100VH+ DataSheet --- ingketech.net

  4. Не используй 100vh! Не попадись на эту багу ;)

  5. Обзор Highscreen Power Five Max

  6. Обзор на F-100 Super Sabre в War Thunder

COMMENTS

  1. height

    The provided article mentions a clean css solution (no JS code needed) which fixes the vh issue for mobile webkit browsers if you want to fill the complete available height (i.e. you use 100vh):.my-element { height: 100vh; /* for non-webkit browsers */ height: -webkit-fill-available; }

  2. 100vh problem with iOS Safari

    The problem you have been receiving after adding the height: 100vh to mobile resolutions. It happens due to the calculation method which Safari and Chrome are using. Mobile devices calc browser viewport as ( top bar + document + bottom bar) = 100vh. I had a hard time with 100vh when the page have to have a section filled the whole screen.

  3. CSS3 100vh not constant in mobile browser

    This is a well know issue (at least in safari mobile), which is intentional, as it prevents other problems. Benjamin Poulain replied to a webkit bug: This is completely intentional. It took quite a bit of work on our part to achieve this effect. ... html { height: 100vh; } body { top: 0; left: 0; right: 0; bottom: 0; width: 100vw; } /* this is ...

  4. Does Safari 15 finally fix viewport height? · Luke Channings

    That means 100vh ("100% the height of the viewport") can't be a static number. Let's start by understanding the definition of the vh unit 3: vh is defined as Equal to 1% of the height of the initial containing block. — Anthony Frehner. And here's the best explanation of the 100vh issues in Mobile Safari that I've seen so far,

  5. CSS fix for 100vh in mobile WebKit

    On the right, the -webkit-fill-available property is being used rather than viewport units to fix the problem. And a solution of sorts: body { min-height: 100vh; min-height: -webkit-fill-available; } html { height: -webkit-fill-available; } The above was updated to make sure the html element was being used, as we were told Chrome is updating ...

  6. The trick to viewport units on mobile

    Safari for iOS was one of the first mobile browsers to update their implementation by choosing to define a fixed value for the vh based on the maximum height of the screen. By doing so, the user would not experience jumps on the page once the address bar went out of view. ... .my-element { height: 100vh; /* Fallback for browsers that do not ...

  7. height: 100% !== 100vh [3 solutions]

    On mobile 100vh !== 100%. This creates weird issues with mobile viewport heights like this: Now this is an issue and indeed a very frustrating one, but we'll discuss a couple of solutions one by one. 1. Use 100% instead of 100vh - "DOM tree nightmare". Now the quickest, and most CSS way is to use 100% in your page for the whole DOM tree ...

  8. 100VH Viewport Safari Fix

    The issue, and the fix. The orange div, set to height:100vh, goes under the webkit's browser bottom bar. The black footer, supposedly stuck to the bottom of the viewport, being masked by the browser bottom bar. Using -webkit-fill-available, the viewport height is calculated as expected. Check the code contained in the HTML Embed Component below.

  9. Safari's 100vh Problem

    Safari, being an Apple product, tends to "Think Different". ... The vh unit, introduced with CSS3, allows you to specify the percent of the total viewport's height. So 100vh would be 100% of ...

  10. Fixing the iOS Toolbar Overlap Issue with CSS Viewport Units

    header { height: 100vh; } The vh stands for "viewport height," so an element set to 100vh will be 100% of the viewport's height. ... Figure A: Safari's bottom bar overlaps the very bottom of the post header, covering up the rest of the text. This approach works well except for one pretty prominent scenario.

  11. 100vh issue in Safari (Fix for Viewport Height on Mobile devices)

    In this video from Webwise, you will learn how to solve the 100vh issue in Safari, where blocks can extend beyond the viewport height. The video will take yo...

  12. Stretching body to full viewport height: the missing way

    Applying min-height: 100vh to the body element should do the trick. Here, ... If we open such a page in a typical mobile browser (such as iOS Safari or Android Chrome), it will be scrollable regardless of the size of its content. Even if the page has no content at all, its bottom will still disappear beneath the bottom UI panel of the browser!

  13. Handling iOS Safari toolbar for full height web content

    When testing on iOS Safari, you'll see there's an issue with this: As seen in the above screenshots from Safari on an iPhone, using 100vh introduces a scrollbar when the Safari toolbar is visible and cuts off the bottom content. This is because the vh unit computes the viewport size without factoring in the toolbar height. Thus, the user either ...

  14. 100vh in Safari on iOS

    100vh. in Safari on iOS. May 6, 2020 https://brm.us/100vh 2 Comments 100vh. Update 2021.07.08: There are new viewport units on the way that will finally solve this issue. 100dvh is the one you're looking for. When working with Viewport Units there's this longstanding and extremely annoying bug in Safari on iOS where it does not play nice ...

  15. CSS fix for 100vh in mobile WebKit

    However, the above problem is specifically in WebKit, which does support -webkit-fill-available. So with that in mind, I added it to my ruleset with 100vh as the fallback for all other browsers. min-height: 100vh; /* mobile viewport bug fix */. min-height: -webkit-fill-available; height: -webkit-fill-available;

  16. Safari mobile does not respect height: 100vh : r/css

    Many web developers struggle with the problem of Safari mobile not respecting height: 100vh, which causes unwanted overflow and layout issues. In this Reddit thread, you can find some possible solutions and explanations for this issue, as well as join the discussion with other CSS enthusiasts.

  17. Add -webkit-fill-available for 100vh classes to fix iOS bottom navbar

    On iphone setting an element to height 100vh will actually make it larger than the viewable part of the screen, because the safari bottom navbar will cover it. Adding (min / max) - height: -webkit-fill-available; for the classes min-h-screen, max-h-screen and h-screen would fix this, and still fall back to 100vh for those who dont support ...

  18. 100vh height when address bar is shown

    As the document describes, this ensures compatibility with mobile Safari and is independent of how large the URL bar is. Share. Improve this answer. Follow answered Nov 24, 2019 at 17:55. Ross Light Ross ... So, when the address bar is displayed, and you use 100vh height, the bottom part will go out of the screen. Many developers do not agree ...

  19. How to fix safari 100vh height? : r/webdev

    How to fix safari 100vh height? Question. Hi, I tried to look at multiple solution + the code. min-height: -webkit-fill-available; However, it's not working and it has a weird white space at the bottom like if my 100vh was transformed into 95vh.

  20. On Safari 16.6 im getting strange colors …

    On Safari 16.6 im getting strange colors when using mix-blend-mode: difference Welcome to Apple Support Community A forum where Apple customers help each other with their products.

  21. Safari + CSS: using "calc" with "vh" does not work

    5. I'm encountering a very niche issue CSS issue on Safari. I have the following CSS rule: min-height: calc(100vh - 115.5px - 25px*2); This works on Chrome, but Safari doesn't seem to like the combination of calc and vh. (It works if I replace vh with %, for example--but I do need to calculate based on vh or some appropriate alternative.)

  22. Safari height 100% element inside a max-height element

    Similar problem appears on Safari if parent element uses flexbox properties - container won't take 100% of the height. Another solution (besides position: absolute;) would be to use vh (viewport height) units: div { height: 100vh; }