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.
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/ ↩
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 .
🤔 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
- 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 .
- Español – América Latina
- Português – Brasil
- Tiếng Việt
The large, small, and dynamic viewport units
New CSS units that account for mobile viewports with dynamic toolbars.
The viewport and its units
To size something as tall as the viewport, you can use the vw and vh units.
- vw = 1% of the width of the viewport size.
- vh = 1% of the height of the viewport size.
Give an element a width of 100vw and a height of 100vh , and it will cover the viewport entirely.
The vw and vh units landed in browsers with these additional units
- vi = 1% of the size of the viewport’s inline axis.
- vb = 1% of the size of the viewport’s block axis.
- vmin = the smaller of vw or vh .
- vmax = the larger of vw or vh .
These units have good browser support.
Browser Support
The need for new viewport units
While the existing units work well on desktop, it’s a different story on mobile devices. There, the viewport size is influenced by the presence or absence of dynamic toolbars. These are user interfaces such as address bars and tab bars.
Although the viewport size can change, the vw and vh sizes do not. As a result, elements sized to be 100vh tall will bleed out of the viewport.
When scrolling down these dynamic toolbars will retract. In this state, elements sized to be 100vh tall will cover the entire viewport.
To solve this problem, the various states of the viewport have been specified at the CSS Working Group.
- Large viewport : The viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be retracted.
- Small Viewport : The viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be expanded.
The new viewports also have units assigned to them:
- Units representing the large viewport have the lv prefix. The units are lvw , lvh , lvi , lvb , lvmin , and lvmax .
- Units representing the small viewport have the sv prefix. The units are svw , svh , svi , svb , svmin , and svmax .
The sizes of these viewport-percentage units are fixed (and therefore stable) unless the viewport itself is resized.
In addition to the large and small viewports, there‘s also a dynamic viewport which has dynamic consideration of the UA UI:
- When the dynamic toolbars are expanded, the dynamic viewport is equal to the size of the small viewport.
- When the dynamic toolbars are retracted, the dynamic viewport is equal to the size of the large viewport.
Its accompanied units have the dv prefix: dvw , dvh , dvi , dvb , dvmin , and dvmax . Their sizes are clamped between their lv* and sv* counterparts.
These units ship in Chrome 108, joining Safari and Firefox which already have support.
There's a few caveats to know about Viewport Units:
None of the viewport units take the size of scrollbars into account. On systems that have classic scrollbars enabled, an element sized to 100vw will therefore be a little bit too wide. This is as per specification .
The values for the dynamic viewport do not update at 60fps. In all browsers updating is throttled as the UA UI expands or retracts. Some browsers even debounce updating entirely depending on the gesture (a slow scroll versus a swipe) used.
The on-screen keyboard (also known as the virtual keyboard) is not considered part of the UA UI. Therefore it does not affect the size of the viewport units. In Chrome you can opt-in to a behavior where the presence of the virtual keyboard does affect the viewport units .
Additional resources
To learn more about viewports and these units check out this episode of HTTP 203 . In it, Bramus tells Jake all about the various viewports and explains how exactly the sizes of these units are determined.
Additional reading material:
- CSS Values 4 Specification: Viewport-relative lengths
- ChromeStatus Entry
- Layout Viewport explainer
- Viewport Units explainer
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License , and code samples are licensed under the Apache 2.0 License . For details, see the Google Developers Site Policies . Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2022-11-29 UTC.
DEV Community
Posted on Nov 1, 2021 • Updated on Nov 4, 2021
CSS *vh (dvh, lvh, svh) and *vw units
This background section is large; here's a handy link to the new unit section if you would like to skip ahead.
However, I think it can be useful to understand how we got where we are. Additionally, this information will be useful to understand when talking about the dvh and dvw units later.
Note: while I'll focus mainly on the vh unit, please know that the vw unit had the same issues. It's just easier to talk about one of them.
History of vh
The vh unit, as it originally existed, was defined as
Equal to 1% of the height of the initial containing block.
The initial containing block (ICB) definition can be found here , but I think the first bullet point summarizes it well (emphasis mine):
The containing block in which the root element lives is a rectangle called the initial containing block. For continuous media, it has the dimensions of the viewport and is anchored at the canvas origin;
In other words, it's the same size as the viewport; or the same size as the content of your website that you can see without scrolling.
Which is very useful and good... that is, until mobile browsers started doing some tricks to maximize your phone's screen space. To see these tricks, open any webpage on your phone and scroll around some bit, while paying attention not to the content of the webpage but the browser's UI itself.
You may have noticed that the browser's UI changes size as you scroll. And if the browser's UI and viewport changes size, then the question regarding vh became:
If vh is 1% of the initial containing block (ICB), but the ICB changes sizes as a user scrolls, what does vh really equal ?
First Solution, First Problem
The first and simplest answer to that question was " vh changes as the ICB changes." Logically, it would seem like that should be the final answer, too.
However, there's a problem with this solution:
Let's say you have a phone that has 100px of screen space. When you load up a page, the browser UI takes up 15px which leaves 85px left for your website's content.
However, your browser is Super Cool, so when you scroll down the browser UI only takes up 10px , which leaves your website content with 90px . Or, in table form:
On your Fancy Website™, you have 5 <section style="height: 100vh"> elements on your page. When someone first loads the page, each <section> is 85px tall. But as they begin to scroll down, the browser UI begins to change size which causes those section elements to grow by 5 pixels, to a total of 90px !
Now, let's say the user's now at the bottom of the page looking at the 5th section element. They decide they want to scroll up, which has the side effect of changing the browser UI to the maximum size. That causes vh to shrink, and your sections are now all 5px smaller; when you're at the bottom of the page, that's a total of 20px of difference (4 sections with 5px each).
Just by scrolling upwards a tiny bit, the content of your page has jumped upwards 1/5 of your total screen space (100px total / 20px smaller)! That is a very jarring experience, and honestly it sucked.
Imagine if you had used vh for things like font-size , and how that would look!
Second Solution, Second Problem
With this problem in mind, in 2015 Safari / Webkit engineers decided to change the behavior of vh units :
Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size. From the data we had, using the larger view size was the best compromise.
In other words, a dynamic vh unit was not great, so they changed it to be a static unit equal to the size of the viewport when the browser UI was its smallest (and the content / "view size" was the largest).
About a year later Chrome / Blink engineers agreed and also updated vh units to do the same .
Which is where we are now (as of the time of this writing) with vh .
One of the problems with vh being the "largest view size" is that anything that is height: 100vh is now larger or overflows the screen when you first load a page. It's pretty difficult, using just CSS, to get content to fit the page exactly .
And so, in 2019, a new CSS proposal was born . And in 2021, that proposal, with feedback and improvements, was accepted in the CSS spec as several new units!
The New CSS Units
The large , small , dynamic , and traditional vh units.
Large Viewport Units
The lvh & lvw units are defined as:
The large viewport-percentage units (lv*) are defined with respect to the large viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be retracted.
In other words, the size when the browser UI is the smallest and the website content is the largest. lvh is essentially how the vh unit currently (at the time of this writing) acts.
Small Viewport Units
The svh & svw units are defined as:
The small viewport-percentage units (sv*) are defined with respect to the small viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be expanded.
Essentially, svh gives you units that you can use to fill the screen when the browser UI is at its largest, and the website content is at its smallest.
Dynamic Viewport Units
The dvh & dvw units are defined as (with emphasis mine):
The dynamic viewport-percentage units (dv*) are defined with respect to the dynamic viewport size: the viewport sized with dynamic consideration of any UA interfaces that are dynamically expanded and retracted. This allows authors to size content such that it can exactly fit within the viewport whether or not such interfaces are present. The sizes of the dynamic viewport-percentage units are not stable even while the viewport itself is unchanged. Using these units can cause content to resize e.g. while the user scrolls the page. Depending on usage, this can be disturbing to the user and/or costly in terms of performance.
While the dvh & dvw units may sound good on paper, the caveats noted in the definition above (and in the first problem & solution section above ) actually lead me to believe that you should only use them in very rare and specific situations.
Traditional Viewport Units
With these new units, where does vh and vw sit? Interestingly enough, they are currently defined as:
The UA-default viewport-percentage units (v*) are defined with respect to a UA-defined UA-default viewport size, which for any given document should be equivalent to the large viewport size, small viewport size, or some intermediary size.
Personally, I find this definition to be too vague, and I have concerns about how this will affect users and developers . I guess we'll see where it goes!
Top comments (6)
Templates let you quickly answer FAQs or store snippets for re-use.
- Email [email protected]
- Location Venezuela
- Work Esports Data Analyst at Tribe Gaming
- Joined Jan 2, 2021
Underrated article. Thank you!
- Location Pune, India
- Work Team Lead at Zensar
- Joined Dec 6, 2018
amazing article...
- Location Kigali,Rwanda
- Work Software Engineer
- Joined Jul 9, 2020
the history makes difference, it makes it easy to understand those l , s , and d prefixes
- Joined Dec 21, 2023
Nice article - really appreciate the history, helps it stick in my mind more :)
- Joined Feb 25, 2020
Now, min-h-[75dvh] is best. tailwindcss.com/blog/tailwindcss-v...
- Joined Aug 6, 2024
Awesome articles with the detailed history, thank you!
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
Tailwind Commands Cheat Sheet
Madhav Ganesan - Jul 13
Responsive Web Design: Techniques Using Media Queries, Viewport Units, and Fluid Layouts
MD Hasan Patwary - Jul 13
Styling Buttons with styled-jsx in Next.js
Antoine - Jul 2
Enhancing Web Performance with Image Sprites
We're a place where coders share, stay up-to-date and grow their careers.
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
PostCSS plugin to fix height/min-height: 100vh on iOS
postcss/postcss-100vh-fix
Folders and files, repository files navigation, postcss 100vh fix.
PostCSS plugin to fix iOS’s bug with 100vh .
It works in Chrome (just -webkit-fill-available causes problems in Chrome in some cases), iOS/iPad/MacOS Safari and all other browsers . Pure CSS solution, no JS required.
It works with min-height and max-height too.
Pure CSS solution is limited. For many cases you need to use JS-based hack like postcss-viewport-height-correction :
Our hack do not work with partial height like height: 90vh or height: calc(100vh - 60px) .
Also, we do not fix Chrome for Android bug:
Step 1: Install plugin:
Step 2: Check you project for existed PostCSS config: postcss.config.js in the project root, "postcss" section in package.json or postcss in bundle config.
If you do not use PostCSS, add it according to official docs and set this plugin in settings.
Step 3: Add the plugin to plugins list:
Step 4: Use height: 100vh or min-height: 100vh in your CSS.
Sponsor this project
Contributors 2.
- JavaScript 100.0%
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:
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!
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 .
New viewport sizes
How svh, lvh and dvh can improve your responsive design, new viewport units.
Up until now, 100vh was the largest height you could get for your CSS styles. The problem with 100vh is that it may actually show more content than is initially available on screen. A practical example you might think of is iOS’ Safari browser.
This now changes, as the standard is enhanced with small, large and dynamic viewport units. I only mentioned the height until now, but all those three size categories also apply for the widt. For simplicity, I’ll use the height throughout this article only.
Small viewport unit (svh, svw)
The small viewport units, identified by sv*, are aligned to the “small viewport”. This unit respects dynamically changing UI-elements from the user agent and essentially allows you to fill the viewport dimension when the the visible document content is its smallest size and the UI-elements are in their largest size.
An example would be Safari’s default UI layout when you load a page. The address bar at the bottom is expanded and takes up quite a lot of space. In this state, the user agent’s UI elements use the maximum of their available space and your page’s visible content is given its minimal visible size.
Large viewport unit (lvh, lvw)
Opposite to the small viewport, the large viewport results in a maximum value that fills the visible page when the UI is in its smallest variant and the page content at its largest. If this specification sounds familiar, it might be due to the fact that the current implementation for “vh” and “vw” acts the same.
A practical example would be Safari’s view when you scroll down a little on any given scrollable page. As you’ll notice, the address bar shrinks, thus leading to the UI’s smallest visible footprint. Your content is given its maximum possible space in this state.
Dynamic viewport unit (dvh, dvw)
You might already have guessed it, the dynamic viewport units can possibly change when the user scrolls. They span at most the large viewport and at least the small viewport. This value can be quite handy if you want to align to the viewport’s height, but always keep your content visible, even if the user scrolls.
A practical example of such a use case would be directly on this PWA. Every “above-the-fold” section for an article keeps the footer aligned to the bottom of the viewport.
Availability
As of writing, these new viewport units are only available from Safari 15.4 and onwards. For Chrome, the feature is behind a feature flag, but not yet stable. Apart from those two browser, no other vendor supports the new units currently.
Suggestions
- can-i-use caniuse.com
- CSS spec w3.org
- Italiano it
- Français fr
- Esperanto eo
- Ελληνικά el
- Afrikaans af
Overlapping bottom navigation bar despite 100vh in iOS Safari
»100vh« may not behave as expected for some mobile browsers and the bottom of your content will be partially hidden behind the browser’s bottom bar (i.e. below the »fold«).
First of all, let’s have a look at the issue by checking out the following example. It’s a simple page with 2 absolutely positioned boxes in the top left corner ( .top ) and the right bottom corner ( .bottom ). These boxes are wrapped within an element ( .container ) with a width of 100vw and a height of 100vh . You may have something similar in your project, such as a fullscreen modal/lightbox with a header/footer.
This should span accross the full viewport, right? Well, in the left screenshot below, you can see that in iOS Safari the bottom navigation bar actually overlaps your content, i.e. your content is below the »fold«—although you may have expected that it’s not part of the viewport.
In the right screenshot, you can see how one would expect the layout to be. The container spans between the top address bar and the bottom navigation bar.
This is a well-known issue and unfortunately intentional, as it prevents other problems, as Benjamin Poulain explained in his reply to a WebKit bug ticket regarding this issue.
This is completely intentional. It took quite a bit of work on our part to achieve this effect.
The base problem is this: the visible area changes dynamically as you scroll. If we update the CSS viewport height accordingly, we need to update the layout during the scroll. Not only that looks like shit, but doing that at 60 FPS is practically impossible in most pages (60 FPS is the baseline framerate on iOS).
It is hard to show you the "looks like shit" part, but imagine as you scroll, the contents moves and what you want on screen is continuously shifting.
Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.
From the data we had, using the larger view size was the best compromise. Most website using viewport units were looking great most of the time.
So, it’s not a bug—and no fix is planned for this.
Luckily, this doesn't have to be the most depresssing answer ever. How do we go on from this? There’s a couple of solutions.
Depending on your use case, it may be enough to simply use 100% instead of 100vh , especially for fixed/sticky elements (as 100% will be relative to the »real« viewport).
However, if your element is nested somewhere in the DOM, this may not work out (as 100% will be relative to the parent elements which are only as tall as the content they contain). And that may have been the motivation why you wanted to use 100vh in the first place.
stretch / -webkit-fill-available
Intrinsic and extrinsic sizing is a new CSS functionality that extends the sizing properties with keywords that represent content-based »intrinsic« sizes and context-based »extrinsic« sizes. This allows CSS to more easily describe boxes that fit their content or fit into a particular layout context.
One of these keywords is stretch which formerly was known as fill , fill-available , and its prefixed spin-offs -moz-available and -webkit-fill-available . We can make use of this functionality and because CSS skips over style declarations it doesn’t understand, we can implement fallbacks for all of these possible implementations.
(Hint: Autoprefixer will compile stretch to -webkit-fill-available and -moz-available automatically.)
JavaScript is always the »last stronghold« for stuff that’s not possible with pure CSS. Using CSS variables, you can pass the value of window.innerHeight into your CSS and update this variable every time the viewport is resized.
In your CSS, you can consume this variable as follows.
If you can’t use CSS variables in your project (e.g. due to browser support concerns), you can also update the height of your affected elements directly from within your script.
Unfortunately, there isn’t a one-size-fits-all solution for this issue. You should try the aforementioned solutions top down and be very conscientious with your cross-browser/cross-device testing.
- Discuss on Twitter
- Edit on GitHub
Fun with Viewport Units
DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!
Viewport units have been around for several years now, with near-perfect support in the major browsers , but I keep finding new and exciting ways to use them. I thought it would be fun to review the basics, and then round-up some of my favorite use-cases.
What are viewport units?
Four new “viewport-relative” units appeared in the CSS specifications between 2011 and 2015, as part of the W3C’s CSS Values and Units Module Level 3 . The new units – vw , vh , vmin , and vmax – work similarly to existing length units like px or em , but represent a percentage of the current browser viewport.
- Viewport Width ( vw ) – A percentage of the full viewport width. 10vw will resolve to 10% of the current viewport width, or 48px on a phone that is 480px wide. The difference between % and vw is most similar to the difference between em and rem . A % length is relative to local context (containing element) width, while a vw length is relative to the full width of the browser window.
- Viewport Height ( vh ) – A percentage of the full viewport height. 10vh will resolve to 10% of the current viewport height.
- Viewport Minimum ( vmin ) – A percentage of the viewport width or height, whichever is smaller . 10vmin will resolve to 10% of the current viewport width in portrait orientations, and 10% of the viewport height on landscape orientations.
- Viewport Maximum ( vmax ) – A percentage of the viewport width or height, whichever is larger . 10vmax will resolve to 10% of the current viewport height in portrait orientations, and 10% of the viewport width on landscape orientations. Sadly, and strangely, vmax units are not yet available on Internet Explorer or Edge.
While these units are derived from viewport height or width, they can all be used everywhere lengths are accepted – from font-size to positioning, margins, padding, shadows, borders, and so on. Let’s see what we can do!
Responsive Typography
It’s become very popular to use viewport units for responsive typography – establishing font-sizes that grow and shrink depending on the current viewport size. Using simple viewport units for font-size has an interesting (dangerous) effect. As you can see, fonts scale very quickly – adjusting from unreadably small to extra large in a very small range.
This direct scaling is clearly too dramatic for daily use. We need something more subtle, with minimums and maximums, and more control of the growth rate. That’s where calc() becomes useful. We can combine a base size in more steady units (say 16px ) with a smaller viewport-relative adjustment ( 0.5vw ), and let the browser do the math: calc(16px + 0.5vw)
By changing the relationship between your base-size and viewport-relative adjustment, you can change how dramatic the growth-rate is. Use higher viewport values on headings, and watch them grow more quickly than the surrounding text. This allows for a more dynamic typographic scale on larger screens, while keeping fonts constrained on a mobile device – no media-queries required. You can also apply this technique to your line-height , allowing you to adjust leading at a different rate than the font-size .
For me, this is enough complexity. If I need to constrain the top-end for rapid-growth headings, I can do that with one single media-query wherever the text becomes too large:
Suddenly I wish there was a max-font-size property.
Others have developed more complex calculations and Sass mixins to specify the exact text-size ranges at specific media-queries. There are several existing CSS-Tricks articles that explain the technique and provide snippets to help you get started:
- Viewport Sized Typography with Minimum and Maximum Sizes
- Fluid Typography
- The Math of CSS locks
I think that’s overkill in most cases, but your milage will absolutely vary.
Full-Height Layouts, Hero Images, and Sticky Footers
There are many variations on full-height (or height-constrained) layouts – from desktop-style interfaces to hero images, spacious designs, and sticky footers . Viewport-units can help with all of these.
In a desktop-style full-height interface , the page is often broken into sections that scroll individually – with elements like headers, footers, and sidebars that remains in place at any size. This is common practice for many web-apps these days, and vh units make it much simpler. Here’s an example using the new CSS Grid syntax:
See the Pen Full-height CSS Grid by Miriam Suzanne ( @mirisuzanne ) on CodePen .
A single declaration on the body element, height: 100vh , constrains your application to the height of the viewport. Make sure you apply overflow values on internal elements, so your content isn’t cut off. You can also achieve this layout using flexbox or floats .Note that full-height layouts can cause problems on some mobile browsers. There’s a clever fix for iOs Safari , that we use to handle one of the most noticeable edge-cases.
Sticky-footers can be created with a similar technique. Change your body height: 100vh to min-height: 100vh and the footer will stay in place at the bottom of your screen until it’s pushed down by content.
See the Pen Sticky-Footer with CSS Grid by Miriam Suzanne ( @mirisuzanne ) on CodePen .
Apply vh units to the height , min-height , or max-height of various elements to create full-screen sections , hero images , and more. In the new OddBird redesign , we constrained our hero images with max-height: 55vh so they never push headlines off the page. On my personal website, I went with max-height: 85vh for a more image-dominated look. On other sites, I’ve applied min-height: 90vh to sections.
Here’s an example showing both a max-height heroic kitten, and a min-height section . Combining all these tricks can give you some powerful control around how your content fills a browser window, and responds to different viewports .
Fluid Aspect Ratios
It can also be useful to constrain the height-to-width ratio of an element. This is especially useful for embeded content, like videos. Chris has written about this before . In the good-old-days, we would do that with % -based padding on a container element, and absolute positioning on the inner element. Now we can sometimes use viewport units to achieve that effect without the extra markup.
If we can count on the video being full-screen, we can set our height relative to the full viewport width:
That math doesn’t have to happen in the browser with calc . If you are using a pre-processor like Sass, it will work just as well to do the math there: height: 100vw * (9/16) . If you need to constrain the max-width, you can constrain the max-height as well:
Here’s a demonstration showing both options, with CSS custom properties (variables) to make the math more semantic. Play with the numbers to see how things move, keeping the proper ratio at all times:
See the Pen Fluid Ratios with Viewport Units by Miriam Suzanne ( @mirisuzanne ) on CodePen .
Chris takes this one step farther in his pre-viewport-units article, so we will too. What if we need actual HTML content to scale inside a set ratio – like presentation slides often do?
We can set all our internal fonts and sizes using the same viewport units as the container. In this case I used vmin for everything, so the content would scale with changes in both container height and width:
See the Pen Fluid Slide Ratios with Viewport Units by Miriam Suzanne ( @mirisuzanne ) on CodePen .
Breaking the Container
For years now, it’s been popular to mix constrained text with full-width backgrounds. Depending on your markup or CMS, that can become difficult. How do you break content outside of a restricted container, so that it fills the viewport exactly?
Again, viewport units can come in handy. This is another trick we’ve used on the new OddBird site, where a static-site generator sometimes limits our control of the markup. It only takes a few lines of code to make this work.
There are more in-depth articles about the technique, both at Cloud Four and here on CSS Tricks .
Getting Weird
Of course, there’s much more you can do with viewport units, if you start experimenting. Check out this pure CSS scroll-indicator (made by someone named Mike) using viewport units on a background image:
See the Pen CSS only scroll indicator by Mike ( @MadeByMike ) on CodePen .
What else have you seen, or done with viewport units? Get creative, and show us the results!
Scroll Indicator is pure madness :)
Great content. viewport is quite useful for responsive web design, helpful article. thanks
vw and vh is awsome! Although iOS 10* and the latest Facebook App Webview are currently having a few issues with 100vh being larger than the innerHeight, so full page webapps are getting lost off of the bottom of the screen.
This seems to do the trick to fix it : https://stackoverflow.com/questions/35421247/wrong-viewport-page-height-in-embedded-facebook-browser-in-ios-9-x
Can you give us an explanation of how the scroll indicator works? Is it just a triangle gradient that is covered by body::before , which is neatly sandwiched between the gradient and the content with z-index: -1 ?
That’s exactly right. The triangle is created like this:
- A linear gradient from bottom left to top right, flipping from blue to gray at the mid-point. This creates the triangle.
- A background size that is full-height ( 100% ) minus the height of the window ( 100vh ) below the header ( 129px ). This constrains the triangle height a bit, since the scroll-bar is at the top of the page, and you can never really scroll to the bottom.
Great article, Miriam. Here is what I’ve been doing with Viewport Units lately: Mixing them with linear equations and breakpoints: Poly Fluid Sizing ( https://www.smashingmagazine.com/2017/05/fluid-responsive-typography-css-poly-fluid-sizing/ )
On a related note, one downside to using Viewport units for font-size is that they aren’t accessible. Resizing your browser text size is ignored for anything using straight Viewport units. They will resize if you are using Viewport units in a calc() , but their rate of change is dependent on your pixel value used in the equation.
Other than that downside, Viewport Units are pretty great!
Great article! I’m so frustrated with browsers not keeping up with CSS/HTML specifications that have been around for years. Always have to wait years to implement the cool features and tools we need yesterday. Thank you Miriam, this will be bookmarked.
Can viewport units be used to create a background image parallax effect (in a similar way to the scroll indicator example)?
Thanks for this article.
You mention calc or sass for fluid aspect ratios, but I think neither is needed for these calculations. Couldn’t we simply write the following?
Sure, that works as well. I always prefer to show my math, with as many semantic hints as possible (like variable names), rather than pasting in a random-looking number like 56.25 . I want the code to be meaningful and readable, in addition to working. Your comments do something similar – adding a few clues, and you could add more – but why not make the math visible directly in the code?
Oh that makes sense. My logic was to avoid using calc() if not needed to make the code more accessible (poor Opera Mini users!) and performance (although I have no idea of the performance cost of using calc()…). But then as you say it does require the extra comments…
Right now it seems like there is a bug in Safari 10.1 that does not support fluid resizing of font-size values that contain a calc() function with a viewport unit as a value in the function. Obviously this doesn’t affect mobile users but definitely sucks for those people who want to resize their browser window with Safari in macOS. I submitted this bug to Apple so we’ll see if there is anything that can be done about it
In you sticky footers example, you were set it up column value as follow: grid-template-columns: minmax(auto, 12em) 5fr;
So, in this line, what is the meaning of 5fr unit? Because, if we set it up 1fr, doesn’t change anything!
Yeah it wouldn’t matter there as they are the only “fractions” in use. Could be 999999fr and be the same, because 999999/999999 = 1 just like 5/5 = 1 or 1/1 = 1
Ha, yep. Good catch. I think I was using fr units initially for the sidebar, and never changed the main width after moving to auto and em s.
Most of these don’t work with IE or Edge so their usefulness is limited.
The usefulness of IE and Edge is limited too.
If you use viewport units for font-size , you override the browsers’ inbuilt ability to zoom the text. This is regardless of whether you use it in a calc()-function. This breaks the most basic web accessibility feature, and rids your users of control.
The only way a user can zoom text with viewport units sizing is to change the viewport size. This seems random, and in many cases it will not work.
Please don’t break my zoom!
Please ignore my comment – I guess I misunderstood. If define font-size with a calc function with vw and px or em, then normal browser scaling works as a charm.
“or 48px on a phone that is 480px wide”===》“or 48vx on a phone that is 480px wide”
IMAGES
COMMENTS
I used this CSS only hax today and it worked. iOS 8.2 iPhone Safari : html, body { ... width: -webkit-calc(100% - 0px); ... } Thought: Using calc seems to get Safari to convert units to px itself. Now my page is correctly full-bleed on Chrome and Safari on iOS.
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 ...
var viewportHeight = $('.banner').outerHeight(); $('.banner').css({ height: viewportHeight }); Doing this solves the issue on mobile devices as when the page loads, the banner element is set to 100vh using CSS and then jQuery overrides this by putting inline CSS on my banner element which stops it from resizing when a user begins to scroll.
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 ...
2. CSS solution (not recommend) The last, but not the least solution is ` — webkit-fill-available`, this solution works only on Apple devices, it won't solve the problem on Android devices. I ...
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 ...
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 ...
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.
🔥 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.
Celebration: This web feature is now available in all three browser engines! The viewport and its units. To size something as tall as the viewport, you can use the vw and vh units.. vw = 1% of the width of the viewport size.; vh = 1% of the height of the viewport size.; Give an element a width of 100vw and a height of 100vh, and it will cover the viewport entirely.
With this problem in mind, in 2015 Safari / Webkit engineers decided to change the behavior of vh units: Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.
Chrome used to take the address bar into account, but this caused another problem: as the address bar slid out of view, all your elements which use vh units auto-resized to match the new viewport size, which caused the page (scroll position) to jump. So at some point in 2016 Chrome implemented the same behaviour as IOS Safari.
Usage. Step 1: Install plugin: Step 2: Check you project for existed PostCSS config: postcss.config.js in the project root, "postcss" section in package.json or postcss in bundle config. If you do not use PostCSS, add it according to official docs and set this plugin in settings. Step 3: Add the plugin to plugins list:
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 ...
Solution 1: CSS Media Queries. This method, albeit not entirely elegant, is simple and easy to implement. Simply target all iOS devices with specific device-width and heights. Here is a code ...
Availability. As of writing, these new viewport units are only available from Safari 15.4 and onwards. For Chrome, the feature is behind a feature flag, but not yet stable. Apart from those two browser, no other vendor supports the new units currently. A change to the CSS specification allows to align viewports to different and dynamic sizes.
Well, in the left screenshot below, you can see that in iOS Safari the bottom navigation bar actually overlaps your content, i.e. your content is below the »fold«—although you may have expected that it's not part of the viewport. In the right screenshot, you can see how one would expect the layout to be. The container spans between the ...
Mobile Safari does not distinguish between the CSS units svh and dvh. These units should yield different heights with the viewport expanded (toolbars retracted), however instead they are yielding the same height. Safari support for these units was announced in the WebKit blog around a year ago. These units are described in the specification CSS ...
Four new "viewport-relative" units appeared in the CSS specifications between 2011 and 2015, as part of the W3C's CSS Values and Units Module Level 3. The new units - vw, vh, vmin, and vmax - work similarly to existing length units like px or em, but represent a percentage of the current browser viewport. Viewport Width ( vw) - A ...
Easy css trick to fix 100vh Safari iOS bottom bar issue. I'm not a fan of using 100vh. vh and vw units are designed to size other elements on the page based on current viewport height and width (such as making your font size or line-height dynamic), they do not take into account of scrollbars or Safari bottom bar because it would mean those ...
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.)