A little help goes a long way. Find a tool/

Developer FIELD NOTES · 5 MIN READ

rem vs em vs px: CSS Units Explained

How px, em and rem really differ, the em compounding trap, the 62.5% root trick, and a decision rule for sizing text, spacing and borders.

Pick any CSS length property and you have a choice of units — px, em, rem, %, vw — and the choice quietly decides whether your page adapts to the user's font-size preference or ignores it. Three units do most of the work: px is fixed, em scales with the element's own text, rem scales with the page's root text. Here is exactly what each one measures.

px: fixed on purpose — and not really a pixel

The CSS pixel is not a hardware pixel. It is a reference pixel: 1px is meant to subtend the same angle of view on every device, so on a high-DPI phone one CSS px may span several physical pixels. In practice, think of px as a device-independent fixed unit. Two consequences: border: 1px gives a hairline you can rely on at any zoom level, and nothing in px responds when a user raises their browser's default font size. That second consequence is the accessibility catch — a page sized entirely in px renders identically for a user who prefers 24px body text, which usually means it renders wrong.

em: relative — to two different things

An em is a multiplier of a font size — but which font size depends on the property. For font-size itself, em refers to the parent's font size. For every other property — padding, margin, width, border-radius — em refers to the element's own font size. That split makes em powerful and treacherous in nesting:

body    { font-size: 16px }   /* 16px */
.widget { font-size: 1.2em }  /* 1.2 × 16 = 19.2px */
.widget span { font-size: 1.2em }  /* 1.2 × 19.2 = 23.04px */

Each nesting level multiplies again — 1.2em of 1.2em of 16px is 23.04px, not 19.2px. Deeply nested UIs drift upward like this, which is the classic em bug. The upside is the flip side: padding: 1em grows with the element's text, so a button's padding automatically breathes with its label. Spacing that should track text size is em's natural home.

rem: one ruler for the whole page

rem ("root em") is a multiplier of the root element's font size — the <html> element — no matter where it appears in the document. Browsers default the root to 16px, so the common conversions are fixed arithmetic:

0.75rem = 12px    1rem    = 16px
1.25rem = 20px    1.5rem  = 24px
2rem    = 32px    3rem    = 48px

Because every rem value traces back to one root, there is no compounding and no drift. The accessibility win is structural: if a user sets their browser default to 20px, the root becomes 20px and every rem-based size on the page scales up proportionally — the layout resizes around their preference without a media query in sight. A popular trick, html { font-size: 62.5% }, makes 1rem equal 10px so mental math is trivial (1.6rem = 16px); the caveat is that it hardcodes your own root value and partially overrides the user's preference, so some teams adopt it and some refuse it.

A decision rule

Font sizes: rem — they should follow the user's preference, and rem is immune to the compounding that plagues em. Spacing inside components (padding on a button or badge): em, so spacing scales with the component's own text; page-level spacing and layout gutters: rem. Hairlines, borders, and box-shadows: px — a 1px border should stay 1px. Media queries in modern browsers are effectively em-based regardless of what you write, so their breakpoints already follow the root font size. Percentages remain the tool for widths that fill a parent — width: 50% is relative to the parent's width, not any font size, which is a different axis entirely.

One card, three units

.card {
  font-size: 1rem;            /* 16px at the default root */
  padding: 1em;               /* 16px here — tracks this card's text */
  border: 1px solid #ddd;     /* hairline stays a hairline */
  margin-bottom: 1.5rem;      /* 24px — page-level rhythm */
  border-radius: 0.5rem;      /* 8px */
}
/* User sets browser default to 20px: font 20px, padding 20px,
   margin 30px — all scaled. Border still 1px. */

Read the comment: the rem and em values scale together when the user's base size changes, while the border holds its hairline. That mixture — rem for the global scale, em for text-local spacing, px for physical detail — is the idiom most design systems converge on.

Units at the byte level

Minifiers squeeze units, not just whitespace. A zero length needs no unit (margin: 0 beats margin: 0px), leading zeros vanish (0.5rem → .5rem), and comments and semicolons before closing braces disappear. Multiplied across a stylesheet, that is real payload — and it is also why unit choice affects more than rendering: consistent rem values compress and deduplicate better than dozens of ad-hoc px values. One honest note about conversion math: an em's true size depends on the element it appears in, so any px↔em conversion is only as good as the font-size context you feed it — treat results as estimates for the context you specify.

Try it yourself

Convert values both ways with the CSS Unit Converter — px, rem, em, pt, vw, vh, and % — including adjusting the root font size to see how a user preference changes every answer. Feed a stylesheet to the CSS Minifier and watch the unit-level savings described above. And for one of the most fiddly unit tasks, the Border Radius Generator builds four-corner radius values in px, %, em, or rem with a live preview, so you can see how 50% turns a square into a circle.

Frequently asked questions

What is the difference between em and rem?
em is relative to the element's own font size (and for font-size itself, to the parent's), so nested em values compound. rem is relative to the root html font size no matter where it appears — one fixed ruler for the whole page, immune to compounding.
How many pixels is 1rem?
By default 16px, because browsers set the root font size to 16px. Change the root (html { font-size: 20px }) and 1rem becomes 20px everywhere. When a user raises their browser's default font size, the root grows and every rem-based size scales with it — that is the accessibility win.
Why use px at all?
For details that should not scale: 1px hairline borders, box-shadows, and pixel-precise overlays. CSS px are device-independent reference pixels, not hardware pixels, so they render consistently — they just do not follow the user's font-size preference, which is why text and spacing should not be px.
What does html { font-size: 62.5% } do?
It sets the root to 10px (62.5% of the default 16px), so 1rem = 10px and mental math is trivial: 1.6rem = 16px, 2.4rem = 24px. The trade-off: it hardcodes your own root value, partially overriding the user's browser preference at the root, which is why teams disagree about it.
What is the em compounding trap?
font-size in em multiplies the parent's font size, so nesting multiplies: 1.2em inside 1.2em inside 16px is 23.04px, not 19.2px. Deep component nesting drifts unpredictably. Use rem for font sizes to stay anchored, or accept compounding only where scaling is intended.