The elements of responsive typography
Tags: featuredposts, css, webdev
Published: 2019-10-24 14:49:46

---
title: The elements of responsive typography
published: true
date: 2019-10-24 13:00:28 UTC
tags: Featured Posts,css,webdev
canonical_url: https://blog.logrocket.com/the-elements-of-responsive-typography/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/qmqqjm59exo3irf7qaq4.png 
---
**Written by [Jessica Chan](https://blog.logrocket.com/author/jessica-coder/)**✏️

Coding responsive typography can be quite confusing. What units are you supposed to use for font-size: px, em, rem, vw? How can you make sure your fonts are accessible? And what is fluid typography all about?

This article will walk you through the basics of sizing your fonts in CSS and look at a few different approaches you can use in your own website projects.

### What are some best practices for writing font styles in CSS?

Let’s address some basic guidelines that will make your fonts look good on all devices and ensure they’re accessible for users who either need to zoom in or change their browser’s base font size.

**Use relative units — not absolute — for fonts**

Absolute units are static and their resulting sizes are not dependent on anything else. In CSS, the default unit we might turn to is the pixel (px) unit.

A font sized at `18px` will be 18 pixels, no matter what device or screen you’re viewing it on. And pixels are familiar and pretty easy to understand. We know that 10px type is tiny, and 100px type is a giant headline.

It might seem tempting to use pixels for `font-size`, but it’s not recommended for accessibility reasons.

This is because even though zooming in will make pixel sizes greater, changing the base font size in the browser settings will have no effect. So pixels are not a fully accessible solution for fonts.

Relative units are measurement units that are dependent on other factors, such as the viewport width, viewport height, or a parent element.

There are several relative units: percents (%), em, rem, viewport width (vw), and viewport height (vh).

**Percentage (%) units**

Percentage units are simply a percentage of the parent element. If you have a `<div>` that is set to `width: 50%`, it will be half the width of its parent.

For fonts, setting `font-size: 100%` will make your text 100% of the base font size set in the browser. Most browsers default to a `16px` base font size, so 100% of that would be `16px`, and 50% would be `8px`.

It’s generally considered [best practice](https://alistapart.com/article/howtosizetextincss/) for accessibility to set your base `font-size` to 100% on the `<html>` element, and use relative units to handle all other `font-size` rules in your CSS.

This allows users to change the base font size in their browser, and have control over the font sizing for webpages.

**Em units**

The em unit is a holdover from the days when typography involved actual metal type blocks and printing presses. According to [Wikipedia](https://en.wikipedia.org/wiki/Em_(typography)), “one em was traditionally defined as the width of the capital M in the current typeface and point size.”

So the actual name “em” seems to derive from the phonetic pronunciation of the letter M.

Since 1 em unit is based on whatever the current font size is, this means that text sized at `1em` won’t always be the same resulting pixel size on the screen.

How that translates into CSS is that any text that is sized using em units will be dependent on the `font-size` of its parent. For example, if you have set your `<html>` to have `font-size: 100%` (resulting in `16px`), a child element with `font-size: 2em` will end up being `2 * 16` or `32px`.

One downside of using em units is that you might encounter unexpected behavior if you happen to nest font sizes.

Consider if you have your base font set to `16px`, a child element set to `1.5em` (which ends up being `24px`), and then a paragraph element as a child of _that_ element set to `0.75em`.

You might think the paragraph would be `0.75 * 16px` or `12px`. But that `0.75em` will be relative to the paragraph’s parent set at `24px`, so the resulting font size will actually be `0.75 * 24px` or `18px`.

**Rem (root em) units**

You can avoid this nesting problem by using the rem (root em) unit.

As the name implies, this will behave like the em unit in that it is relative. But instead of being relative to the element’s parent, a font sized using rem units will always be relative to the root element of the website, which usually means the `<html>` element.

If you set your root font size to `100%` or 16px, you can be certain that any other fonts using rem units will be relative to that `16px`.

You can see em and rem units in action in a Codepen demo I’ve created [here](https://codepen.io/thecodercoder/pen/YzKgLYZ).

**Viewport units**

Viewport units are relatively new to the scene, and they can add an extra level of responsiveness to CSS.

They include viewport width (vw) and viewport height (vh). What these units measure is your actual device width and height.

Let’s say you have an iPhone 8, with a device width of 375 pixels and height of 667 pixels.

For this iPhone, `100vw` is 100% of the viewport width, which would be `375px`, and `50vw` is 50% of the viewport width, or `187.5px`.

The exciting part of using viewport units in relation to font sizing is that they’re inherently responsive, as the viewport changes with different devices.

If you set text to be `font-size: 4vw` this translates to 4% of your viewport width.

On that iPhone it will result in a `font-size` of `15px`. If you have a tablet that’s `768px` wide, you’ll end up with a `font-size` of `30.72px`, and on a desktop screen of `1440px` wide your text will display at `58px`.

You might have already identified a problem with the usage of viewport units for fonts.

Having some paragraph text that is nice and readable at `15px` on mobile phones will quickly become way too large at almost `60px` for desktop widths.

And there is another drawback to using viewport units for fonts. The fact that they are relative to the device size, not the base `font-size,`means that they will not be affected by changing the base `font-size` in the browser.

Attempting to make text bigger by zooming in on the browser window may not go as expected, since the viewport itself changes when you zoom in or out.

You can see for yourself how text sized with viewport units behaves in a Codepen demo [here](https://codepen.io/thecodercoder/pen/JjPzZQZ).

However, as we’ll see later on, we can still harness the inherently responsive viewport unit for font sizing in some fashion.

### Different approaches to responsive typography

Now that we have a baseline for how font sizing works in CSS, let’s look at a few different approaches we have for creating responsive text for the web.

We’ll look at the traditional responsive approach, as well as two different fluid approaches. Any of these three are fine solutions to responsive typography, depending on your own preferences and design requirements.

### Approach #1: Responsive typography

Responsive design is the practice of writing CSS styles in a way that displays the website correctly on all device widths.

Whether you’re using a mobile phone, tablet, or ultra-wide monitor, the website should look good and be readable, even if it doesn’t look exactly the same on all devices.

Using media queries to target different breakpoint widths and writing different sets of CSS at different breakpoints are at the core of responsive design.

For example, you could build a 2-column layout on desktop, and have it stack to a 1-column layout for tablet and mobile.

For typography, we can use media queries to make the font-size smaller on mobile devices, and bigger on desktop devices:

```jsx
html {
  font-size: 100%;
}

h1 {
  font-size: 2.25rem; // 36px
}

@media (min-width: 700px){ 
  h1 { 
    font-size: 3.5rem; // 56px
  }
}

@media (min-width: 1000px){   
  h1 {
    font-size: 4.75rem; // 76px
  }
}
```

In this example, `h1` text will be `2.25rem` (`36px`) for devices under 700px wide, `3.5rem` (`56px`) for devices between 700px and 1000px wide, and `4.75rem` (`76px`)for devices greater than or equal to 1000px wide.

With this approach, we’re using a base `font-size` of `100%`, and relative font size units with rems, so the website will be accessible. The user can increase the size of the displayed content by adjusting their browser font size setting, or zooming in.

This is the standard responsive approach, which is accessible. The standard responsive approach also makes it pretty easy to understand how the fonts will behave by looking at the CSS styles.

I’ve created a simple working demo of responsive typography [here](https://thecodercoder.github.io/responsive-typography/responsive-typography.html).

**Potential issues**

One problem that comes with using media queries for your CSS styles is that it’s not completely future-proof.

While the majority of current phones are at most `414px` wide and tablets are at most `800px` wide, we can safely assume that those device widths will increase as technology advances.

So our breakpoints will probably need to be updated at some point. (This isn’t so much of a practical issue as it is one of principles.)

It would be nice if we could write styles that more elegantly scale up as the device width increases — and if it could scale fluidly no matter the device width without being so dependent on media queries.

If the styles scaled fluidly, text would be slightly larger on larger phones compared to smaller phones and larger on desktop screens compared to small laptop screens.

Let’s see how this next approach tackles that problem.

### Approach #2: Fluid typography

Fluid typography refers to a way of sizing your fonts so that they scale up as the viewport width increases. And they don’t necessarily require media queries!

The two main components of fluid typography are:

- The `calc()` [CSS function](https://developer.mozilla.org/en-US/docs/Web/CSS/calc), which allows you to perform mathematical operations using a combination of different CSS units
- Viewport units, specifically the viewport width (`vw`) unit

As we mentioned earlier, viewport units by themselves will quickly get out of hand in how much it will scale up and down.

But what if we combined vw units with rem units, using `calc()`? Using this function, we can create a rule like this:

```jsx
h1 {
  font-size: calc(1.3rem + 3.6vw);
}
```

This calculated value will be at minimum `1.3rem` or about `21px` if the viewport was hypothetically zero (which wouldn’t happen in the real world). Then the `3.6vw` value will add a rate of growth that we can control to be more gradual than just viewport units by themselves.

At a viewport width of `414px`, `3.6vw` will be 3.6% or about `15px`. So the calculated `font-size` will be `21px + 15px`, or about `36px`. At a device width of `1440px`, `3.6vw` will end up being about `52px`, so the `font-size` will be `21px + 52px` or about `73px`.

This combination of `calc()` and viewport units is a really powerful way to write fonts that size themselves to any scale. And we know this solution will be accessible by setting `html { font-size: 100% }` and using rem units as part of the calculation.

You can check out a working demo of fluid typography [here](https://thecodercoder.github.io/responsive-typography/fluid-typography.html).

And if you’re interested in further reading on this topic, Chris Coyier talked about some potential use cases for mixed unit formulas in [CSS Tricks](https://css-tricks.com/a-couple-of-use-cases-for-calc/), and Mike Riethmuller [applied this](https://www.madebymike.com.au/writing/precise-control-responsive-typography/) to calculating `font-size`.

### Potential issues

As we mentioned earlier, there are some accessibility issues with viewport units since they are not affected by changing the browser base font size. Although rem units themselves are affected, this will be offset by the viewport units in the calculation.

What this means is that the larger you set the `vw` portion of the `calc()` function, the greater that offset will be. But you’ll generally be all right if you set the rem portion of the function to `1rem` or greater.

**Getting more specific control with media queries**

In addition, there is a degree of approximation when using this calculation function for your font sizing. By its very nature, it will continue to scale the `font-size` up as screen sizes increase, and will scale down as the device width decreases.

This may be fine for most cases, but may not always be desirable. If you need to add specific limits on how small or large the `font-size` ends up being, you’ll unfortunately have to add back media queries. That way you can force the minimum and maximum `font-size` values for the smallest and largest breakpoints, and scale fluidly between those widths.

Here’s an example of limiting the font to be no less than `36px` and no greater than `76px`:

```jsx
h1 {
    font-size: 2.25rem; // 36px
}

@media (min-width: 414px){
  h1 {
    font-size: calc(1.3rem + 3.6vw); // fluidly scale
    }
}

@media (min-width: 1440px){
  h1 {
    font-size: 4.75rem; // 76px
  }
}
```

If you choose this approach, you may have to play around with the rem and vw values to try to get close to `36px` at `414px`, and `76px` at `1440px`. Personally I like the approach without media queries, since it lets you have fluidly responsive font sizing with a single CSS rule.

If you really need to have an exact amount of control over the font sizing at specific breakpoints, this last approach will give you that. Let’s check it out!

### Approach #3: Fluid typography with “locks”

This method is similar to the previous approach, using `calc()` to calculate the `font-size`. It adds some math to ensure that the `font-size` will be exactly what we want at both the minimum and maximum breakpoints.

The downside, as we shall soon see, is that this exactness requires a more complex mathematical formula than simple addition. We’ll walk through how this works, and I’ll leave it up to you to decide if it’s a viable option for your projects.

Our previous approach used `calc(1.3rem + 3.6vw)`, which will scale up as the viewport width increases, and scale down as it decreases.

What we want to do now is make the resulting `font-size` be exactly `36px` at a viewport width of `414px`, and exactly `76px` at a viewport width of `1440px`.

**The formula, using a range of viewport widths and font sizes**

To accomplish this, we’ll use a mathematical formula first written about in 2012 (as far as I could tell) by Tim Brown in a [blog post](https://tbrown.org/notes/2012/02/03/molten-leading-or-fluid-line-height/) to calculate `line-height`. [Mike Riethmuller](https://www.madebymike.com.au/writing/precise-control-responsive-typography/) and others then applied it to `font-size`.

Tim later published a follow-up explanation using a [canal locks analogy](https://blog.typekit.com/2016/08/17/flexible-typography-with-css-locks/) in 2016.

Here is the formula in the `font-size: calc()` function that we’re using:

![](https://blog.logrocket.com/wp-content/uploads/2019/10/minimum-font-size-nocdn.png)<figcaption id="caption-attachment-8662">The parts of the formula: minimum font-size, the “viewport multiplier,” and the font-size difference.</figcaption>

Let’s walk through the three sections of the formula.

The far left section starts off with the minimum font-size. This ensures that the resulting font-size will be at least that value, if not more.

Then, ignoring the middle section for now, we can jump to the last section on the far right to see the difference between the maximum font-size and the minimum font-size.

If we add this difference to the minimum font-size, we will end up with the maximum desired font-size.

Now let’s look at the more complicated looking middle section, which uses viewport widths. This is multiplied by the font-size difference.

If this multiplier is equal to 0, the resulting formula will be equal to the minimum font-size. And if the multiplier is equal to 1, the resulting formula will be equal to the maximum font-size.

This might be easier to see when we plug in actual values for each part of the formula.

Let’s plug in values and assume the current viewport width will be equal to the minimum viewport width, 414px. (We’ll use pixels first to more easily parse the math, then at the very end we’ll convert to rems)

![36 pixels](https://blog.logrocket.com/wp-content/uploads/2019/10/36px-nocdn.png)

When we do the math, we end up with **36px + (0 \* 40px) = 36px**. This is exactly what we want — at a viewport width of 414px, we want the font to be 36px.

Now let’s assume the current viewport width will be equal to the maximum viewport width, 1440px:

![](https://blog.logrocket.com/wp-content/uploads/2019/10/1440px-nocdn.png)

After we do the math, we’ll end up with **36px + (1 \* 40px) = 76px**. Again, this is what we want — at the viewport width of 1440px, the font will be 76px.

Converting our pixels to rems now, the final `font-size` rule will look like this:

```jsx
h1 {
  font-size: calc(2.25rem + (((100vw - 20rem) / (90 - 20))) * (4.75 - 2.25));
}
```

Note that we need to remove units from everywhere except the initial **minimum font-size** and the numerator of the viewport multiplier in order for the `calc()` function to work correctly.

**Adding media queries to limit the minimum and maximum font-size**

Since the formula will calculate the `font-size` between our breakpoints of `414px` and `1440px`, we need to use media queries to force our minimum and maximum `font-size` values below `414px` and greater than `1440px`.

This is similar to what we did in the previous approach, and it’ll look like this:

```jsx
h1 {
    font-size: 2.25rem; // 36px
}

@media (min-width: 414px){
  h1 {
    font-size: calc(2.25rem + (((100vw - 20rem) / (90 - 20))) * (4.75 - 2.25)); // fluidly scale
    }
}

@media (min-width: 1440px){
  h1 {
    font-size: 4.75rem; // 76px
  }
}
```

You can also make this into a Sass mixin for convenience:

```jsx
@mixin calc-font-size($min-vw, $max-vw, $min-font-size, $max-font-size){
    font-size: calc(#{$min-font-size}rem + ((100vw - #{$min-vw}rem) / (#{$max-vw} - #{$min-vw})) * (#{$max-font-size} - #{$min-font-size}));
}

h1 {
  font-size: 2.25rem;
}

@media (min-width: 414px){
  h1 {
    @include calc-font-size(25.875, 90, 2.25, 4.75);
  }

@media (min-width: 1440px){
  h1 {
    font-size: 4.75rem;
  }
}
```

You can check out a live demo of this fluid typography formula [here](https://thecodercoder.github.io/responsive-typography/fluid-typography-locks.html).

This last fluid typography approach gives us the highest level of control over how the `font-size` scales up.

However, it’s quite possible that this may be more complex than need be. There’s a non-insignificant amount of time needed to fully understand the math involved, which may add to frustration for a developer seeing this CSS rule for the first time.

### Conclusion

If I had to choose one approach for my own web development needs, I would probably choose the simpler fluid approach with no media queries.

In my opinion, it’s hard to beat being able to set your `font-size` with just a single line of CSS:

```jsx
h1 {
  font-size: calc(1.3rem + 3.6vw);
}
```

Even though there are no explicit minimum or maximum limits on the `font-size`, as long as you lean more on the rems as opposed to the vw portion of the `calc()` statement, you won’t end up with font sizing that’s too small or too large.

I hope that this article has helped explain some possible ways to handle responsive and fluid typography.

Which approach did you like the best? Leave a comment with your thoughts below.
* * * 
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/the-elements-of-responsive-typography/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [The elements of responsive typography](https://blog.logrocket.com/the-elements-of-responsive-typography/) appeared first on [LogRocket Blog](https://blog.logrocket.com).