Easy Responsive Typography
Tags: typography, sass, css, webdev
Published: 2020-02-18 19:24:56


The principals that govern typographic design on the printed page don't always transfer to the digital screen. In web development, we don't need to worry about maximizing words per page or minimizing total page count. The economies of the printed page aren't relevant on the web. For digital developers and designers, the possibilities are nearly endless.

And therein lies the problem. Our vast range of possibilities are also available to our readers (like you, right now), who can read text on a smartphone, tablet, laptop, or an ultrawide 8K display in vertical orientation. Whereas the printed page is invariably defined (text is typeset after all), the digital viewport is a shapeshifting specter, forever haunting our designs.

> How do you ensure that your text is comfortable to read on any viewport?

Thankfully, solutions exist to scale the font size of your design in relation to changes in viewport size (and I'm indebted here to posts on the subject by [Geoff Graham](https://css-tricks.com/snippets/css/fluid-typography/) and [Michael Riethmuller](https://www.smashingmagazine.com/2016/05/fluid-typography/)). Generally speaking (and perhaps counterintuitively), font size should increase as the viewport increases, since there is more room on  larger screens, and the user is likely sitting further away from it, compared to a phone.

Below I offer three solutions. Though each accomplishes the goal of increasing font size based on viewport width, I recommend the third option as being the best (it's also implemented on this website):

1. Responsive Typography (easiest to get up and running)
2. Fluid Typography (implements continuous font-size scaling)
3. Fluid Typography via SASS mixin (easiest implementation for fluid scaling)
4. Fluid Typography via JavaScript (accessibility friendly solution)

## Responsive Typography
Responsive typography, which defines different font sizes based on media queries, is the easiest solution to implement and has the widest browser support. First, define your minimum desired font size (16px recommended) via CSS on your html selector. Then, increase your font size for each media query breakpoint. See the example below for an illustration.

```css
/* Define minimum font size */
html {
 font-size:16px;
}

/* Define breakpoints and font sizes */
@media (min-width:640px) and (max-width:1023px) {
 html {
  font-size: 17px
 }
}

@media (min-width:1024px) and (max-width:1279px) {
 html {
  font-size: 18px
 }
}

/* Define max font size and viewport */
@media (min-width:1280px) {
 html {
  font-size: 19px
 }
}
```

By altering the root font size, we can use the rem CSS unit to alter the size of other typographic elements. For example, for an H1 tag with a size of 2.5rem, it would have the following sizes according to the CSS rules above.

| Viewport Width |< 640 px | > 639px | > 1023px | > 1279px |
| --- | --- | --- | --- | --- | --- |
| Base Font Size | 16px | 17px | 18px | 19px |
| H1 Font Size | 40px | 42.5px | 45px |  47.5px |

## Fluid Typography
Fluid typography expands on responsive typography by implementing a formula to scale text in relation to the size of the viewport. Rather than changing font size in discrete steps according to media query breakpoints, as seen above, fluid typography sets font size along a continuum in relation to the width of the viewport.

To achieve this result, it is necessary to define a formula in CSS that is capable of dynamically updating the font-size property. The key to this formula is the CSS unit "vw" or viewport width.

The formula is as follows: `min font size + (max font size - min font size) * (100vw - min vw) / (max vw - min vw)`

For example, given the following arguments of 16px min font size, 23px max font size, 640px min vw, and 1440px max vw; the formula would yield the following results:

| Viewport Width | Base Font Size |
| --- | --- |
|< 640px | 16px |
| 700px | 16.5167px |
| 750px | 16.95px |
| 1200px | 20.9px |
| > 1440px | 23px |

To implement the formula in CSS, you'd write the following rules:

```css
/* Define minimum font size */
html {
    font-size: 16px;
}

/* Fluid typography formula */
@media (min-width: 640px) and (max-width: 1439px) {
    font-size: calc(16px + (23 - 16) * (100vw - 640px) / (1439 - 640));
}

/* Define max font size */
@media (min-width: 1440px) {
    font-size: 23px;
}
```

## Fluid Typography via SASS Mixin
If you're using SASS/SCSS, you can implement the above solution using a mixin. The advantage of using a mixin is the ability to update variables easily and to generate the media query breakpoints automatically. The mixin and its use is shown below.

```scss
/* Define the mixin */
@mixin fluid-typography($minFont,$maxFont,$minBreakpoint,$maxBreakpoint) {
  
  /* Define variable for media query */
  $maxLessOne: $maxBreakpoint - 1;
  
  /* Define variable for fallback */
  $avg: ($maxFont + $minFont) / 2;
 
  /* Base font size */
  font-size: #{$minFont}px;
  
  @media (min-width: #{$minBreakpoint}px) and (max-width: #{$maxLessOne}px) {
    
    /* Adds a fallback for unsupported browsers */
    font-size: #{$avg}px;
      
    /* The fluid typography magic 🌟 */
    font-size: calc(#{$minFont}px + (#{$maxFont} - #{$minFont}) * (100vw - #{$minBreakpoint}px) / (#{$maxBreakpoint} - #{$minBreakpoint}))
  }
  
  @media (min-width: #{$maxBreakpoint}px) {
    font-size: #{$maxFont}px;
  }
}

/* Generate the CSS */
html {
  
  /* Just add your arguments */
  @include fluid-typography(16,25,300,1500);
}
```

See it in action. Remember to resize your browser to see the changes 👉
{% codepen https://codepen.io/royalfig/pen/jOPEgvQ %}

## Fluid Typography via JavaScript

The previous solution is limited in terms of accessibility, since setting font size in pixels will overwrite user-defined values. For example, if a user has set the font size in the browser to be 22px, the above solution would overwrite that value with 16px, which is undesirable.

Unfortunately, the way calc works in CSS doesn't allow for using a rem-based solution. You could use the responsive solution, the first example above, but then you lose fluidity. With JavaScript, though, you can preserve font-size fluidity, user-defined preferences, and aspects of your design.

The constructor defined below works by taking four arguments, the same as those above: . Then, if the user has not set a font-size preference, it implements those sizes on the DOM. If the user has set a preference, then the function implements the same same ratio you defined between the smallest and largest font sizes. For example, if you defined your smallest font size as 16px and the largest as 23px, then that represents a  43.75% increase. If a user defines a minimum font size of 22px, then the function will calculate the rem of the page based on the user supplied minimum and a 43.75% increase. For 22px, that would mean a maximum of 31.625px. The function is below.

```javascript
class FluidTypography {
  constructor(minVW, maxVW, minFontSize, maxFontSize) {
    this.minVW = minVW;
    this.maxVW = maxVW;
    this.minFontSize = minFontSize;
    this.maxFontSize = maxFontSize;
    this.maxRem = this.computeRem().maxRem;
    this.minRem = this.computeRem().minRem;
  }

  // Compute the maxRem based on arguments and user's browser preferences
  computeRem() {
    const body = document.documentElement;
    const properties = window.getComputedStyle(body);
    const baseFontSize = properties.fontSize.replace(/px/, '');
    // Gets the max font size of either the browser or the dev
    const max = Math.max(this.minFontSize, baseFontSize);
    const relativeMax = (this.maxFontSize * max) / this.minFontSize;
    const maxRem = relativeMax / baseFontSize;
    const minRem = max / baseFontSize;
    return { maxRem, minRem };
  }

  // Calculate font size based on arguments and user's browser preferences
  fontSize() {
    const width = document.documentElement.offsetWidth;
    let rem = this.minRem;

    if (width > this.minVW && width < this.maxVW) {
      rem =
        this.minRem +
        ((this.maxRem - this.minRem) * (width - this.minVW)) /
          (this.maxVW - this.minVW);
    }

    if (width > this.maxVW) {
      rem = this.maxRem;
    }

    document.documentElement.style = `font-size: ${rem}rem`;
  }

  resizeHandler() {
    this.fontSize();
    window.addEventListener('resize', this.fontSize.bind(this));
  }
}

new FluidTypography(640, 1280, 17.5, 22).resizeHandler();
```

👉 See it in action. Remember to resize the browser to see changes.
{% codepen https://codepen.io/royalfig/pen/rNVWzXm %}

This post first appeared on [my blog](https://ryanfeigenbaum.com/easy-responsive-typography/)