Typography

The _typography.scss file contains the rules to set the whole typography system of your web project.

Typography is arguably the essential part of a website. When we think about the content of a web page, we think about words.

Guide content:

  1. 📝How to set Typography using the Framework
  2. 🚀How to use the Typography Editor

Font family #

The first step of setting your typography system is choosing the font families you want to use. Font families are stored in CSS variables so that it's easier to assign and update them:

:root, body {
    /* font family */
    --font-primary: sans-serif;
    --font-secondary: serif;
}

Type scale #

A modular scale is a set of values obtained from a base value and a ratio (or multiplier). You can apply the scale to any measurable element (margin, paddings, etc.). In the Spacing docs page, we explain how to create a spacing scale. In this case, we're applying it to typography, hence the "type scale".

To control the scale, we’ve defined two variables: the --text-base-size and the -- text-scale-ratio. The first one is the body font size, while the second one is the ratio used to generate the scale. The default value of --text-base-size is 1em.

When applied to the <body>, 1em equals to 16px in most modern browsers. Since our framework is mobile-first, we’re saying: on small devices, I want the body text size to be ~16px (the font-size of the body is equal to the --text-base-size).

The scale values are obtained combining a unit value equal to 1em with the scale ratio:

:root, body {
    /* body font size */
    --text-base-size: 1em;

    /* type scale */
    --text-scale-ratio: 1.2;
    --text-xs: calc((1em / var(--text-scale-ratio)) / var(--text-scale-ratio));
    --text-sm: calc(var(--text-xs) * var(--text-scale-ratio));
    --text-md: calc(var(--text-sm) * var(--text-scale-ratio) * var(--text-scale-ratio));
    --text-lg: calc(var(--text-md) * var(--text-scale-ratio));
    --text-xl: calc(var(--text-lg) * var(--text-scale-ratio));
    --text-xxl: calc(var(--text-xl) * var(--text-scale-ratio));
    --text-xxxl: calc(var(--text-xxl) * var(--text-scale-ratio));
}

body {
    font-size: var(--text-base-size);
}

The reason why a modular scale is useful when applied to anything in a design system is that it generates a harmonious set of values, as opposed to setting each value independently.

Note that in defining each text size variable we multiply 1em by the --text-scale-ratio. That 1em is not the --text-base-size value. You could set a --text-base-size different from 1em (while you shouldn’t change the 1m in the calc() function).

Since the Em unit is a relative unit equal to the current font size, if we update the --text-base-size variable at a specific media query, we update the font-size of the body, and, with a cascade effect, all the text size variables. The whole typography is affected.

@supports(--css: variables) {
    @include breakpoint(md) {
        body {
            --text-base-size: 1.25em;
        }
    }
}

The paragraph element inherits the base font size, while we set a specific font size for each heading element. Besides, we create some utility classes in case, for example, we want to apply the --text-xxl font size to an element that is not an <h1>.

body {
    font-size: var(--text-base-size);
}

.text--xxxl {
    font-size: var(--text-xxxl);
}

h1, .text--xxl {
    font-size: var(--text-xxl);
}

h2, .text--xl {
    font-size: var(--text-xl);
}

h3, .text--lg {
    font-size: var(--text-lg);
}

h4, .text--md {
    font-size: var(--text-md);
}

.text--sm, small {
    font-size: var(--text-sm);
}

.text--xs {
    font-size: var(--text-xs);
}

Why including the type scale in your CSS? In one word: control.

Say we want to increase the body font size at a specific media query; for example, we increase the --text-base-size to 1.25em past 1024px. The heading elements are all affected by the change (the 1em in the calc() function is no longer ~16px, but ~20px); therefore they all become bigger. Let’s suppose we feel like increasing the size of the <h1> element even more. How do we do that?

One option would be increasing the --text-base-size value, but it’s not ideal if I want to target specifically the <h1>, preserving the size of the body text. Here’s the advantage of storing the -- text-scale-ratio in a variable. We can edit it and affect everything but the body text:

@supports(--css: variables) {
    @include breakpoint(md) {
        body {
            --text-scale-ratio: 1.25;
        }
    }
}

