Px to Em Functions

We've talked about "Why Ems?" here before.

For those new to em values, The Mozilla Developer Network does an excellent job of explaining ems:

...an em is equal to the size of the font that applies to the parent of the element in question. If you haven't set the font size anywhere on the page, then it is the browser default, which is probably 16px. So, by default 1em = 16px, and 2em = 32px.

If you still prefer to think in px, but like the benefits of em, no need have your calculator handy, you can let Sass do the work for you. There are a variety of ways to calculate ems with Sass.

Inline math:

h1 {
  font-size: (32 / 16) * 1em;
}

p {
  font-size: (14 / 16) + 0em;
}

Note: We need the "* 1em" there for Sass to correctly append the unit value. You can also use "+ 0em" for the same purpose.

Result:

h1 {
  font-size: 2em;
}

p {
  font-size: 0.875em;
}

This works, but we can make it easier.

The em() Sass function

There are quite a few different ways to write this function, this one from a Web Design Weekly article:

$browser-context: 16; // Default

@function em($pixels, $context: $browser-context) {
  @return #{$pixels/$context}em;
}

Super clever! This function uses Sass' string interpolation to append em to the value. Note that the $context parameter has a default value of $browser-context (so, whatever you set this variable to). This means that when calling the function in your Sass, you only need to define the $pixels value and the math will be calculated relative to $browser-context - in this case - 16px.

Example Usage:

h1 {
  font-size: em(32);
}

p {
  font-size: em(14);
}

Result:

h1 {
  font-size: 2em;
}

p {
  font-size: 0.875em;
}

A similar function using math instead of string interpolation from The Sass Way can easily be modified to accept variables like our string interpolation function:

//un-modified

@function calc-em($target-px, $context) {
  @return ($target-px / $context) * 1em;
}

// and modified to accept a base context variable

$browser-context: 16;

@function em($pixels, $context: $browser-context) {
  @return ($pixels / $context) * 1em;
}

Another using Sass' unitless() method:

$browser-context: 16;

@function em($pixels, $context: $browser-context) {
  @if (unitless($pixels)) {
    $pixels: $pixels * 1px;
  }

  @if (unitless($context)) {
    $context: $context * 1px;
  }

  @return $pixels / $context * 1em;
}

This allows us to either include the px unit or not in the function call.

h1 {
  font-size: em(32);
}

// is the same as:

h1 {
  font-size: em(32px);
}

Comments

  1. User Avatar
    Gelu timo
    Permalink to comment#

    Would it not be a ton easier with setting a base font size of 10px on the or some root element and the 1em will always be 10px? And you would only work with em. So it would be very easy to calc px. (32px = 3.2em).
    It is not clearly mentioned here but em is looking up the dom tree for thE first font-size.

    • User Avatar
      Jesse Rand
      Permalink to comment#

      Why would you base your entire font structure on a font size that small though? 10px would require you to change your font size on nearly element on the page just so that you could save yourself a little math?

    • User Avatar
      Gelu Timo

      What do you mean by changing the font size on each element?
      If you want “elastic” or responsive font size you need to declare that anyway for each component, just like this article shows.
      Just take 5 minutes and compare the 2 approaches. It’s not about avoiding math, it’s about making development easier for your team.

    • User Avatar
      Mahmoud
      Permalink to comment#

      sass is rock !!

      specially when you know some of programming concepts like functions , variables , if condition .. etc

      i didn’t engage with it a lot but seems like a life saver

    • User Avatar
      Navin Samuel

      re: Jesse’s comment
      Jesse is correct in that setting a base font of 10 would involve adjusting the font size on many more elements than would be necessary if your natural font size is > 10. It is incorrect to state that you would need to set font-size on every element for a responsive site.

      Consider the following snippet:

      <div>
      <!-- red and blue should be normal sized -->
      <div class="red"/>
      <div class="blue"/>
      
      <!-- We want green to be 3px smaller in size -->
      <div class="green/>
      </div>
      
      // Set base font size to 10px
      body { font-size: 10px; }
      .red { color: red; font-size: 1.3em; }
      .blue { color: blue; font-size: 1.3em; }
      .green { color: green; }
      

      … Now set font size to 13px (or whatever your base size is):

      body { font-size: 13px; }
      .red { color: red; }
      .blue { color: blue; }
      .green { color: green; font-size: em(10);  }
      

      If you set the base font to 10 (which is quite small!), you would end up with specifying font-size on classes red and blue if you want them to be the equivalent of a base size other than 10 for normal text. This is too much work compared to using a function to adjust the font-size.

  2. User Avatar
    Travis Hall

    Hi Chris,
    I love this mixin but I’m wondering how to make it accept shorthand.
    I would like to be able to use this for any property measurement – i.e. padding: em(10 20 15 0);
    I believe bourbon and compass have this built in but I prefer not use them if possible.
    I’m also wondering if there’s any value to converting to REM rather than EM.
    Thanks for all you do!

    • User Avatar
      Sean Dempsey

      Hi Travis,

      I wrote the article for Chris. Sorry I’m only just seeing this as I was reading the comments. These are sass functions which are a little less flexible than mixins.

      You could use this for short hand currently like so: padding: em(10) em(20); which isn’t quite what you are asking but the only way to do it with the functions listed above. I’ll see if I can work out a mixin using these functions and get back to you here.

      Thanks!

    • User Avatar
      Sean Dempsey

      And I missed answering one of your questions.

      For a great REM mixin, check out this awesome article by Hugo Giraudel:

  3. User Avatar
    Daryl Baker
    Permalink to comment#

    Thanks for this helps a lot with the process, instead of having to use calculator back and forth!

  4. User Avatar
    Jero
    Permalink to comment#

    Hey Sean/Chris

    Really like the solutions using Sass’s unitless function, but found that if the context is an em value then it still returns an px value, instead of an em value

    I fixed that for me by checking for the em unit for context first, then stripping the units before doing the calculations then converting the final result to em.

    Here is an pen to illustrate the difference:

    • User Avatar
      Joel Farris
      Permalink to comment#

      Hey Jero, I read your comment and wanted to make sure I would avoid the issue you’re describing but wasn’t sure how to recreate it.

      Tried following your example but was still confused.

      Can you help me understand why px were being returned in some cases?

  5. User Avatar
    Tim Basiliere
    Permalink to comment#

    When attempting to implement this into a bootstrap template, I ran into this issue: Error: Undefined operation: “1rem times 1.25”.

    Here’s the code that triggered the issue:

    $font-size-context: 15;
    $font-size-base: rem($font-size-context);

    As a solution, I found this revised version of the function fixes it:

    @function rem( $pixels, $context: $font-size-context ) {
    @return ($pixels/$context)*1rem;
    }

    Just a heads up to anyone else who may have this issue, since I hit my head for awhile trying to figure out what was going on. Thanks for the em() idea!

Submit a Comment

Posting Code

You may write comments in Markdown. This makes code easy to post, as you can write inline code like `<div>this</div>` or multiline blocks of code in triple backtick fences (```) with double new lines before and after.

Code of Conduct

Absolutely anyone is welcome to submit a comment here. But not all comments will be posted. Think of it like writing a letter to the editor. All submitted comments will be read, but not all published. Published comments will be on-topic, helpful, and further the discussion or debate.

Want to tell us something privately?

Feel free to use our contact form. That's a great place to let us know about typos or anything off-topic.

icon-anchoricon-closeicon-emailicon-linkicon-logo-staricon-menuicon-nav-guideicon-searchicon-staricon-tag