px, rem, and em in CSS: When to Use Each
CSS gives you three everyday length units that look interchangeable and behave very differently: px, rem, and em. Pick the wrong one and your layout either ignores a user's font-size preference or balloons unexpectedly inside nested components. Here's how to choose.
px — absolute and predictable
A pixel is a fixed reference size. 16px is 16px no matter where it appears. That predictability is the appeal and the problem: text set in px does not respond when a user increases their browser's default font size for readability. For that reason, prefer to avoid px for font sizes. It's perfectly fine for things that genuinely shouldn't scale — 1px borders, fixed icon sizes, hairline rules.
rem — relative to the root
rem means "relative to the root element's font size." By default browsers use 16px, so:
1rem = 16px
1.5rem = 24px
0.5rem = 8px
The magic is that the user controls the root. If someone sets their browser font size to 20px, every rem on the page scales with it — 1rem becomes 20px, your headings, spacing, and body text all grow together. This is why rem is the default choice for font sizes and most spacing in accessible, responsive design.
Because rem always references the root, it never compounds. 1rem is the same everywhere, regardless of nesting.
To convert a design spec from px to rem (or back), the PX ↔ REM converter does the math at any root size.
em — relative to the element
em is relative to the current element's font size. If a button has font-size: 18px, then padding: 1em is 18px; if the button's font size changes to 14px, the same 1em padding becomes 14px. That's powerful: em lets a component's internal spacing scale with its own text.
The catch is compounding. Because em references the element's font size, and font sizes inherit, nested em-based font sizes multiply:
<ul style="font-size: 1.2em"> <!-- 1.2 × parent -->
<li>
<ul style="font-size: 1.2em"> <!-- 1.2 × 1.2 = 1.44 × grandparent -->
<li>This text is bigger than you expected</li>
</ul>
</li>
</ul>
Each level multiplies the last. This compounding is the single most common source of "why is this text huge?" bugs.
A simple decision guide
- Font sizes →
rem. Predictable, respects user preference, no compounding. - Component-local spacing that should scale with the component's text (button padding, badge gaps) →
em. - Things that must not scale (borders, fixed hairlines) →
px. - Media query breakpoints →
emorrem(they respect zoom better thanpx).
The 62.5% trick — and why to skip it
You'll see advice to set html { font-size: 62.5% } so that 1rem = 10px and the math is easier. It works, but it overrides the user's chosen base size, quietly undoing the accessibility benefit of rem in the first place. Better to keep the root at its default and let a converter handle the arithmetic.
If you're translating a whole stylesheet between units, the CSS unit converters cover px↔rem, px↔em, and rem↔em with a configurable base. Default to rem, reach for em when a value should track its own element, and keep px for the few things that should never move.