Authored by Primate

A rather awesome, informative
and witty blog about all things web

SASS typography maps

Since the release of SASS 3.3 and the inclusion of maps, I’ve been really keen to refactor the typography SASS file we use at Primate.

Before refactoring, the file was pretty large and repetitive, but it did provide all functions, @mixin’s, and placeholders we like to work with. I was keen to harness maps and for/each loops to take out the repetition and create a file that is easier to work with. I’d like to share what I created and briefly explain how it works.

Defining typography

Maps provide a way to group information together, and for my use case they provided a way of encapsulating information about a specific typographic style (e.x. header 1, paragraph). The definition looks like the following:

$museo_sans: (
  font_family: "museo-sans",
  font_weight: 300,
  font_style: normal
);

$header_1: (
  size_px: 42, 
  line_height_baseline: 4, 
  margin_bottom_baseline: 2,
  font_family_map: $museo_sans,
  color: white
);

The first map $museo_sans sets the base font style. It’s defined as it’s own separate map as the values are likely to be shared by a number of typographic definitions. The second map $header_1 defines all the values required for a header 1 style. Things to note are:

size_px: font-size is specified in pixels. This is purely for convenience as our Designers work with pixels. Later in the process the pixel value will be converted to em’s

line_height_baseline, margin_bottom_baseline: defined as multipliers of the site wide baseline value. Each site we work with has one universal baseline value. For example the baseline may be 10px, in which case $header_1 would have a line-height of 40px, and a margin-bottom of 20px.

font_family_map: must be passed a map with the same syntax as $museo_sans, which we defined previously.

Generating styles based on the definitions

Now that we have some definitions, lets turn them into something usable. The following each loop is used to set the groundwork for how we will use typographic styles. It generates a class selector definition, and a placeholder which we can @extend from:

$typography: (
  header_1: $header_1,
  paragraph: $paragraph
);

@each $font, $map in $typography {

  .#{$font},
  %#{$font} {
    @include generate_font_styles($map);
  }
}

We first have to create a map $typography that specifies all the styles we have created definitions for. It’s a shame we have to create this map, it would be a lot nicer to be able to use a list in the following manner:

@each $font in (header_1, paragraph) {}

This isn’t possible however as we cannot use #{} interpolation within variable names. In other words we cannot define a variable in the following way: $#{variable}: xxx;. This means that we must explicitly pass in the variable, which is why we need the $typography map.

The loop doesn’t actually do very much. Most of the work is hidden within the @mixin generate_font_styles().

generate_font_styles( )

As a warning, the following @mixin is not the easiest to read. The main reason being that the syntax used to access map values is really quite verbose, and it doesn’t lend itself to readability. Here is the generate_font_style function:

@mixin generate_font_styles($map) {

  font-family: map-get(map-get($map, 'font_family_map'), 'font_family');
  font-size: px2em(map-get($map, 'size_px'));
  line-height: calculate-em(map-get($map, 'size_px'), (map-get($map, 'line_height_baseline') * $bl_px));
  font-weight: map-get(map-get($map, 'font_family_map'), 'font_weight');
  font-style: map-get(map-get($map, 'font_family_map'), 'font_style');
  margin: 0 0 calculate-em(map-get($map, 'size_px'), (map-get($map, 'margin_bottom_baseline') * $bl_px)) 0;
  color: map-get($map, 'color');

}

In that tangles up mess of SASS we define 7 styles (font-family, font-size, font-height, font-weight, font-style, margin, and color).

The most basic definition is color, which simply refers to the color value held in the typographic definition map.

Similar, but a little more complex are the definitions for font-family, font-weight, and font-style. They must refer to a map within a map. If you remember we added a base map of values $museo_sans into our typography definition. This means that we must use the function map-get() twice. The first time to retrieve the $museo_sans map, and then a second time to retrieve the values from within it.

The definition for font-size is passed through a function named px2em(). This function simply converts the pixel value into a em value. The function looks like the following:

@function px2em ($px) {
  @return ($px / $base_font_size_px) * 1em;
}

The calculations for margin and line-height are a little more complicated. We originally defined the values for these styles as a multiplier of a site wide baseline. We must convert the multiplier into a pixel value. To do so we multiply the value against the variable $bl_px, which specifies the site wide baseline value in pixels. The calculated value is then converted to em’s which is our final value.

Using the definitions in our styles

For the most part I @extend the defined placeholders, e.x. h1 { @extend %header_1; }. It’s as simple as that, our h1 selector is now connected to our style definitions for %header_1.

I never use the class selector in my markup, so for example I would never have the following: <h1 class="header_1">My header</h1>. This would work because our styles are also defined with the .header_1 selector. I’m quite averse to having any style related class definitions in my markup, but some of my colleagues prefer to have the option to do so.

Within mixin’s

Unfortunately we cannot use @extend’s within @mixin’s, so we need another method. The styles under our placeholders were fleshed out using the function generate_font_styles(), and fortunately we can use functions within @mixin’s. Instead of using the placeholder, we can call the generate function within our @mixin. So for example:

@mixin my_mixin {

  .title { @include generate_font_styles($header_1); }
}

Using the generate function is not as efficient as using placeholders, but it provides the exact same styles as the latter. Using the generate function will duplicate style definitions within your CSS, whereas placeholders group CSS selectors instead. If this doesn’t make sense it’s worth brushing up on how @extends work.

Vertical margins

We have the option to specify a default bottom margin for each typographic style we define, but quite often when using the style we will need to alter or remove the bottom margin. Or perhaps we need to add a top/left/right margin, or padding alternative. If we do make alterations the best solution is to define values as em’s, which needs to be calculated based on the current font-size defined.

We use a handy function named typographic_spacing() to calculate em values for us. For example:

h1 {
  @extend %header_1;
  margin-bottom: typographic_spacing($header_1, 40px);
}

The first parameter for the typographic_spacing() mixing is the map associated with the definition we’re adding margin to. The second parameter is the amount of space we desire. Again we’re asking for a value using pixels (40px), but we get em values returned to us. This is simply because it’s easier to measure spaces in pixels when using photoshop or something similar.

The implementation for typographic_spacing() can be found below:

@function typographic_spacing ($map, $desired_px) {
  $variant_size_in_px: map-get($map, size_px);
  @return calculate-em($variant_size_in_px, $desired_px);
}

Summary

The previous incarnation of our typography file was not the nicest file to work with, it was repetitive and very difficult to read. It didn’t lend itself to being used by the designers in our team who occasionally had to work with it to tweak typography. Using maps has given us a designer friendly file, which I hope is more accessible to them in the future. Key/value pairs are easy to understand and read, it’s nice to extract away from the style definitions.

The implementation discussed has obviously been tailored around the way I like to work, and it’s not designed to be overly flexible. It doesn’t have to be! It might not suit you straight off the bat, but you could modify the generate_font_styles() function to output styles the way you like. For example if you don’t work with a site wide baseline, you could change how line-height or margin-bottom are defined.

View the entire file and please use it if you find it helpful, and obviously share any comments you may have.

If you liked this article then why not subscribe to our RSS feed.

Author: Chris Lamb

Chris is terrible at coming up with taglines.

Comments

Showing 2 Comments

Leave a comment
  1. Torbjørn Caspersen December 5, 2014 at 6:35 am

    Hi, and thank you.

    It’s good and interesting to see SASS used in more complex ways to create efficient workflows. I especially like that you’ve manage to output css with the flexibility of ems without the hassle of manually converting from px.

    • Chris December 5, 2014 at 10:07 am

      Thanks Torbjørn, I’m glad you enjoyed the post. We sometimes find it more intuitive to think in pixels, especially when it comes to typography. The designers at Primate think it’s great, and I’m glad to have their seal of approval.

      There is a sometimes a balance between what is efficient in SASS, and what is outputted in CSS, but in this case it feels like a win-win.

      Thanks again.

Leave a Reply