Setting responsive font sizes in CSS
In an ideal world, we wouldn’t have to tell browsers how big we want our fonts to be. Instead, type would just look right on whichever device we were using.
However, because of browser history and the existence of millions of fonts with different dimensions, it’s rare we’ll not want to alter the browser default.
There are several approaches to scaling fonts. Here’s how I go about it.
Other posts in this series
What we want to achieve
Two things:
1. Respect user settings
Users will sometimes want to change the font size for their own purposes. This may be due to environmental factors, such as looking at a website on a TV screen from a couch a few metres away, a visual impairment (such as plain deteriorating eyesight as we age) or even just taste.
Whatever the reason, altering the font size should work up to a scale of around 300% at least. Note that some users change their browser’s base font size in its settings, while others do this on an ad hoc basis, by pressing Cmd/Ctrl
and +
or -
.
2. Scale up our font size for wider viewports
As the viewport becomes wider we’ll probably want to increase the font size. There are a couple of reasons for this.
Firstly, devices offering wider viewports, such as laptops or a PC connected to a monitor, allow us to read the text from further away. The further we get from the screen, the bigger the text needs to be.
Note we can’t actually tell what type of device (mobile, laptop, TV screen, projector etc.) our users are reading with. We can only know how wide the viewport is.
Secondly, when we have more pixels available we have the luxury of being able to increase our type size in order to make reading easier.
If you can, test your settings on different devices to at least get a sense of how your fonts look in the real world. As with measure, this is a mixture of art, science and trial and error.
The CSS
1. Set your HTML font size using a %. For example:
html {
font-size: 118.75%;
/*
This will probably compute to 19px
*/
}
What we’re saying here is that we’d like the browser to scale up the font-size
118.75%. Why that weird number? Well, browsers have a default font size of 16 pixels, and 118.75% of 16 works out at 19 pixels.
For reference, here are some other % and pixel values:
% | Pixels |
---|---|
106.75 | 17 |
112.5 | 18 |
118.75 | 19 |
125 | 20 |
As it stands, our website will scale all text by 118.75%, regardless of whether you’re viewing your site on an old mobile phone or a modern monitor. This may well be OK – test on a few screens.
If you feel you need something bigger for wider screens, you’ll need to start using media queries.
2. Use media queries to set larger font sizes on wider screens
We can tell our browser to scale our text again whenever the viewport (including the width of the scrollbar) reaches a certain width:
html {
font-size: 118.75%;
/*
This will probably compute to 19px
*/
}
@media screen & (min-width: 30em) {
/*
When the viewport + scrollbar reaches 30x16 = 480px
*/
html {
font-size: 125%;
}
/*
This will probably compute to 20px
*/
}
@media screen & (min-width: 60em) {
/*
When the viewport + scrollbar reaches 60x16 = 960px
*/
html {
font-size: 150%;
}
/*
This will probably compute to 24px
*/
}
This time we’re saying: make the default font-size
19px
, then, when the screen width reaches 30em
, make it 20px
. Finally, when it reaches 60em
, make it 24px
.
(A note on media query width units)
We use em
s to express our width ranges because they provide the most consistent results across browsers.
In this case, 1em
is equivalent to 1 times the browser’s base font size, not your website’s. Unless the user has changed the browser base font size in its settings, 1em
will equal 16px
, whatever you’ve set your html
font size to.
3. Use rem
to size type
rem
(relative em) units make sizing elements relatively easy. They work by multiplying your web page’s base font-size
, which is why we set our html
font-size
rather than its body
. 1rem
equates to 100%, 1.25rem
to 125%, 3rem
to 300%, and so forth.
Take our above example. Body copy elements such as p
and li
automatically have a font-size
of 1 rem
of our html
base font-size
. By default this will be 19px
, scaling up to 20px
and 24px
.
We can then set the size of other elements using rem
units and calculate how many pixels that will equate to:
h1 {
font-size: 2rem;
/*
Will probably compute to 38px
Then at 30em it will compute to 40px
Then at 60em it will compute to 48px
*/
}
h2 {
font-size: 1.5rem;
/*
Will probably compute to 28.5px
Then at 30em it will compute to 30px
Then at 60em it will compute to 36px
*/
}
h3 {
font-size: 1.25rem;
/*
Will probably compute to 23.75px
Then at 30em it will compute to 25px
Then at 60em it will compute to 30px
*/
}
And finally, on (not) using CSS clamp
Wouldn’t it be great if we could just set our font-size
to a proportion of the screen width? Something like font-size = 2.5% of the viewport width
. That way we could get rid of all these verbose media queries.
Well, we can – nearly. Take this CSS:
html {
font-size: clamp(118.75%, 2.2vw, 131.25%);
}
This says:
- Set the minimum
font-size
to118.75%
of the browser default (probably 19px) - Set the maximum
font-size
to131.25%
of the browser default (probably 21px) - Between these ranges, set the font-size to 2.2% of the viewport width. So at
960px
this would compute to21.12px
(or whatever your broswer rounds that to).
The only reason not to do this is because it can make user resizing unpredictable 🤷🏻♂️.