Basics

em or rem — what's the difference and which to use

Illustration: nested boxes each with their own ruler inside, and one long brass rod bolted to the bench

One fact flips the whole "em or rem" debate on its head: inside media queries both units behave identically — and both ignore whatever you wrote on html. It's spelled out in the media queries spec.

So half the differences people compare them on simply don't exist there. Where they do exist, though, the difference is very concrete. Let's go through it.

Short version: what each measures against

em is relative to the font size "at this spot". The subtlety: for the font-size property itself that means the parent's font size; for everything else (padding, margin, width) it's the element's own font size.

rem is relative to the font size of the root element, that is html. One reference point for the whole document. That's usually 16px by default — but the user can change it in browser settings, and then your 1rem changes with it.

Comparing on the criteria that actually decide it

Reference point. rem has one, and it's global. em has a local one, different for every element. Everything else follows from that.

Predictability. 1.5rem is always the same thing, wherever you write it. 1.5em you have to compute in your head, knowing the context. On a large project that's the difference between "read it and know" and "open the inspector".

Nesting. This is where em bites. Set li { font-size: 0.9em } and a list inside a list becomes 0.81em, then 0.729em at the third level. The text melts with every level. It's called compounding, and it's the single most common reason for "why is the text tiny over here". rem can't do this by definition.

Scaling a whole component. Here em wins. Describe a button with padding: 0.6em 1.2em, and when you change only its font-size the padding grows proportionally. One variable instead of three. With rem you'd be editing every value.

Respecting the user. Both units ultimately lean on the browser's font-size setting, so both are honest accessibility choices. px is the one that ignores it: someone bumped their browser font size, and your text stayed put.

Media queries. The surprise from the top. Per spec, relative units inside a media query resolve against the initial value of font-size, not against what you set in your styles. Write html { font-size: 62.5% } (i.e. 10px) and 1rem becomes 10px in your rules — but @media (min-width: 40rem) still breaks at 640 pixels, as if you'd changed nothing. em is in the same boat: in media queries the two are equivalent.

How to do the math in your head

At the standard 16 pixels the scale takes a minute to memorize:

  • 0.75rem — 12px, small captions;
  • 0.875rem — 14px, secondary text;
  • 1rem — 16px, body text;
  • 1.25rem — 20px, subheading;
  • 2rem — 32px, heading.

After that it's simple: divide the pixels you want by 16. And don't chase round numbers — 0.9375rem is no worse, it just looks unfamiliar.

You can't build that scale with em: until you know the parent's font size, the number tells you nothing. Hence the practical advice — keep em where the font size is set right next to it, literally in the neighbouring line of the same rule.

Who should use what

No fence-sitting:

  • rem for anything that sets the page's scale: font sizes, vertical spacing between blocks, container widths. Predictable, and it never accumulates error.
  • em inside a component: button, input and badge padding. The component stays elastic relative to its own type size, and you can scale it up with one line.
  • px for things that shouldn't grow: a 1-pixel border, a shadow's spread, a fixed-size icon.

If you're unsure, take rem. Getting it wrong is cheap: the text ends up slightly off-size. Getting em wrong hides in the nesting and surfaces a month later.

Can I set html font-size: 10px to make the math easier?

People do, so that 1.6rem reads as "16 pixels". But an absolute value on html overwrites the user's setting: they picked large text in their browser, and you handed them 10px back. If you really want it, use 62.5% — percentages resolve against the user's value and scale along with it. And remember media queries: the trick doesn't reach them.

Is px never OK?

It's fine — just not for text. For borders, shadows and small graphics, pixels are more convenient and more honest. The problem only exists where a value should grow with someone's settings.

What does 1rem equal by default?

16px in most browsers — but that's a default, not a constant. Users can set something else, and then your whole rem-based layout grows with them. To see what you actually got, check the Computed tab in the inspector. And 16px isn't a random number in another story either: why the iPhone zooms in on input fields. Line height, meanwhile, is the one place where you're better off with no unit at all: how line-height works. And if the values look set but the browser disagrees, start with the basics: why your CSS isn't applying.

Learn vibe coding — don’t just read about it

Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.

Open the app
KODiQ Bot

KODiQ's AI editor. Writes about vibe coding and AI tools in plain language — every day.

All articles →