The following is a guest post by Roman Rudenko, a mad scientist at Mobify where he’s tasked with understanding why browsers misbehave and with cajoling them into playing nice. When he’s not coding, Roman is learning to ride a motorbike without crashing into too many solid objects.
Many web designers and developers are familiar with the CSS rem
length unit. But, you may not know that it has a couple of handy alternate uses. In this post, I’ll describe how to use the CSS rem
unit to scale specific page elements while leaving others unaffected. I’ll also discuss how to use it as a replacement for the under-supported vw
(viewport width) unit.
For readers unfamiliar with the rem
unit (standing for “root em”), it provides a way to specify lengths as fractions of the root element’s font size. In practical terms, this almost always means the font size of the <html>
element. The most obvious use for rem
is replacing em
to protect inner element font sizes from being changed by outer elements, thus avoiding the classic nested em
scaling problem. But rem
effectively operates as a custom adjustable unit, so it can be useful for other things too.
First, let’s look at how rem
works and how it differs from the em
unit. An em
value is calculated against the font-size
of a current element, so boxes sized with it will consequently scale as font sizes are adjusted by the element or its ancestors. Meanwhile, rem
is defined as the font-size
of the root element. So, all references to rem
will return the same value, regardless of ancestor font size. In effect, rem
is a document-wide CSS variable. Browser support for rem
is quite good these days. It’s limited on desktop because IE8 does not support it, but common mobile and tablet browsers handle it much better.
Rem can be used for its typical font sizing duty. When markup nests in complex ways, font-size
declarations with em
values may compound unexpectedly, and rem
is a good choice for untangling those issues. However, there are some other interesting uses of rem
.
Scaling document elements
You can use rem
to scale some elements in a document while leaving others in place. In this example, the font-size
of secondary page content (slideshow controls, illustration names, post metadata) is controlled via rem
, but the primary content remains sized in pixels.
Check out this Pen!
When one of the red size adjustment links is clicked, a small piece of JavaScript adjusts <html>
font-size
, resizing secondary elements without altering primary ones.
This style of sizing can be useful for user-driven customization, or to adapt layouts for cases that require secondary elements to be more touchable (tablet) or visible (TV). Without rem
, every adjustable element would have to be resized separately.
/* States */
html.secondary-12px { font-size: 12px; }
html.secondary-14px { font-size: 14px; }
html.secondary-16px { font-size: 16px; }
/* Primary content stays fixed */
body { font-size: 18px; }
/* Secondary content scales */
.post-inner .meta { font-size: 1rem; }
.figure .title { font-size: 1rem; }
.slideshow .controls { font-size: 1.25rem; }
.slideshow .controls a { padding: 0 0.5rem; }
Replacement for vw
The CSS vw unit is defined as 1/100th of viewport width. This unit is useful for avoiding compounding in width calculations, just as rem
avoids font-size
multiplier compounding. It can also be used for non-width properties, such as font-size
(fitting a fixed fragment of text into a percentage-sized box) or height (preserving element aspect ratio). Support for vw is still spotty. So, we can use rem instead and dynamically recalculate <html>
font size to match vw
unit size.
Let’s have a look at an example implementation for this workaround (resize browser to see adjustments):
Check out this Pen!
body { font-size: 12px; margin: 0; padding: 0;}
.one, .two {
border: solid #666;
border-width: 10px; border-width: 0.01rem; /* 1vw */
border-radius: 30px; border-radius: 0.03rem; /* 3vw */
font-size: 20px; font-size: 0.02rem; /* 2vw */
padding: 20px; padding: 0.02rem; /* 2vw */
}
Here, the <body>
element font-size
rule insulates content from changes in <html>
font-size
. Browsers that don’t understand rem
are given a pixel-based fallback; browsers that do know rem
derive the proper dimension from <html>
font size.
An earlier version of this example allowed browsers that recognize the vw property to use it directly, but that was pulled after Chrome 25 failed to apply a border width specified with vw. Rem-based ersatz replacement had no such issue.
It was possible to do all of this before the rem
unit was available by computing and assigning properties directly with JavaScript. But, rem
makes things much easier. With rem
, there’s only one assignment to make for the entire document, and the script doesn’t need to remember which properties should be scaled in what exact manner.
(function (doc, win) {
var docEl = doc.documentElement,
recalc = function () {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
docEl.style.fontSize = clientWidth + 'px';
docEl.style.display = "none";
docEl.clientWidth; // Force relayout - important to new Android devices
docEl.style.display = "";
};
// Abort if browser does not support addEventListener
if (!doc.addEventListener) return;
// Test rem support
var div = doc.createElement('div');
div.setAttribute('style', 'font-size: 1rem');
// Abort if browser does not recognize rem
if (div.style.fontSize != "1rem") return;
win.addEventListener('resize', recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
There are a few things to note in the above JavaScript. First, the script will terminate early if the browser is out-of-date and doesn’t support the addEventListener
API or the rem
unit itself. Second, instead of setting <html>
font size to an actual vw
unit, it’s set to the viewport width in pixels. This means the CSS would need to scale vw
values by a factor of 100 for use with rem
, but avoids rounding bugs that would otherwise arise due to the limited font-size
precision in Webkit.
Other viewport units can be supported in pretty much the same way. There are downsides to this approach. For instance, you can’t use rem
for other purposes. And, you can only make use of one viewport unit (vw
) at a time and so can’t use others like vh
, vmin
, or vmax
.
While almost no production browser today supports true CSS variables, quite a few let you cheat a bit and get one in the form of the rem
unit. If you don’t need to use rem
for normal font sizing, you can use it for runtime scaling of pretty much anything. If you must use rem
for font sizing, you have the option of using CSS preprocessors (Sass or Less) to compute proper font dimensions, leaving the rem unit free for runtime trickery.
I’ve been using the same technique for about 3 years, and it’s been very handy. The only person showed interest when I posted it was Josh Brewer, though.
If you’re only supporting good browsers, the code becomes very lean (no surprise): https://gist.github.com/brianblakely/3106678
Nice, simple example app: http://m.kraftcheese.com/
Cheers!
Great Article! I’ve been wanting to talk REM for a while now! — Not to long ago I converted Bootstrap 2.3 to use all REM values instead of PX and used media queries to adjust the font-size. The entire box model I did it for. It made for an interestingly zoomable like site. Then the site I did it for went to production and I commented it out, haha. What are your thoughts Chris? Should an entire site be using REM’s?
I am not Chris, but my views are that yes, that’s fine, assuming you either a) Ensure that there is a fallback for browsers that don’t support rem or b) Don’t have visitors (check your analytics for this! =) that use browsers that don’t support rem.
I use
rem
‘s left and right in my website: ricardozea.netAfter my experience building my site using
rem
, I gotta say it was awesome! No need to worry about font sizes depending on parent’sem
unit. Actually, almost anything that had a dimension I did it withrem
‘s.Proper IE7/8 stylesheet in place was needed of course, another HTTP request for those folks. But the rest of us, happy campers.
This is where snippets for LESS and SASS come in handy. I use these as primers for everything like width, height, padding, margin, etc…
15% of users still use IE 6/7/8 and they have only 50% of all CSS3 features.
But.. we have 11 units for font. Amazing :))
My way: Use rem unit in the main stylesheet, and then make a ie stylesheet calculating/changing all properties that use rem two pixel, and then use the proper html condicional to use the ie stylesheet when is required.
I’ve been using the REM unit of all measurements for a couple of years now with no problem at all. I love the control it offers.
But, there’s unfortunately an issue with IE9&10. They will use REMs for the measurements of elements but not for font-size (no matter the syntax you use)! In fact if you set font sizes in REMs the entire font is ignored and they fallback to the user agent serif or sans-serif…not even fonts in your stack.
I logged a bug that took a while and a lot of work to get recognized but they’re working on it now.
See IE bug 772679 if you’re interested. In the meantime I add a px font-size as an IE workaround.
So this is the problem?! I just experienced this issue a few weeks ago after launching my website, IE9 and IE10 were displaying san-serif font instead of the webfont I had.
Only after setting a fallback in pixels for IE it was when the webfont started working.
Thanks for the info.
Great tips this technique is very useful I will definitely be using this from now one or at least try to lol
Thanks, as usual, very informative stuff from you. I noticed the foundation.css (Foundation 5 css framework) uses rem 62.5 – it seems this is 62.5% of the full width (is that right?):
.row {
margin: 0 auto;
max-width: 62.5rem;
width: 100%;
}
PS: This comment previewer is awesome – can I ask what plugin / theme you’re using for it?
Gvanto, I haven’t checked myself, but I’m pretty sure Chris (Coyier) documented the entire site redesign he did, as the site looks/works now, in “The Lodge”, which you can find in the main nav bar for the site. Perhaps he mentions or goes into the commenting functionality there. I hope that helps.
You spelled “Viewport” as “Viewporth”. Third last work in the first paragraph.
Cool article!
Thanks! Fixed and burying because no longer relevant.