In the world of web development, there are many misconceptions circulating that persist even when they've been refuted many times. "External links should always open in new tabs" is a good example . CSS Tricks explained this in detail (in short: mostly wrong) almost a decade ago, but it still seems to be around in some corners.
Case in point: the idea that there is no functional distinction between , , or units in CSS is a misconception I hear over and over again, so I thought I'd post here to address it. px
em
rem
Let's be very clear: the units you use in CSS absolutely matter. And should be avoided as much as possible when . font-size
px
What units are we talking about and what do they do?
Before we get into why using as should be avoided , let's make sure we're all clear which units we're talking about, and how they behave in general. px
font-size
px
px
is short for pixel...although it's not really a pixel in most cases anymore. 1024×768
In an era when monitors were usually a relatively predictable low-resolution ratio of pixels, such as , 1px
was usually equal to one actual pixel on the screen.
Screens display images using dots of colored light called pixels. A pixel is a single point of colored light on a display; the smallest possible "point" that the hardware is capable of representing. This is what I mean in this section as a "literal", "actual" or "device" pixel; a pixel in the physical world.
However, when high-resolution (sometimes called "retina") screens came along and devices started packing more pixels into a smaller space, those physical device pixels became tiny. Browsing the web on a high-res screen, 1px
it's going to be very difficult to even read anything if the in CSS still corresponds to a literal device pixel, because the pixels themselves are rapidly shrinking. After all, modern smartphones have even higher resolutions than HDTVs.
So now, 1px
usually the size corresponds to the enlarged "scaled" pixels, not the literal pixels on the actual hardware. In our CSS, 1px
things may take up multiple physical hardware pixels, and we don't have any pure CSS way to specify a literal device pixel. But that's okay because they're usually too small and we don't want to deal with them.
An example: the pixels on the iPhone 14 Pro are so tiny that 16px is literally the size of a device pixel about the size of a typographic font at a 2pt font size. Good thing the browser scales them for us!
Most of the time, these don't really matter in the context of this discussion, but I think it's good to know. The important part is: 1px
Equal to whatever the browser sees as a single pixel (even if it's not really a pixel on a hardware screen).
em and rem
This brings us to em
and rem
, which are similar to each other. Moving on to a tidbit that isn't strictly related, but still fun: "em" is a typographical term that actually predates computers by decades. Typographically, one em
is equal to the current font size.
If you set the font size to 32pt
("pt" is another old typography term still sometimes used), then 1em
yes 32pt
. If the current font size is 20px
then 1em = 20px
.
On web pages, the default font size is 16px
. Some users never change the default, but many do. But by default, both 1em
and 1rem
will be equal to 16px.
"Em" originally referred to the width of the "M" character, which is where the name comes from. But now it refers to the current font size, not the dimensions of a specific glyph.
Difference Between EM and REM
To differentiate the two: 1rem
always equal to the browser's font size, or more precisely the font size of the html element. rem
stands for "root em", and the root of a web page is <html>
the tag. Hence, 1rem
= document
font size. (By default this is 16px
, but can be overridden by the user.)
On the other hand, em
is the font size of the current element. Look at the CSS below:
.container {
font-size: 200%;
}
p {
font-size: 1em;
}
Taking into account the above CSS, the paragraphs inside the element will be doubled in size. This is because it means "current font size", inside , it is . (default is ) . .container
1em
.container
200%
1em × 200% = 2em
32px
However, paragraphs outside the element will still be at the normal font size (the default ). .container
1em
16px
em
If we change to in the CSS above rem
, then the font size of all paragraph tags will always be the browser's default size, no matter where they are.
font-size: 1em is equivalent to font-size: 100% . em and % units are not always equivalent in other contexts; for example,
width: 1em
em andwidth: 100%
% are likely to be very different, since percentages are then based on the parent container's width rather than its font size. However, as far as the font-size property is concerned,%
andem
are the same.
in conclusion:
-
1em
is the font size of the current element. -
1rem
(root em) is the font size of the document (i.e. the browser's font size).
Ok, so that's what units are and where they come from. Now let's answer why it matters which unit you use.
why it all matters
Again, the misconception is that since 1em
and 16px
are equal, it doesn't matter which unit you choose. This seems reasonable; if 16px = 1rem
, then it doesn't seem to matter which way you choose to enter.
Remember, em
and rem
are relative; by default, they are both (ultimately) based on the browser's font size.
2rem
Twice the browser font size; 0.5rem
half it, and so on. So if the user changes their preferred font size, all text on the site changes accordingly, as it should, if using em
and . Still twice that font size; still half that.rem
0.5rem
Values , by contrast, are static. 2 just works regardless of the font size of the container, browser or user . When setting a static pixel value, it overrides that selection and uses the exact value specified, regardless of the user's font preference size. px
0px
20px
Critically speaking, this means that if your stylesheet usage px
is set anywhere font-size
, any text based on that value will not be able to be changed by the user.
That's a very bad thing. It is inaccessible and may even prevent someone from using the site at all.
So while there may be valid use cases for this behavior, it's definitely not the default behavior you want.
This is also a very good reason to avoid setting font sizes using viewport units such as vw or vh . They are also static and cannot be overridden by the user. At most,
calc(1rem + 1vw)
a value like this is probably acceptable since it's still includedrem
as a base. Even so, I'd still recommend using or media queries to set min and max values, as screen sizes are often way beyond what we expect or test for.clamp()
Difference beyond font size
Ok, now let's talk about how andfont-size
changes when we don't deal with properties in particular .px
em / rem
Developers usually test by zooming the page, which I think is the source of the misconception at the center of this article. When you zoom, everything gets scaled (zoomed in or out), in which case it usually doesn't matter what you choose or / as your CSS units. As far as scaling is concerned, both behave the same way. And most developers with good eyesight probably won't realize there's more to it. However, the tricky questions are: px
em
rem
Behavior differs from and font-size
even px
beyond .em
rem
px
Units are still tied to scaled values of pixels on the screen. em
and rem
are tied to the font size of the document, not the zoom or scale of the page.
For a demonstration, take a look at this CodePen:
HTML CSSResult Skip Results Iframe
EDIT ON
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nam eum aliquam eveniet.</p>
<p>Sapiente delectus in ab excepturi, commodi placeat quaerat saepe voluptas sunt numquam.</p>
<p>Rerum veniam, quidem voluptatibus deleniti nihil consequatur blanditiis explicabo eum quos. Nam.</p>
<p>Natus necessitatibus delectus neque tenetur sint illum obcaecati similique sequi doloribus eligendi?</p>
<p>Eos quidem iure debitis dolorum repellendus ab incidunt ipsam suscipit, autem consequuntur?</p>
p {
border-bottom: 2px solid black;
margin-top: 0;
margin-bottom: 20px;
}
We have several paragraphs with 2px
borders at the bottom of each paragraph and margins between them. Note that we use units for both. 20px
px
If you zoom in or out, the size and distance of elements remain relatively unchanged. That is: the more you zoom in, the thicker that line gets, and the more space there is between paragraphs.
For your convenience, here's a screenshot showing the same pen at 400% zoom . The text, lines, and spacing are all 4 times larger; they remain the same size relative to each other:
When it comes to scaling, there is no real difference between px
, , em
or rem
. But zooming isn't the only way users can make a site more usable.
As mentioned earlier, users can also specify a default and/or minimum font size. When they do, the features start to diverge.
In the screenshot below, I've set Firefox's default font size to 64px
. take a look:
Compare the text in the screenshot with the text above it. Note that this time, the lines are not getting thicker and the margins between paragraphs are not increasing proportionally. Only the text itself gets bigger. Since both border width and margin are set in px, they stay the same and don't scale.
But notice that if you change the values in CSS px
to the corresponding rem
values, you will find that the lines and spacing are indeed bigger! (zh-Hans)
So, the summary here is:
-
The px value doesn't scale when the user changes the font size.
-
The em and rem values scale proportionally to the font size.
If you want an interactive demo that ties all of this together, check out the resulting CodePen; adjust the sliders at the top to see how changing the document font size affects various elements, based on the CSS units they use. https://codepen.io/collinsworth/pen/KKepeMQ
which one to choose
So, knowing em
that rem
sums scale with font size, but px
values don't, what do we do? Should we never use px
it?
While I think you'll probably be fine if you choose this path, I still think px has a purpose.
We know that px
the value , which means that pixel units are actually a good choice for certain aesthetic elements. Maybe we have some spacing that we don't want to get bigger when the font size gets bigger. (Maybe it doesn't make sense to allow it to scale to a larger size if it's a big chunk of negative space by default.)
Maybe there's some border size we don't want to change, or a decorative element on the page created with CSS that doesn't look good at larger font sizes. Maybe we don't want the padding to bloat as the font size increases. In all these cases, px is still a good choice.
Personally I recommend using rem
to set all the sizes. I only add em's in things where you want to be proportional to the current font size (for example, in the case of some text where an icon next to it should be exactly the height of the character, and have half a character on one side)
. I wouldn't use it anywhere px
except for design elements that explicitly don't want to scale with font size.
Never px
set font-size in units unless you are very sure what you are doing, how it will behave, and whether it will still be accessible when you do.
Important note about media queries
For the same reasons as all of the above, it's important to avoid @media
using in queries px
; it will work fine when the user zooms, but use px
a media query that will fail when the user sets a larger font size themselves.
@media (min-width: 800px) {
/* Changing font size does NOT affect this breakpoint */
}
@media (min-width: 50rem) {
/* Changing font size DOES affect this breakpoint */
}
This is because as the font size increases , 50rem
it becomes a different value based on the user's preference, while 800px
it doesn't .
Chances are, when we write CSS for larger breakpoints, we think there is enough screen real estate for the element to expand. This may not be the case if the user has set a very large font size, setting the media query to rem
not px
helps us avoid this assumption and respond to the user's preference.
I ran into this problem on this site; I set all breakpoints on px . However, when I make the default font size larger, my media queries become unresponsive because they still only look at the pixel width of the screen. So I still have a tiny sidebar stuffed with huge illegible text because I'm not taking user preferences into account. Immediately after that, I changed to rem and the problem was resolved.
In short: In media queries, unless you are sure you know how setting your own font size in the browser will affect your users, definitely avoid it px
.