CSS Font Size (font-size)

Viewport Units

There are values in CSS that are for sizing things in relation to the viewport (the size of the browser window). They are called viewport units, and there are a number of them that do slightly different (all useful) things. One unit is 1% of one of the axes of the viewport. These can be useful for responsive design, that is, designing websites that look good across different screen sizes, taking advantage of the space available to them.

There are many things you can do with viewport units, but let's look at one in particular: typography.

Discussion

One unit is 1% of the viewport axis. Viewport == browser window size == window object. If the viewport is 40cm wide, then 1vw == 0.4cm.

For use with font-size, I guess it's one character that takes on that size, but as we know, in non-mono-spaced fonts the width of a letter is rather arbitrary. I find you just need to tweak around with the values to get it how you want it.

  • 1vw = 1% of viewport width
  • 1vh = 1% of viewport height
  • 1vmin = 1vw or 1vh, whichever is smaller
  • 1vmax = 1vw or 1vh, whichever is larger

An Example

Here is an easy example:

h1 {
  font-size: 5.9vw;
}
h2 {
  font-size: 3.0vh;
}
p {
  font-size: 2vmin;
}

Not just font-size

These are just units. Just like em, px, and so on. You can use them on anything, not just font-size.

I think font-size is the most compelling use case, since things like margin, padding, and width can already essentially react to browser window size by using % units.

Still, there is the case where perhaps a more deeply-nested element needs to react to the browser window size instead of its direct parent size.

Simplified Fluid Typography

Fluid typography is the idea that font-size (and perhaps other attributes of type, like line-height) change depending on the screen size (or perhaps container queries if we had them).

The core trickery comes from viewport units. You can literally set type in viewport units (e.g. font-size: 4vw), but the fluctuations in size are so extreme that it's usually undesirable. That's tempered by doing something like font-size: calc(16px + 1vw). But while we're getting fancy with calculations anyway, the most common implementation ended up being an equation to calculate plain English:

I want the type to go between being 16px on a 320px screen to 22px on a 1000px screen.

Which ended up like this:

html {
  font-size: 16px;
}
@media screen and (min-width: 320px) {
  html {
    font-size: calc(16px + 6 * ((100vw - 320px) / 680));
  }
}
@media screen and (min-width: 1000px) {
  html {
    font-size: 22px;
  }
}

That's essentially setting a minimum and maximum font size so the type won't shrink or grow to anything too extreme. CSS locks was a term coined by Tim Brown.

Minimum and maximum you say?! Well it so happens that functions for these have made their way into the CSS spec in the form of min() and max().

So we can simplify our fancy setup above with a one-liner and maintain the locks:

html {
  font-size: min(max(1rem, 4vw), 22px);
}

We actually might want to stop there because even though both Safari (11.1+) and Chrome (79+) support this at the current moment, that's as wide as support will get today. Speaking of which, you'd probably want to slip a font-size declaration before this to set an acceptable fallback value with no fancy functions.

But as long as we're pushing the limits, there is another function to simplify things even more: clamp()! Clamp takes three values, a min, max, and a flexible unit (or calculation or whatever) in the middle that it will use in case the value is between the min and max. So, our one-liner gets even smaller:

body {
  font-size: clamp(100%, 1rem + 2vw, 24px);