Three Units, One Underlying Question
px, rem, and em all describe a length, but they answer a different question about what that length is relative to. Getting that distinction wrong is the single most common source of "why is this element a different size than I expected" in CSS.
px โ an absolute length, period. 16px is 16px everywhere, always.
rem โ relative to the ROOT element's font-size. Always the same ratio, everywhere.
em โ relative to the CURRENT element's own (or inherited) font-size. Compounds through nesting.
The Conversion Math
rem = px รท root-font-size
px = rem ร root-font-size
With the browser default root font-size of 16px:
24px = 1.5rem (24 รท 16)
8px = 0.5rem (8 รท 16)
1rem = 16px (1 ร 16)
If a project sets html { font-size: 62.5%; } โ a common trick making the root 10px instead of 16px โ the same math applies with 10 as the divisor instead: 24px = 2.4rem. Always check what the actual root font-size is before converting; assuming 16px when a project overrides it is the most common place this goes wrong.
Why em Compounds and rem Doesn't
This is the part that actually causes bugs, not just conceptual confusion.
.card { font-size: 1.2em; } /* 1.2 ร parent's font-size */
.card .title { font-size: 1.2em; } /* 1.2 ร .card's font-size, NOT the root */
If .card sits inside an element that's already at 1.2em, and .title is 1.2em again, the actual rendered size is 1.2 ร 1.2 ร root-size โ each nested em multiplies against the one before it. This is called compounding, and it's the specific, well-documented reason em has a reputation for "unpredictable" sizing in deeply nested components: the same 1.2em declaration produces a different actual pixel size depending on how deeply nested the element happens to be.
.card { font-size: 1.2rem; } /* 1.2 ร ROOT font-size โ always */
.card .title { font-size: 1.2rem; } /* 1.2 ร ROOT font-size โ still always */
rem sidesteps this entirely by always measuring against the root element, no matter how deep the nesting goes. That's why rem became the default recommendation for most sizing in modern CSS โ not because em is "wrong," but because rem's behavior doesn't depend on where in the DOM tree a value happens to be used.
When em Is Actually the Right Choice
em isn't a mistake to avoid entirely โ it's the correct tool for one specific job: sizing something relative to its own element's font-size, on purpose.
.button {
font-size: 1rem;
padding: 0.75em 1.5em; /* padding scales WITH the button's own text size */
}
.button.large {
font-size: 1.5rem; /* padding automatically scales up too, no extra rule needed */
}
Here, em is exactly right: the padding should track the button's own font-size, whatever that happens to be set to โ that's genuinely the relationship you want, not a bug to avoid. This pattern (padding/spacing in em, font-size in rem) is common in well-built component libraries specifically because it lets a single font-size change resize everything proportionally.
pt and % โ The Other Two
pt (point) is a fixed conversion from px, not relative to anything: 1pt = 96 รท 72 px โ 1.333px, based on the universal 96px-per-inch, 72pt-per-inch CSS reference. It's mostly a holdover from print design โ there's rarely a reason to use it in a screen stylesheet over px or rem directly.
% (percent) is relative to a parent property's value, and which property depends entirely on what you're setting โ width: 50% is relative to the parent's width, font-size: 150% is relative to the parent's font-size (behaving exactly like em in that specific case). Useful for genuinely fluid, container-relative sizing; confusing as a general-purpose unit specifically because what it's "50% of" changes per property.
Quick Reference
| Unit | Relative to | Use for |
|---|---|---|
px | Nothing โ absolute | Borders, fixed icons, anything that shouldn't scale with user preferences |
rem | Root element's font-size | Font sizes, spacing, most layout values โ the safe default |
em | Current element's own font-size | Padding/spacing that should scale with that specific element's text |
pt | Fixed ratio to px (96/72) | Rarely needed outside print stylesheets |
% | Depends on the property | Fluid, parent-relative sizing where that's genuinely the goal |
Try It
ToolNinja's CSS Unit Converter โ converts instantly between all five units with a configurable base font size. For responsive type that scales smoothly across viewport sizes rather than jumping at breakpoints, the Fluid Typography Calculator โ builds the clamp() expression for you.
Sources: