Using Sass to Build a Custom Type Scale with Vertical Rhythm

James Steinbach
James Steinbach
Share

One way to achieve visual consistency in web design is to use a vertical rhythm. For a website, this would mean that no matter what font-size any text element is, its line-height is always an even multiple of a consistent unit of rhythm. When this is done precisely, you could put a striped background behind your page and each text block (paragraphs, headings, blockquotes, etc) would line up along the lines in that grid.

As you could imagine, setting this up by hand would require a lot of math and typing. If you want to change the proportions of that grid responsively, you’ll redo that work for every breakpoint. Sass, as you might expect, provides a great toolbox to automate all that work and generate a custom type scale with consistent vertical rhythm more easily.

I’ll start off by admitting that there are already some good Sass plugins that help build a custom type scale with consistent vertical rhythm. If you’re just looking to grab a pre-built chunk of code, try Typesetting, Typomatic, or Typecsset.

Why Build Our Own?

That raises a great question: With all those tools available, why should we try building our own? There are a couple of reasons. The fact that there are three different tools already means that there are different ways to solve this successfully. Additionally, building our own type scale with Sass is a great way to move beyond the basics and learn some more advanced Sass.

Before we start, let’s define what kind of type scale we’re going to build. The scale will start with a base font-size and line-height. From that font-size, it will generate several proportionally larger font-size values. This Sass code will also control the leading for all the sizes it generates, allowing us to keep the line height tied to a multiple of the base line-height.

One last detail for fun: We’ll be able to adjust the base font-size and line-height for various screen widths with a short media query mixin.

Setting Up Variables

We’ll put these variables near the top of the file because we want to organize our code by keeping all the “config” stuff in one easy-to-find place.

If we didn’t want this to be responsive, this is all we’d need for the base font-size and line-height values:

$base-font-size: 16px;
$base-line-height: 1.3;

We’ll also use a variable for the proportion by which font-size values will scale up:

$scale: 1.5;

However, we’re going to make this responsive, so a Sass map is a better way to store this information:

$bp-sizes: (
  small: (
    base-font-size: 16px,
    base-line-height: 1.3,
    scale: 1.25
  ),
  medium: (
    base-font-size: 18px,
    base-line-height: 1.4,
    scale: 1.4
  ),
  large: (
    base-font-size: 22px,
    base-line-height: 1.5,
    scale: 1.5
  )
);

We’ll also need a set of breakpoint values for media queries. You could make the $bp-sizes map more complex and add a min-width value. But in this tutorial, I’ll allow the possibility that you may already have a breakpoint plugin you like, so we’ll keep those separate. Just make sure the breakpoint labels match:

$breakpoints: (
  small: 480px,
  medium: 960px,
  large: 1200px
);

In just a moment, we’ll look at how we use the values in that map. For now, let’s move on to the final set of variables we need — the list of font sizes we plan to use.

$font-sizes: ( p, bq, ssh, sh, h, hero );

In this case, we’re using p for paragraph, bq for blockquotes, ssh for sub-sub-heading, sh for sub-heading, h for heading, and hero for hero text.

Setting up Mixins

Now that we have all the data we need in variables, it’s time to start processing it. To get the generated CSS we desire, we’ll need a mixin that creates font-size and line-height properties for each label in the $font-sizes map. Then we’ll repeat that mixin with data from each breakpoint in the $bp-sizes map, wrapping each set of styles in the appropriate media queries.

Generating font-size and line-height

Here is our mixin to generate the font-size and line-height values:

@mixin generate-font-properties($label, $base-font-size, $base-line-height, $scale) {
  $scale-value: index($font-sizes, $label) - 1;

  $fs: $base-font-size * pow($scale, $scale-value);
  $lh: $base-font-size * $base-line-height / $fs;

  @while $lh < 1 {
    $lh: $lh + $lh;
  }

  font-size: $fs;
  line-height: $lh;
}

This mixin accepts four parameters: The label of the font size (from the $font-sizes map), the base font size, the base line height, and the scale (all from $bp-sizes).

The first line of this mixin gets the index of the label in the $font-sizes map and subtracts one so that the generated font sizes increment correctly.

Note: Sass index values start the count at 1, unlike most other programming languages which start index count at 0.

The second line sets the variable $fs to the current base font-size multiplied by the current scale to the power of the scale index. In other words, if it’s the first generated font size, $fs will be the base font size multiplied by 1 (the scale to the power of 0). If it’s the second generated font size, $fs will be the base font size multiplied by scale to the first power.

Note: If you’re using Compass, pow() is a helper function; if not, you may want to include a pow() function like this one.

After this, $lh (which serves as a placeholder for the eventual line height) is set to the base vertical rhythm (font size * line height) divided by the current calculated font size. This makes the starting line height for any font size identical to the base vertical rhythm. For larger font sizes, this measurement is smaller than 1. The @while loop increases the $lh variable by one vertical rhythm unit at a time until the line height is at least 1.

Note: We’re generating a unitless line-height value so that we can use any valid font size measurements in this mixin. You can use any measurement units you like (px, rem, em, vh) in the $bp-sizes map.

Finally, this mixin outputs the font-size as the $fs value and the line-height as the $lh value.

Generating Breakpoints

We’ll also need a mixin to generate media queries for breakpoints. Here’s what I like to use:

@mixin breakpoint($name) {
  @if not map-has-key($breakpoints, $name) {
    @warn "Invalid breakpoint `#{$name}`.";
  } @else {
      @if map-get($breakpoints, $name) == '' {
        @content;
      } @else {
        @media (min-width: map-get($breakpoints, $name)) {
        @content;
      }
    }
  }
}

This mixin checks the name of the breakpoint passed to it against the list of labels in the $breakpoints map. If the name is missing, it returns an error message.

