Paulund
2014-10-22 #sass #css

REM Sass Mixin With Pixel Fallback

The REM CSS unit is similar to the EM CSS unit except it allows you to size elements relative to the root of the HTML tag, as it stands for Root EM. The EM unit is relative to the font-size of the parent, which can cause problems in working out the correct size to use in descendent elements. Because the REM is relative to the root we can set the default font-size in the HTML tag which allows us to easily define the size of the other elements. Defining the html font-size as 62.5% allows us to defines REMs similar to pixels.


html { font-size: 62.5%; } 
body { font-size: 1.4rem; } /* =14px */
h1   { font-size: 2.4rem; } /* =24px */

The REM CSS unit is widely supported on most modern browsers but it's not supported in IE8 and lower browsers, for unit values to work in both modern browsers and IE8 we will need to duplicate the property in the CSS.


html 
{ 
    font-size: 62.5%; 
} 

body 
{ 
    font-size: 14px;
    font-size: 1.4rem;
}

h1   
{ 
    font-size: 24px;
    font-size: 2.4rem;
}

Instead of repeating ourself everywhere in the CSS we can use a Sass Mixin for our REM property and pixel fallback.


@mixin rem2px($property, $sizeValue: 1.6) {
  #{$property}: ($sizeValue * 10) + px;
  #{$property}: $sizeValue + rem;
}

Now we can use this in our Sass to output the same as above with the following code.


body 
{ 
    @include rem2px(font-size, 1.4);
}

h1   
{ 
    @include rem2px(font-size, 2.4);
}

This @mixin can work well for single value elements like font-size but it's not going to help when you need to have multiple values in properties like margins or padding. The following @mixin was found on Hugo Giraudel post which allows you to handle properties with multiple values.


html {
  font-size: 62.5%; /* 1 */
}

@function parseInt($n) { /* 2 */
  @return $n / ($n * 0 + 1);
}

@mixin rem($property, $values) {
  $px : (); /* 3 */
  $rem: (); /* 3 */
  
  @each $value in $values { /* 4 */
   
    @if $value == 0 or $value == auto { /* 5 */
      $px : append($px , $value);
      $rem: append($rem, $value);
    }
    
    @else { 
      $unit: unit($value);    /* 6 */
      $val: parseInt($value); /* 6 */
      
      @if $unit == "px" {  /* 7 */
        $px : append($px,  $value);
        $rem: append($rem, ($val / 10 + rem));
      }
      
      @if $unit == "rem" { /* 7 */
        $px : append($px,  ($val * 10 + px));
        $rem: append($rem, $value);
      }
    }
  }
  
  @if $px == $rem {     /* 8 */
    #{$property}: $px;  /* 9 */
  } @else {
    #{$property}: $px;  /* 9 */
    #{$property}: $rem; /* 9 */
  }
}