CSS Units (em, rem, %, px, vh, vw): Which One to Use Where?

Let’s round up all the CSS units you can reasonably use in modern web design — and show, with examples, what each one is good for.

My go-to units are the ones relative to font size: rem and em.

But I don’t treat them as Supermen among CSS units either. They simply aren’t right for everything. For different jobs we’ll still need Spider-Man, Batman, and the rest of their colleagues.

A quick overview of useful units

These are the six worth committing to memory.

UnitHow it computes the size?
remrelative to the font size on the html element
emrelative to the font size on the element
pxthe reference pixel, the CSS pixel
%percentage relative to the parent element
vwa percentage of the browser window’s width
vha percentage of the browser window’s height

There are of course others — pt, ex, or vmax, to name a few. But their usefulness is either small or close to none, so to keep things simple I’ll leave them out of this text entirely.

Which units to use in which situations?

Throughout this text I care a lot about not breaking the natural inheritance of font size in browsers. Besides us, the page authors, the user may want to change it — and sometimes browsers change it too. So for different uses I recommend different units:

  • Base font size of the document: %
  • Dimensions derived from the document’s font size: rem
  • Dimensions derived from the parent’s font size: em
  • Media Queries: em
  • Line height: a unitless number
  • Borders, decoration: px
  • Typography based on window size: vw

It’s quite possible you’ll get by with px almost everywhere. I’ll get to that at the end of the text.

Base font size of the document: %

The default font size in the vast majority of browsers is 16px. But there are less prominent browsers that set the font size differently — simply so that reading is more comfortable on a particular device.

For example, the browser on the Kindle 3 had a default font size of 26px, and Opera Mini 7 used 17px. We could bet that most of the most widespread browsers don’t change the default font size, but my experience says it’s better not to.

Nicolas Hoizey wrote a nice article on default font sizes in browsers, “People don’t change the default 16px font size in their browser (You wish!)”: nicolas-hoizey.com

If the default font size doesn’t suit you, change it with relative units — ideally percentages:

html {
  font-size: 125%;
}

This sets the text a quarter larger than the default. In nearly all browsers that means 20px. On the Kindle 3 it would be 33px. It’s important to realize that this is fine. If a browser sets a different font size, it does so for sensible reasons.

Why not px? Because people enlarge text in their browsers

If we used px here, we’d be forbidding our dear users from changing the default font size in their browsers.

To be clear, we’re not talking about “zooming,” which is available via a keyboard shortcut or easily set in the browser interface, but about increasing the font size for all websites. That feature still exists in browsers and operating systems. And yes, people use it. We probably will too one day. It’s used by people with weaker eyesight, or simply with lower-quality displays.

Evan Minto, a developer at Archive.org, measured this and found that 3% of users had changed the font size in their browser. As he aptly noted, that’s more than the share of visitors using Internet Explorer, Edge, or Opera Mini at the time.

Because we want to build solutions with the widest possible reach, we shouldn’t ignore this. He writes about it in “Pixels vs. Ems: Users DO Change Font Size”: medium.com

Dimensions derived from the document’s font size: rem

I set the font size of individual parts of the document, the outer and inner spacing, and other properties across the document and components, in rem.

1rem (1 root em) holds the default font size set by the author for the document — possibly further adjusted by the user or the browser, as we’ve already seen.

If we don’t change it on the document, 1rem = 16px.

p { margin-bottom: 1rem; }
h1 { font-size: 2rem; }

With this code I set the paragraphs’ bottom margin to the height of the text. First-level headings will be twice as large as the standard font size.

Using rem is also handy from a developer’s point of view:

  • 1rem holds the base font size, so you don’t have to remember whether it’s 12, 14, 16, or however many pixels.
  • A layout width set in rem will keep an optimal line length even when the user enlarges the text.
  • Thanks to rem you can also scale the whole document up within specific ranges between design breakpoints.

What if I get assets in px?

You may be used to working in px when turning a design into code, because designers deliver assets in those units. But as I’ve written, setting anything derived from the main font size in px complicates life for many users.

How to get out of the design-versus-accessibility conflict? Automatic CSS processing can help.

You may know the PostCSS library, which enables automated processing of styles. The PostCSS plugin “postcss-pxtorem” handles converting px to rem for selected properties. It simply turns font-size:16px into font-size:1rem: github.com