If $name is in the $breakpoints map, the next @if statement looks for a measurement paired with the name. If there is no measurement value, the styles are compiled without a media query. This allows us to set a default set of font config values in the $bp-sizes map.

If $name has a measurement, that is used as the min-width media query value. (Yes, I’m a big believer in mobile-first media queries.) All the @content styles are wrapped in that media query.

Looping through Values and Breakpoints

Now that we have a working @mixin for generating font size and line height, we need to repeat it for all values in the $font-sizes list. To do that, we’ll use an @each loop:

@each $label in $font-sizes {
  %#{$label} {
    @include generate-font-properties($label, $base-font-size, $base-line-height, $scale);
  }  
}

All that’s left is to take one more step back and wrap that loop in a loop that goes through the $bp-sizes map. This loop will also include our breakpoint() mixin to generate media queries. To get the necessary information from the $bp-sizes map, this loop will use the get-breakpoint-property() helper function. It searches $bp-sizes, finds the breakpoint size map, and returns the right property value from that map:

@function get-breakpoint-property($prop, $bp) {
  @return map-get(map-get($bp-sizes, $bp), $prop);
}

@each $size, $data in $bp-sizes {

  $bsf: get-breakpoint-property(base-font-size, $size);
  $blh: get-breakpoint-property(base-line-height, $size);
  $s: get-breakpoint-property(scale, $size);

  @include breakpoint($size) {
    @each $label in $font-sizes {
      #{$label} {
        @include generate-font-properties($label, $bsf, $blh, $s);
      } 
    }
  }
}

The loop then calls the breakpoint() mixin to generate each necessary media query. Inside that mixin, it runs the $font-sizes loop and regenerates font placeholders using the appropriate font size, line height, and scale for the current breakpoint. The output is attached to placeholder selectors, which we then extend where needed:

.page-title {
  @extend %h;
}

You can see the code in action in this CodePen:

See the Pen Sass Type Scale with Vertical Rhythm by SitePoint (@SitePoint) on CodePen.

Conclusion

In the process of creating a Sass plugin that allows us to quickly generate custom font sizes with consistent vertical rhythm and responsive adjustments, we’ve learned several important things about Sass.

We’ve used variables, lists, and maps. We’ve used mixins, extends, and loops. These are some of the most important tools in your Sass toolbox. Please share in the comments how you could use this plugin, or how you can use these tools to create other useful styles!

Frequently Asked Questions on Using SASS to Build Custom Type Scale and Vertical Rhythm

What is a type scale in SASS and why is it important?

A type scale in SASS is a progressive scale of font sizes that helps to maintain consistency and visual hierarchy in your design. It is important because it allows for a harmonious and balanced typography in your web design. It ensures that the text elements on your webpage are proportionate and visually appealing. A well-implemented type scale can greatly enhance the readability and overall user experience of your website.

How can I create a custom type scale using SASS?

Creating a custom type scale using SASS involves defining a base font size and a scale ratio. The base font size is the starting point of your type scale, while the scale ratio determines the progression of the font sizes in your type scale. You can use SASS functions and mixins to generate your type scale based on these parameters. This allows for a flexible and easily adjustable type scale that can adapt to different design requirements.

What is vertical rhythm and how can it be achieved with SASS?

Vertical rhythm is a design principle that involves the consistent spacing and arrangement of text elements along a vertical axis. It can be achieved in SASS by using a combination of line-height, margin, and padding properties. By maintaining a consistent vertical rhythm, you can create a more balanced and harmonious layout that improves readability and aesthetic appeal.

How can I vertically center a div with SASS?

Vertically centering a div with SASS can be achieved by using a combination of CSS properties such as position, top, left, and transform. You can use the position property to position the div relative to its parent element, and the top and left properties to position it at the center. The transform property can then be used to offset the div by half of its height and width, effectively centering it.

What are the benefits of using SASS for typography?

SASS offers several benefits for typography. It allows for more flexible and efficient styling with features like variables, mixins, and functions. This means you can define your typography styles once and reuse them throughout your project, making your code more maintainable and easier to update. SASS also supports mathematical operations, which can be used to calculate font sizes, line heights, and other typography-related measurements.

How does SASS syntax work?

SASS syntax is similar to CSS, but with additional features. It uses indentation to denote nesting of selectors, and supports variables, mixins, and functions. Variables in SASS are defined with a dollar sign ($), and can be used to store values that are reused throughout your stylesheets. Mixins are reusable blocks of styles that can be included in different parts of your stylesheets. Functions are used to perform calculations and manipulate values.

What are type scale tokens in SASS?

Type scale tokens in SASS are variables that store the font sizes in your type scale. They allow you to easily adjust your font sizes across your project by changing the values of these variables. This makes your code more maintainable and easier to update, as you don’t have to manually change the font size in every instance it’s used.

How can I use SASS to implement a material design typography system?

Implementing a material design typography system with SASS involves defining a type scale based on the material design guidelines, and using it to style your text elements. You can use SASS variables to store your type scale values, and mixins to apply the typography styles. This allows for a consistent and harmonious typography that adheres to the material design principles.

Can I use SASS with other CSS frameworks?

Yes, SASS can be used with other CSS frameworks like Bootstrap, Foundation, and Bulma. These frameworks often provide SASS versions of their stylesheets, which you can customize to suit your design needs. This allows you to leverage the features of SASS while also benefiting from the pre-defined styles and components provided by these frameworks.

How can I learn more about SASS and its features?

There are many resources available online to learn about SASS and its features. The official SASS documentation is a great starting point, as it provides a comprehensive guide to the language and its syntax. There are also many tutorials, articles, and online courses that cover SASS in more detail. Practice is key to mastering SASS, so try to apply what you learn in your own projects.