How I’m Dealing With Font Sizes

March 16, 2021 Updated: 3/25/2021

I have the hardest time with fonts. It’s so much which font to use, but how to size them appropriately for any given screen size. I might be particularly sensitive to this only because I often find myself bumping up text using with some ⌘ + action, or simply switching to Safari’s Reader Mode for a better reading experience. I often find that content is too small for me and I try to prevent that for old geezers like me, with degrading eyesight.

I’m thinking of context, not viewport widths

One way to think about font sizes is not only by device, but how that device is used. This isn’t prescriptive or anywhere near comprehensive, but what I mean is like:

  • Desktop/Laptop: Probably sitting (or standing) at a desk and viewing content on a large screen that’s approximately 24-36 inches from their face. Assuming good ergonomics are a virtue, the user is likely leaning back (unless they’re standing at the desk).
  • Tablet: Probably sitting, perhaps even legs up with the tablet resting on them. We’re looking at a distance that’s probably 14-20 inches from the face.
  • Phone: Let’s go with standing on this one and assume the user is standing straight (not hunched with neck down). I wouldn’t be surprised if the screen is being held somewhere between 10-16 inches from the face.

I think this way a lot because I’m constantly changing my natural posture to read content. If text is too small on my desktop, I find myself leaning forward to get a closer look. If text is too large on my phone, I find myself wishing I had Inspector Gadget-like powers to hold the screen as far away as possible. I have absolutely zero proof that this is the way you or other people feel reading content on the web, but it’s how I’ve been empathizing and what I’ve been using to base many of my font sizing decisions.

I’m bullish on clamp() for responsive sizing

Me and media queries have had a long love-hate relationship when it comes to font sizes. I love that I’m able to adjust the size when the viewport hits a certain threshold, but I hate that I find myself writing a crap ton of media queries to get fluid-like sizing.

Then came calc(). That allowed me to use a pattern that’s become popular for automatically scaling font sizes. The idea is you define a minimum font size on the <html> element, a maximum font size in a media query that’s set for a larger screen, then declare a font size in one more media query that sits between the base <html> element and the media query for large screens. This “middle” media query is where calc() — true to its name — calculates sizes between the two extremes.

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 blew my freakin’ mind when I first saw it. But then clamp() became a thing. It does exactly what that snippet is doing, but with a grand total of zero media queries.

For example:

h1 {
  font-size: clamp(24px, calc(24px * 1vw), 36px);
}

See that? Now the font stays within a range of 24px and 26 pixels. The middle value is the “ideal” size and it’s set to a number that multiplied by a responsive unit — that allows the browser to calculate font sizes within the given range.

Any time I can reduce a snippet of code by ~75% is a good thing.

I’m making variables out of every value

Custom variables are The Thing™ that I was missing from CSS that kept me reaching for Sass. I still use Sass for things like nesting and partials, but write a lot more vanilla CSS in those files than I ever used to.

I definitely use them in my font sizing stack. I start with a base font font size:

--text-size-base: 18px;

So far, so good. Then I create more sizes based on that:

--text-size-base: 18px;

--text-size-tiny: calc(var(--text-size-base) - 6);
--text-size-small: calc(var(--text-size-base) - 4);
--text-size-medium: var(--text-size-base); /* Takes the base size */
--text-size-big: calc(var(--text-size-base) + 4);
--text-size-large: calc(var(--text-size-base) + 6);
--text-size-huge: calc(var(--text-size-base) + 12);

Still good? I like having these as utilities should a situation come up where I need a specific font size but want to keep it consistent with everything else.

I’m using a variable for scaling

The thing about fluid typography with clamp() — besides lacking full browser support — is that the ideal (or middle) value needs to be a responsive unit. That way, the computed value changes depending on the container that contains the text, and we clamp it so that value never goes above or below our range.

I like basing things on viewport units (vw) only because it truly responds to the size of the browser. It’s sorta arbitrary what exact vw value to use, but I might only think that because I suck at math. I grab a nice round number, say 10vw, and make that a variable I can use as a multiplier that converts the fixed pixel value for the ideal size into a responsive unit.

--text-size-scaler: 10vw;

I like 10vw because it’s the same as saying 10% of the browser width. That’s easier to calculate for math duds like me.

I’m clamping the variables

Now, instead of raw numbers on an element, like this:

h1 {
  font-size: clamp(24px, calc(24px * 1vw), 36px);
}

…I can drop my variables in there instead:

h1 {
  font-size: clamp(var(--text-size-large), calc(var(--text-size-base) * var(--text-size-scaler)), var(--text-size-huge));
}

Now, I have a few things working for me with this setup:

  • I can reuse these variables anytime I need them.
  • I can update the variables in one place.
  • I can override any of the variables on an element if I need to tweak something for a specific use case.
  • I can enjoy a nicely-scaled font size at any screen size.

Is this the best solution? Probably not. Truthfully, I normally raise an eyebrow when I see crazy-looking calculations with subjectively-named variables. They make sense to me, but I’d have to leave detailed comments or show someone else the ropes if I ever planned to hand this off. I do sorta miss the days when font-size was typically a single-value property. There was a lot less to grok back then.

Either way, the benefits of an improved developer experience and user experience outweighs nostalgia for “simpler” days.

This is not a font “stack”

Nope, nope, nope. All I’m doing here is showing how I’m tacking font sizes. If we were talking about a “font stack” in the traditional sense, we’d be referring to a series of fonts on the font-family (or font shorthand), like:

font-family: "Alfa Slab One", Helvetica, sans-serif;

But as web typography gets more and more advanced, “font stack” might also be used to refer to an overall strategy for legibility, including fonts, fallback fonts, font sizes, line heights, and other things contributing to the “stack.”

What do you do?

Seriously, what do you do with your font sizes? Everything I’ve shared here is merely tailored to my needs and I’d be surprised if anyone else finds it works perfectly for them. I wanna see what you do!