Success StoriesBlogContact
3D type on a wall
Web Development |

A guide to CSS units — pt. 2: font relative units

Moritz Jacobs

January 29, 2021

tl;dr quick summary
Welcome to our four part series on units in CSS, where we dive into each available unit, explain the relationship between them and try to find out when to use which. We will talk about accessibility, best practices for developer teams and the history of sizing elements on screens. In part 2 we will talk about font relative units and how user settings can influence your layout and a11y!

If you missed the first part of this article series, start here!

CSS knows many different units to describe dimensions, the spec calls that "lengths". Font relative units of length produce values — as the name states — that are dependent on their parent element's font size. They are multiplicator based values, for example 1.5em == 1.5 × $fontSize There's a few:

em

em is a unit that is relative to the font size of the current element. It is a typographic unit since the age of metal type and was originally a reference to the width of the capital M (hence the name). Examples!

This .box will be rendered with 18px padding because 1em is relative to its implicit font size:

1
2
3
4
.box {
  font-size: 18px;
  padding: 1em;
}

In this next example, all list items will be rendered with a left margin of 3 × 12px = 36px because font-size: 12px; is the closest, inherited font size declaration.

1
2
3
4
5
6
7
ul {
  font-size: 12px;
}

ul li {
  margin-left: 3em;
}

em is easy to understand but can lead to messy and unreadable code due to the nature of font size inheritance and the cascade in CSS. It isn't always clear what font size you are pulling from at any given moment.

That is one of the reasons I prefer…

rem

Like em, rem is relative to font size, but in this case always the font size specified by the root element of the document (:root {…}, for websites it's the html element). So across your whole CSS code base 1rem will always be rendered the same size. But the question is: what size will that be in practice?

To answer that, let's briefly talk about accessibility (a11y for short). Every browser has a default font size and a method for the user to change it.

screenshot of the Firefox option for the default font

That's the base font size users will see on your website when unaltered by your CSS. The default default here is 16px and according to this research about 3% of users change this setting, almost always to something bigger than 16. When it comes to the internet, 3% is quite a few million people!

Now if you go ahead and do something like this…

1
2
3
4
5
6
7
8
9
html {
  font-size: 14px;
  /* 🚫 do not do something like this! */
}

body {
  font-size: 14px;
  /* 🚫 this is also bad */
}

… you break a part of a11y for 3 percent of people. These people rely on text scaling up, and your statically declared font size prevents that.

That doesn't mean all your text has to be >= 16px. It just means people who require bigger text, get bigger text. So if your design uses 14px for body text, an user-set browser font size of 20px (or 125%) will render as 125% × 14px == 17.5px.

To make this work for both your design and your users, just use rem for font sizes and omit setting a baseline font size:

1
2
3
4
5
6
7
8
html {
  /* do not specify any font size here, at least not in px */
}

body {
  font-size: 0.875rem; /* == 14/16 */
  padding: 2rem; /* 32px */
}

More examples?

Look at these four buttons. They appear identical to 97% of all users (the 16px crowd):

four boxes in a container with font-size: initial;

There is no font size context inherited. From left to right:

  • uses px for everything
  • uses em for font size and px for padding
  • uses em for everything
  • uses rem for everything

At first sight all of the four implementations seem equivalent. But see what happens when you change the browser's default font size to 24px (150%):

four boxes in a container with font-size: initial; and a changed default size

From left to right:

  • font-size: 24px This box hasn't changed at all because we override the default setting with a px value. This is bad a11y, the user should be in control of this!
  • font-size: 1.5em— better, the font is bigger but padding: 12px is still static, so the padding didn't scale and the designer will probably be unhappy (they tend to get that way).
  • padding: 0.5em— the best yet! Now the padding scales as well. 0.5em === 12px because the font size in this box is 1.5em == 24px
  • but rem seems to be identical... why bother?

See what happens when these appear in a container with a specified font size that they inherit:

four boxes in a container with font-size: initial; and a changed default size
  • the first one is still to small
  • the second one is way too big and the padding is wrong
  • our winner from before is even bigger because the padding adapts
  • The one using rem is arguably the "correct" solution, because it scales with the user font size but is not dependent on a parent element's font size declaration.

Conclusion: Use rem units when you're sizing text or when you want something to be proportionate to a user's text size setting. Some people argue that you should use rem for everything. Others disagree. In my opinion, there's no definitive answer for this. It depends on your design, your approach to a11y and your use case. Be aware of the differences and — together with a designer — decide for each of your components how scaling should behave. Be consistent and test things with different font size browser settings and browser zoom levels!

ex, ch

There's two more relative units that you never see in real projects: ex and ch.

ex is relative to the current font's x height. Chris Coyer explains it further:

Sometimes that comes from information embedded in the font itself, sometimes browsers figure it out by measuring a lower case glyph, and worst case, it’s set to 0.5em. It is named “x” height because it is supposedly based on the height of the x character. To understand x-height, think of a lowercase character that as a bit that sticks up (ascender) like a lowercase “d”. The x-height doesn’t include that ascender, it is the height of the lower loop part of that character.'

CSS Tricks

ch is similar but instead of the font's x-height it relates to the width of the 0 character (pen).

comparison screenshot of squares sized with ex and ch

There will be another set of units that are relative to line height, but none of the browsers support them yet: lh and rlh.

See, there's a lot to unpack, once you start to closely look at all of your options. And there's more to come! We will discuss percentage based units in the next part.

Header Photo by Jason Leung on Unsplash

Are you developing an app idea? Our mobile apps are built for a wide array of platforms.

Go to software development

CSS units

relative units

font realtive

rem

em

web typography

Read also

Moritz Jacobs, 01/29/2024

Heading for Greatness — A React Developer's Guide to HTML Headings

HTML Headings

Best Practices

Uberschrift

Accessibility

SEO

Go to Blogarticle

Stephan, 12/18/2023

A small contribution towards a better world

Company Culture

Donations

Charity

Corporate Culture

Go to Blogarticle

Klara, 11/22/2023

Peerigon goes purple

#PurpleLightUp 2022

#InclusionMatters

company culture

diversity

Go to Blogarticle

Peerigon: the company for bespoke software development.

Peerigon GmbH

Werner-von-Siemens-Straße 6
86159 Augsburg
+49 821 907 80 86 0
mail peerigon
BCorpkununu top company 2022
ClimatePartner

service

IT staff augmentation servicesUX/UI DesignCustom Software DevelopmentSoftware Development ConsultingFull Stack Training
BlogSuccess StoriesContactWhy use TypeScript?Why use JavaScript?

© 2024 Peerigon

Privacy PolicyLegal NoticeContactPress

Do you already know Konsens, our free online tool?