HTML & CSS
Article

Converting Your Typographic Units with Sass

By Byron Houwens

Long ago, in the dense mists of the Internet’s past, intrepid adventurers tackled danger in much the same way: sling a fixed 960px layout, fight in a set grid and fire their typography in pixels to ward off evil.

Responsive web design has since changed all that, bringing us out of that dense mist and into an age of enlightenment. And yet, using typography on the web is still a pain at times. With the push from pixels or points to percentages and ems, I find myself continuously having to Google “pixel to percentange conversion chart” or something similar at the beginning of every project and often during as well.

In frustration, I turned to the power of Sass to forge a new, all-encompassing function in my quest to vanquish these problems, and today we’re going to go through building that function. 

It’ll allow us to convert freely between pixels, ems, and percentages without having to consult a chart every time and, hopefully, alleviate a lot of our headaches in the process.

Setting Things Up

Firstly, it’s extremely important to have a default font size defined in your CSS. Most browsers will default to 16px, but if your project requires something different make sure that your CSS knows about it. Most boilerplates come with 16px defined as the default as well, and I’m going to be assuming it as the default for this tutorial.

We then need to decide which units we’re going to support. Since this is likely to be helpful in a print to web environment, or even just a project which starts in Photoshop and ends up in the browser, we’ll be looking at pixels, points, ems and percentage.

We also want to give ourselves the option to freely convert between them, so already we can say that we’ll need three arguments for our function at least:

@function convert($value, $currentUnit, $convertUnit){}

The first argument is the number we want going in (like 16), the second is the unit we’ve currently got (like pixels) and the third one is the unit we want to convert to (like percentage). So in our quick example, if we want to convert 16 pixels to a percentage value, we would do this:

.foo{
   font-size: convert(16, px, percent);
}

Which will give us:

.foo{
   font-size: 100%;
}

Let’s Beef It

Now for the bit that goes in between the braces. Firstly, we want to be able to start off with either pixels, ems, points or a percentage so we’ll need four statements to take care of them all. If we were using a full-fledged language we might use a switch statement, but since this is Sass we’ll stick with much simpler if statements:

@function convert($value, $currentUnit, $convertUnit){
   @if $currentUnit == px{

      //stuff for pixels

   }@else if $currentUnit == ems{

      //stuff for ems

   }@else if $currentUnit == percent{

      //stuff for percentage

   }@else if $currentUnit == pts{

      // stuff for points

   }
}

We now have an if statement for each possible input unit (whether we want pixels, ems, points or percentage to start with). So this is about 50% of the way there. All we have to do now is throw some awesome stuff into those if statements!

“Mathematical!” — Finn the Human

Okay so things get pretty mathematical at this point. Assuming we’re working with 16px as the default font size, we have to convert into ems and percentage like so:

@if $currentUnit == px{
  @if $convertUnit == ems{
    @return $value / 16 + 0em;
  }
  @else if $convertUnit == percent{
    @return percentage($value / 16);
  }
}

Again, we’re using one if statement per conversion (so one for ems, one for percentage) and then doing a little math to get our output. I’m not going to do a case for point values, since these only work with print CSS anyway. 

With ems (and a default size of 16px) we just divide by 16 and throw on the “em” unit. Percentages with Sass are a little more tricky. We can’t just throw a “%” on the end like we did with ems, as Sass will throw an error right back (something to the effect of “what the hell are you doing putting that there”). So here we need to incorporate Sass’s percentage function in order to return a valid percentage unit.

And with that we have a function that converts pixels into ems or percentages! This is usually enough for a lot of developers, but let’s see how to extend this into the other two units as well:

@else if $currentUnit == ems{
  @if $convertUnit == px{
    @return $value * 16 + 0px;
  }
  @else if $convertUnit == percent{
    @return percentage($value);
  }
}

The math needs to change here for each statement, but that will sort out ems. Now percentages:

@else if $currentUnit == percent{
  @if $convertUnit == px{
    @return $value * 16 / 100 + 0px;
  }
  @else if $convertUnit == ems{
    @return $value / 100 + 0em;
  }
}

And finally points:

@else if $currentUnit == pts{
  @if $convertUnit == px{
    @return $value * 1.3333 + 0px;
  }
  @else if $convertUnit == ems{
    @return $value / 12 + 0em;
  }
  @else if $convertUnit == percent{
    @return percentage($value / 12)
  }
}

And we’re done! Our function allows us to convert any value freely between any unit we want.

TL;DR

Our final function looks like this:

@function convert($value, $currentUnit, $convertUnit){
   @if $currentUnit == px{

      @if $convertUnit == ems{
        @return $value / 16 + 0em;
      }
      @else if $convertUnit == percent{
        @return percentage($value / 16);
      }

   }@else if $currentUnit == ems{

      @if $convertUnit == px{
        @return $value * 16 + 0px;
      }
      @else if $convertUnit == percent{
        @return percentage($value);
      }

   }@else if $currentUnit == percent{

      @if $convertUnit == px{
        @return $value * 16 / 100 + 0px;
      }
      @else if $convertUnit == ems{
        @return $value / 100 + 0em;
      }

   }@else if $currentUnit == pts{

      @if $convertUnit == px{
        @return $value * 1.3333 +0px;
      }
      @else if $convertUnit == ems{
        @return $value / 12 + 0em;
      }
      @else if $convertUnit == percent{
        @return percentage($value / 12)
      }

   }
}

It looks a little daunting, but all it really does is take our size, then convert it from the first unit into the second unit, returning the result. The only tough part is keeping track of what calculations to make.

If you want to play around with this function, you can do so on Sassmeister over here.

As always, feel free to steal, mangle, rearrange and otherwise use this in whatever helps you the most when working with typography on the web. You could easily expand on this, maybe including something like rems unit conversion, error handling for inputs that won’t work, or setting default units that you use all the time.

 If you have any other awesome ideas for this function let us know in the comments section below.

Comments
npostulart

Nice function.

I would recommend to use a more configurable approach using the base size of the html for converting, using the unit in the value for converting and output errors if input is wrong.

You can see my suggestion here https://gist.github.com/npostulart/4de142bf2874c1802a15

I didn't updated the pt convert part yet as I need to figure out the correct values for converting.

Hope you like my changes to your code.

HugoGiraudel

Ohai. Nice article.

One suggestion: you might want to use % rather than percent since unit(1%) returns %, not percent. smile

HugoGiraudel

Hey @npostulart. Unfortunately, your code incorrectly handles units by treating these as strings. More information in a recent SitePoint article: http://www.sitepoint.com/understanding-sass-units/. Cheers. smile

npostulart

Hi @HugoGiraudel, thanks for your hint. I updated my Gist to better handle the unit conversion.
The only small issue I cannot solve is using '%' for comparison as % alone would lead to an Invalid CSS error.

Hope I did it better this time. smile

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.