So I consider rem the main unit for building interfaces. But em is very useful too.

Dimensions derived from the parent’s font size: em

The em unit holds the font size of the element, not the document.

html {
  font-size: 100%; /* = 16px */
}

p {
  padding: 1em; /* = 16px */
}

.button {
  font-size: 75%;
  padding: 1em; /* = 12px */
}

You can see that 1em means different things in different places in the document. Sometimes that’s a good thing: for example, when you’re coding a component whose size should be determined precisely by the font size on the parent element.

Of course, developers find “ems” a bit harder to work with than “rems.” Not everyone wants to become a walking calculator for converting between em and pixels.

Just note that em is not the typographic em quad

The “em quad” is a typographic unit measured from the width of the capital “M.” em is sometimes inaccurately called the em quad. But then it would be a different size for different typefaces.

And it isn’t. The W3C defined em differently. Its size at the root of the document is the same in all browsers and with any typeface — 16px. Unless, that is, it’s changed by the treacherous trio of user, browser, or page author.

Media Queries: em

Why not use px? I’d recommend avoiding pixels because of the inability of Media Queries to respond to font-size enlargement.

@media screen and (min-width: 30em) { }

In “PX, EM, or REM? Examining Media Query Units in 2021,” Iris Winter writes that because of an imperfect implementation in Safari it’s still better not to use pixels, and to prefer em or possibly rem. Both units refer, in Media Queries, to the document’s font size, so they’re freely interchangeable: betterprogramming.pub

In coding practice, though, you can use automatic conversion from px, because CSS pixels are easier to work with here. A handy PostCSS plugin called “postcss-em-media-query” will help: github.com

Line height: a unitless number

A unitless value is specific to line height, but it makes perfect sense:

h1 {
  line-height: 1.5;
}

In the CodePen example (see below) the line height is simply one and a half times the font size of the first-level heading. And then it doesn’t matter at all how anyone sets their font size.

Why is this better than setting it “hard” in rem, em, or px? If the font size changes in some context — by the author or the user — you then don’t have to change the line-height setting too.

Layout: percentages, but other units too

Percentages work well for layout. For example:

.layout-col {
  width: 50%;
}

A reminder: they’re always calculated from the width of the nearest parent element.

But there are more usable units for layout:

  • Percentages or vw stretch with the window’s width, vh with its height.
  • rem and em go by the font size.
  • In flexbox you can also use absolute units (flex:1).
  • In CSS grid there are the so-called fractional fr units (grid-template-columns:3fr 1fr).
  • Sometimes fixed dimensions in px come in handy too.

Borders, decoration: px

I recommend using px only where you need a precise expression in pixels. For example, for borders between navigation items:

.nav-item {
  border-left: 1px solid white;
}

Just to be safe, a reminder that this is no longer a hardware pixel, but the reference — or so-called “CSS pixel.” I write about it on Vzhůru dolů: vzhurudolu.cz/prirucka/css-pixel

Typography based on window size: vw

You may also need to grow and shrink the font size according to the window’s width or height. In that case, remember the vw (viewport width) or vh (viewport height) units.

For example, this heading from the example will have a font size of 2rem plus, always, two percent of the window’s width:

.heading {
  font-size: calc(2rem + 2vw);
}

Finally, I’m adding a link to a CodePen that uses what I consider an optimal unit setup on a simple example.

CodePen: codepen.io/machal

What if I still want to use mainly px?

In web design there’s always a whole range of possible solutions. Some are optimal for users but demanding to implement; others are a compromise. Units are no different. I don’t think a lot of kittens will die if you favor “CSS pixels.” Using px is significantly more convenient to implement for a large share of design types.

Still, make sure the text in the design is large enough for most users to read. As a baseline, at least those 16px are generally recommended. You should also ask yourself whether you’re bothered by anything on the following list:

  • Users who changed their font size in the system or browser (about 3% on Archive.org) won’t see their setting reflected on your site. They’re left with the option of zooming the whole page.
  • A change in font size won’t be correctly reflected in Media Queries.
  • The design doesn’t account for fluid typography that grows with the viewport.
  • The designer likewise didn’t account for a flexible change in a component’s size based on the parent’s font size, nor for a global change in font size at certain design breakpoints.