Resize me or open demo in a new window
Resize the iframe on the left using the dragger next to the text. (or, better yet, see the demo on the left without the iframe and resize your browser). Open the demo on the left in your mobile browser and rotate the screen. You will see that font-size
of the word “STACEY” resizes according to the size of the containing circle. The diameter of this circle is always 33% of the width or height, depending on which is bigger. All these calculations are done in CSS using viewport units, which are very handy when coding responsive websites. With them, you can describe width
, height
, font-size
and other styles as percentages of the viewport width and height.
This article will cover off what viewport units are and how to use them. If you are already familiar with them, you will already know that there a few nasty gotchas when it comes to using viewport units, not the least of which are some commonly known bugs in iOS Safari and IE9 and 10. These are easily fixed, however, with an excellent JavaScript fix (or “buggyfill”) by Rodney Rehm (I have used these fixes in a production environment and they work like a charm and have a minimal effect on performance). His original release fixed issues with Safari for iOS, but he also graciously merged code that I added that plugs some holes in IE9 and allows developers to use viewport units inside of CSS3 calc()
expressions in iOS Safari and IE9+. If you want to use viewport units today, hopefully this article and “buggyfill” will help you avoid the issues that I had when I first started using them.
Here are screenshots of the full demo page on an iPhone.Hover over it to see the layout in portrait mode.
Download the latest version of viewport-units-buggyfill from github.
What are Viewport Units
Unlike the px
unit, viewport units describe how big an object is relative to the width and height the viewport (i.e. the visible part of the web page). If you always want a block of text to be 90% the viewport height, you can use the following CSS to do this:
.myLargeText {
/* 90% of the browser height = 90vh */
font-size: 90vh;
}
Resize me or open demo in a new window
1vh
is equal to 1% of the viewport height, so have the font-size of 90vh means the font is never taller than the height of the viewport. If you want to have a block that is 80% of the viewport width, that can be done like this:
.myLargeBlock {
/* 80% of the browser height = 80vw */
width: 80vw;
/* let's center this block */
margin: 0 auto;
}
Resize me or open demo in a new window
There are also vmin
and vmax
. 1vmin is equal to 1vw or 1vh (whatever is smallest) …
Resize me or open demo in a new window
… while 1vmax
1vw or 1vh (whatever is largest).
Resize me or open demo in a new window
Cool … So What Are The Gotchas You Were Talking About?
There are, of course, some issues with viewport units as they are implemented natively in browsers today, most of which are outlined on the html5please website:
- Safari for iOS browsers has a well-known bug that affects calculating viewport units when a iPhone/iPad is rotated.
- IE9 does not calculate viewport units correctly when changing media-query breakpoints.
- IE9 and 10 does not support
vmax
- IE9 does not support
vmin
(it does, hoever, supportvm
which is equivalent tovmin
). - IE9 and Safari for iOS calculates viewport units differently inside of a frame. IE9 will assume the
100vw
and100vh
to be the width and height of the parent document’s viewport, while Safari for iOS will choose 1px (!!!!) for both. - Safari in iOS cannot do viewport units in a lot of the more complicated CSS properties (e.g.
text-shadow
).
All of these issues, however, are handled using viewport-units-buggyfill.. It was created by Rodney Rehm to fix iOS Safari’s well known bugs with viewport units, and I added some code to it to handle additional issues in iOS as well as IE9+. Just download the code from github and insert this into the bottom of your web page — you’ll then be able use viewport units in your code in all modern browsers, including iOS Safari and IE9+:
<!-- This is the base buggyfill. --> <script src="path/to/viewport-units-buggyfill.js"></script> <!-- This is the hacks plugin needed for contentHacks to work correctly (see below). --> <script src="path/to/viewport-units-buggyfill.hacks.js"></script> <script>window.viewportUnitsBuggyfill.init({ // milliseconds to delay between updates of viewport-units // caused by orientationchange, pageshow, resize events refreshDebounceWait: 250, // provide hacks plugin to make the contentHack property work correctly. hacks: window.viewportUnitsBuggyfillHacks });</script>
The paramater object for .init()
is optional. It can have the following properties:
- If the
refreshDebounceWait
property is set to a number, then IE9+ will use a debounce routine to throttle how many times the buggyfill is exeucuted on a browser resize. - If
hacks
is set towindow.viewportUnitsBuggyfillHacks
, then we can use it to usevmin
andvmax
units in IE9+ using “content hacks”. (Note that in order for “content hacks” to work properly, thehacks
property has to be set towindow.viewportUnitsBuggyfillHacks
). Content hacks are coded like this:
.myLargeBlock { /* Non-IE browsers */ width: 50vmin; font-size: 80vmax; /* IE9 and 10 */ content: 'viewport-units-buggyfill; width: 50vmin; font-size: 80vmax;'; }
-
Note that Safari for older iOS (6.0 specifically) does not support the
vmax
keywords and it’s JavaScript engine ignores it. As of September 2014, David Smith’s iOS Version Stats Page states this only makes up 8.3% of all iOS devices in the wild, so I don’t think it’s not a huge deal. However, if this is of concern to you, you can use a “content hack” to fix this as well:.myLargeBlock { /* Modern browsers */ font-size: 80vmax; /* Safari for iOS < 8 and IE <= 10 */ content: 'viewport-units-buggyfill; font-size: 80vmax'; }
Note that I implemented this hack by using the
content
property since it is not used in CSS selectors that don’t use::before
or::after
pseudo-elements. This means, of course, that you cannot use virtual units in::before
and::after
rules for Safari for iOS <= 6. If you have to support this browser, it is better than nothing, but hopefully I can come up with a better way of doing this to prevent this restriction (if you have an idea, please make a comment below).
Download the latest version of viewport-units-buggyfill from github.
Using Viewport Units inside of calc()
Expressions
Using viewport units inside of CSS3 calc()
statements can be very handy! Let’s say, for example, you want to center a DOM element smack in the middle of the viewport. The CSS recipe is rather simple:
.circle { /* Let's make this a 200 x 200 circle */ width: 200px; height: 200px; border-radius: 50%; background: red; position: absolute; /* * We set the top property to be half of the * viewport height minus half of the height * of the element height to center it vertically. * * Similarly set the left property to be * half of the viewport width minus half * the element width to center it horizontally */ top: calc(50vh - 100px ); left: calc(50vw - 100px ); }
Resize me or open demo in a new window
Perfect and simple! However, Safari for iOS and IE9-10 also doesn’t allow viewport units inside the CSS3 calc()
expressions (it ignores these expressions). In order to fix this, I implemented the following hack so that viewport-units-buggyfill would do it instead:
.circle {
/* Let's make this a 200 x 200 circle */
width: 200px;
height: 200px;
border-radius: 50%;
background: red;
position: absolute;
/*
* We set the top property to be half of the
* viewport height minus half of the height
* of the element height to center it vertically.
*
* Similarly set the left property to be
* half of the viewport width minus half
* the element width to center it horizontally
*/
top: calc(50vh - 100px );
left: calc(50vw - 100px );
/*
* Here is the code for WebKit browsers that will allow
* viewport-units-buggyfill.js to perform calc on viewport
* units.
*/
content: 'viewport-units-buggyfill; top: calc(50vh - 100px ); left: calc(50vw - 100px );';
}
.init()
the buggyfill with the hacks
property set to window.viewportUnitsBuggyfillHacks
(as described above) in order for this to work.
How about support for legacy IE properties?
While adding features to this library, I did some tests with some of the fancier CSS3 properties, such as text-shadow
. They, of course, didn’t show up in IE9, since it doesn’t support CSS text-shadows. To fix this, I applied the DropShadow
Visual Filter trick I wrote about in a previous blog post to my test page. It looked fine, except it didn’t have the responsive sizing for the shadows like in more modern browsers, so I decided to add Visual Filter support for viewport units. Take a look at the demo below in both IE9 and a modern browser and you’ll see that text-shadows grow with the height of the viewport:
Resize me or open demo in a new window
As you can see from the CSS, viewport units now work in IE’s DropShadow
Visual filter:
.copy { font-family: "AmericanCaptain", "Impact", sans-serif; text-align: center; color: white; /* * The first part of this text-shadow (1vh 1vh 0 #000) * is the huge drop shadow on the bottom right of the * text. The other 1px shadows create the outline * around the text. */ text-shadow: 1vh 1vh 0 #000, 1px 0 0 #000, -1px 0 0 #000, 0 -1px 0 #000, 0 1px 0 #000; } body.ie9down .copy { zoom: 1; background: #000001; /* * This CSS is the IE9- equivalent of the text-shadow code above. * For more details, see my article "CSS3 Text-Shadow – Can It Be * Done in IE Without JavaScript?" * */ filter: progid:DXImageTransform.Microsoft.Chroma(color=#000001) progid:DXImageTransform.Microsoft.DropShadow(OffX=1, OffY=1, Color=#000000) progid:DXImageTransform.Microsoft.DropShadow(OffX=-1, OffY=-1, Color=#000000) progid:DXImageTransform.Microsoft.DropShadow(OffX=1vh, OffY=1vh, Color=#000000); }
.init()
the hacks
property set to window.viewportUnitsBuggyfillHacks
in order for this to work. Remember to include the viewport-units-buggyfill.hacks.js
script to ensure this works correctly!
Download the latest version of viewport-units-buggyfill from github.
Acknowledgements
- The iframe resizing routines on this web page were lifted from jQuery UI Resizable (note, however, that neither jQuery nor jQuery UI are needed for this buggyfill to work).
- As mentioned before, the buggyfill was originally written by Rodney Rehm. I just added some extra fixes to it
- Thanks to talented STACEY for the kind use of her photo and logo for the demo at the top of this page. In return, I promise to work on making her site responsive using this technique. :-)
- In the Rob/Doug Ford example, I used the freeware version of American Captain by The Fontry’s Michael G. Adkins.
1 response so far
Give Feedback
Don't be shy! Give feedback and join the discussion.
Please Note: If you are asking for help using the information on this page or if you are reporting a bug with the code featured here, please include a URL that shows the problem you are experiencing along with the browser/version number/operating system combination where the issue manifests itself. Without this information, I may not be able to respond.
denotes a required field.