Converting PX to REMs with a Simple Mixin

One of the first challenges you face when working with a designer is fonts. Designers are typically working with a fixed unit such as pixels, and we developers are typically working in EMs or REMs. So how do we handle the conversion?

Problem

So here's the full issue. Let's pretend you've been handed a new design for a page for a simple blog. The blog has a header that's 24px sub-header that's 20px and body copy thats 14px:


h2 {
    font-size: 24px;
}
h3 {
    font-size: 20px;
}
.body-content {
    font-size: 14px;
}

Simple right? Well, not really. As soon as you hard-code those values in, you've given other peoples devices a very explicit command. These three areas will be these font sizes, even if they don't make the most sense for the device.

The fix is to use REMs, but REMs come with two problems that we need to fix let's check it out:


h2 {
    font-size: 2.4rem;
}
h3 {
    font-size: 2rem;
}
.body-content {
    font-size: 1.4rem;
}

First thing you had to do was convert the size of the text, that's not difficult, just divide by 10--or is it? Does 2.4 REMs equal 24 pixels?

Nope.

Not unless you've first set your ROOT EM:


body {
    font-size: 62.5%;
}

Now 2.4rem will equal 24px. But this sorta sucks too because now all your fonts will start up a lot smaller. There's also the small issue of older browsers not really supporting REMs. Honestly, this probably isn't a huge deal, but a simple mixin and fix this all up so let's do it.

Mixin Code

The first thing I do is pull in a small Sass function that I like to use: get-value() the idea is simple, this function will make sure our number is a a real number i.e 14px will become 14:


@function get-value($n) {
   @return $n / ($n * 0 + 1);
}

Now lets dive into the mixin:


$base: 62.5%;

body {
    font-size: $base;
}

@mixin font-size($font-size) {
   $base-size: get-value($base / 100);
   $base-px-size: 16 * $base-size;

   font-size: $font-size;
   font-size: (get-value($font-size) / $base-px-size) + rem;
}

So what's going on here? Starting at the top, I've got a $base variable set, now I could set that to any percentage I'd like, but in this case I'm sticking with 62.5%. The $base gets used in both the body class and the mixin itself. This is important. If you like to adjust your font base size from 16px to something else, you need to make sure that the mixin knows you're doing that or else all the calculations will be off.

In the next line we're just taking our new number and multiplying it by 16, which is the default root font size. So in our example we're taking 16 * 0.625 (0.625 is 62.5 / 100) that give us our new base of 10 pixels because 16 * 0.625 = 10.

Whew, ok, we're almost done with the math. Now, this next line is optional, because you may say, IDGAF to old browsers. I, on the other hand, am making sure I don't have to respond to a client has a 400 year old computer and doesn't understand why "everything looks weird." Basically that line is just your pixel number and just setting it as the font size. Old browsers that don't understand REMs will understand pixels, and things will look good, modern browsers will just ignore that line, because the next line will just overwrite it.

So that last line is where the magic happens. We take the font size we want, divide it by our pixel base and append "rem" to it and we're done. Nothing crazy, but let's see it in action:


h2 {
    @include font-size(24px);
}
h3 {
    @include font-size(20px);
}
.body-content {
    @include font-size(14px);
}

Looks pretty good, we can change our base size to whatever we want, or not change it and this little mixin will calculate the right REM value for us, and we have the added bonus of being able to talk to our designers in a language that makes sense to everyone: pixels.