With this technique, you can manage the size of all your text elements by editing only two variables. Not just that, you can take advantage of the Em unit and modify all margins, paddings, and spacing in general by editing the -- text-base-size at a root level.

type scale variables

Important: while the variables defined on the :root level are supported in all browsers, changes to the --text-base-size and --text-scale-ratio at specific media queries are visible only in browsers that support CSS variables. The reason being that the postcss-css-variables plugin skips code wrapped in a @support feature query, not generating any fallback. The alternative would be generating tons of extra CSS code (the plugin would create a media query for each selector where a text variable is being used). This is why we decided to consider these changes as enhancements. The content in old browsers remains accessible. For more info on how to deal with CSS Variables in our framework, check our Globals page.

text-component #

The .text-component is a class to be applied to any block container of typography elements. It takes care of 1) vertical rhythm and 2) styling inline elements.

Typography vertical rhythm in our framework relies on the combo of two powerful forces:

  1. The global spacing system.
  2. The line-height and vspace (vertical space) multipliers set on a component level.

Since we already have a spacing scale set in our _spacing.scss file, we can set margins within our .text-component class by applying the spacing variables.

Because our space values are in Ems, the spacing updates automatically according to the size of the element it's applied to, and is affected by changes to the --text-base-size.

To push the control one step further, we defined two multipliers that affect line-height and margins: 1) --line-height-multiplier and 2) --text-vspace-multiplier.

These two variables control the spacing of your text component. In the example below, try editing their values and see how vertical rhythm changes accordingly.

Images in a text component #

The framework includes a set of classes that can be applied to images to float them, or even offset them past a specific media query.

Editing text size on a component level #

How do we edit text size on a component level? If you want to affect a component and all its child elements, you can target the component and specify the font-size in Ems:

.component {
    font-size: 1.2em;
}

This change will affect the whole component, in all media queries. It’s like saying: “I want all text elements of this component to be 120% of what they would normally be”.

If you want to target a specific element inside the component, you have the following options:

.component h1 { 
    font-size: 3em; /* the size is no longer affected by the --text-scale-ratio */
    font-size: calc(var(--text-base-size) * 3); /* use base size to create new value - off scale */
    font-size: calc(var(--text-xxl) - 0.8em); /* use original font size to create new value - off scale */
    font-size: var(--text-xl); /* update the value of the font size using a different variable */
}

The first option allows you to set the size of the <h1> as multiple of the --text-base-size (in this example x3); probably not ideal, because the size of the <h1> is no longer affected by the --text-scale-ratio variable. The second and third options set a new size based on either the --text-base-size or another text-size variable. The last option consists in reassigning the variable. The new size obtained still belongs to the type scale and is affected by the --text-scale-ratio.

Themes #

If you set the color of your text elements correctly, they will look fine regardless of the theme in use. We talk more about themes in the colors article. What's important is setting a high contrast color (either --color-contrast-high, or --color-contrast-higher) for text elements.

Line-height crop #

It’s a natural behavior of text elements to include some space on top (and bottom) of the element itself based on its line-height value. In some cases, you may need to remove those spaces as they can create inconsistencies in your page design.

You can achieve that using the lhCrop mixin (_mixins.scss) together with the --font-primary-capital-letter variable. 

.text-to-crop {
  @include lhCrop(1.2); /* line-height: 1.2 */
}
line-height crop

Important: for the lhCrop mixin to work properly, you need to edit the --font-primary-capital-letter variable. Because the value of this variable varies according to the font in use, you need to set a different capital-letter variable for each font you use if you want to crop its line-height.

We explain the logic behind the lhCrop mixin in this thorough article on our blog.

Credits to Kevin Powell for introducing the idea of using a pseudo-element with a negative margin to crop the line-height.

How to use the Typography Editor #

The Typography Editor is a tool that helps you to set the typography of your web project based on the CodyHouse Framework. You can use it for font pairing, to generate the type scale and set responsive rules.

✅ If you save the changes, you're extending the typography changes to all the CodyHouse components as well.

Set the general typography rules: #

How to create and edit the typography scale: #

How to use the Line-height Crop tool: #