Grow your CSS skills. Land your dream job.

There’s more to the CSS rem unit than font sizing

Published by Guest Author

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.

Comments

  1. Brian Blakely
    Permalink to comment#

    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!

  2. Permalink to comment#

    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?

    • Marie
      Permalink to comment#

      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.net

      After my experience building my site using rem, I gotta say it was awesome! No need to worry about font sizes depending on parent’s em unit. Actually, almost anything that had a dimension I did it with rem‘s.

      Proper IE7/8 stylesheet in place was needed of course, another HTTP request for those folks. But the rest of us, happy campers.

    • Jess
      Permalink to comment#

      This is where snippets for LESS and SASS come in handy. I use these as primers for everything like width, height, padding, margin, etc…

      http://css-tricks.com/snippets/css/less-mixin-for-rem-font-sizing/

  3. 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 :))

  4. Jbeja
    Permalink to comment#

    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.

  5. EricM
    Permalink to comment#

    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.

  6. Great tips this technique is very useful I will definitely be using this from now one or at least try to lol

  7. Permalink to comment#

    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?

    • Jesse
      Permalink to comment#

      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.

  8. Riki Sharma
    Permalink to comment#

    You spelled “Viewport” as “Viewporth”. Third last work in the first paragraph.

    Cool article!

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".