px, rem and em: sizing text and space in CSS

Development · 6 min read

Choosing between px, rem, and em in CSS trips up developers at every level, not because the concepts are complicated, but because the practical consequences only become obvious once a design needs to scale, respond to user preferences, or nest components inside one another.

Pixels: simple and absolute

A pixel in CSS is a fixed, absolute unit that does not change based on anything else on the page. Set something to 16px and it stays 16px regardless of the parent element's font size, the user's browser settings, or anything else in the surrounding document.

This predictability is genuinely useful for things that should never scale, like a 1px border or a fixed 24px icon that needs to stay visually consistent regardless of context. The downside is that pixel values ignore the user's browser-level font size preference, which some people increase for accessibility reasons.

A converting tool like unit-calculator is handy when you are translating a design handed over in pixels into rem-based CSS values and want to avoid doing the division by hand repeatedly.

rem: relative to the root, consistently

rem stands for 'root em' and is always relative to the font size set on the html element, regardless of where in the document it is used. By default, most browsers set that root font size to 16px, so 1rem equals 16px unless the user or the developer changes it.

Because rem always refers back to the same root value no matter how deeply nested an element is, it behaves predictably throughout a whole document. This makes it the standard recommendation for font sizes and most spacing values in modern CSS, since it respects a user's browser-level font size preference while remaining consistent everywhere it is used.

If a user increases their default browser font size for accessibility, everything sized in rem scales proportionally along with it, while anything sized in px stays fixed and can end up looking cramped or too small relative to the rest of the page.

em: relative to the immediate parent

em is also a relative unit, but unlike rem, it is relative to the font size of the current element's parent, not the document root. This means the same em value can compute to different actual pixel sizes depending on where it sits in the document's nesting structure.

This behaviour, often called compounding, means that nested elements each sized in em relative to their own parent can stack multiplicatively, so three levels of nested 1.2em text can end up noticeably larger than intended, in a way that is easy to lose track of in a complex layout.

This is not necessarily a flaw. It is genuinely useful for component-level sizing where you want padding or spacing to scale in proportion to that specific component's own font size, such as a button whose internal padding should grow or shrink along with its text.

A practical rule of thumb

Use rem as your default for font sizes and most layout spacing, since it gives predictable, document-wide consistency and respects user accessibility preferences without any extra effort on your part.

Use em selectively, inside a specific component, when you deliberately want an internal measurement, like padding or a small margin, to scale in proportion to that component's own font size rather than the page's root size.

Use px sparingly, for things that genuinely should never scale regardless of user preferences or root font size, such as hairline borders, box shadows, or fixed-size icons that need to stay visually crisp.

Why root font size matters more than people realise

Because rem always traces back to the root font size, changing that root value, either through a media query for responsive typography or through the user's own browser settings, cascades cleanly through every rem-based measurement on the page at once.

Some sites deliberately set the root font size with a percentage, for example html { font-size: 100%; }, specifically to preserve the user's own accessibility preference rather than overriding it with a fixed pixel value, which is worth respecting rather than fighting against.

Doing the mental maths without doing the mental maths

With a 16px root font size, 1rem is 16px, 1.5rem is 24px, and 0.875rem is 14px, which covers most of the common values used in typical design systems. Once you get used to a handful of these conversions, working in rem stops feeling like an extra translation step.

For values outside that familiar set, or when you are converting a full design spec from px to rem quickly, a quick pass through a unit-calculator saves time and avoids small arithmetic slips that are surprisingly easy to make when converting many values by hand.

Common mistakes worth avoiding

Mixing em and rem inconsistently within the same component, without a clear reason, tends to make a codebase hard to reason about, since two developers reading the same stylesheet may reasonably expect different scaling behaviour from the same-looking unit.

Overriding the root font size with a fixed pixel value, rather than a percentage or leaving it at its default, silently breaks the accessibility benefit that rem is meant to provide, since it locks every rem-based value in the entire document to a fixed developer-chosen scale.

Using em for deeply nested components without accounting for compounding is a classic source of unexpectedly large or small text several levels down a component tree, and is worth testing deliberately rather than assuming it will behave the same as at the top level.

What about viewport units?

Viewport units like vw and vh are a separate category again, sized relative to the browser window rather than any font size, and are useful for elements that should genuinely scale with screen size, like a full-height hero section. They solve a different problem to px, rem, and em, and are worth knowing about but not a direct substitute for any of the three.

Common questions

Should I use rem or px for font sizes?
Use rem in almost all cases. It respects a user's browser-level font size preference, which matters for accessibility, and stays consistent regardless of where in the document it is used, whereas px ignores that preference entirely and stays fixed no matter what the user has configured.
Why did my nested em values end up much bigger than expected?
This is compounding: em is relative to the parent element's font size, so nested elements each sized in em relative to their own parent multiply together level by level. If you want consistent sizing regardless of nesting depth, use rem instead, since it always refers back to the same root value.
Is 1rem always equal to 16px?
Only if the root font size is left at its default. Most browsers default the html element to 16px, so 1rem equals 16px out of the box, but if the root font size is changed, either by the developer or by the user's own browser settings, that equivalence changes proportionally along with it.
When is px actually the right choice?
For details that should never scale regardless of user preference, such as a 1px border, a small fixed-size icon, or a box-shadow offset. These are cases where absolute, consistent sizing matters more than respecting accessibility-driven font size adjustments.
Do em and rem affect spacing as well as font size?
Yes, both units work for any length value, including margin, padding, width, and gap, not just font-size. The same relative behaviour applies: rem values trace back to the root font size while em values trace back to the current element's parent font size, wherever they are used.

Tools for this

Keep reading