Skip to main content

CSS units: px, rem, em

CSS units px, rem, and em define what a size value actually references: a fixed pixel, the parent font-size, or the root font-size.

Theory

TL;DR

  • px is a brick: fixed size, unchanged regardless of parent or root
  • em is relative to the parent element's font-size and compounds on nesting
  • rem is relative to the root html font-size and stays consistent anywhere
  • Core problem with em: three levels of 1.2em = 1.728em (1.2³), not 1.2em
  • Decision rule: rem for almost everything, px for borders and shadows, em when padding should scale with the component's own font-size

Quick example

css
html { font-size: 16px; } /* rem base */ .parent { font-size: 2rem; } /* 32px */ .child-em { font-size: 1.5em; } /* 48px - multiplied by parent's 32px */ .child-rem { font-size: 1.5rem; } /* 24px - multiplied by root's 16px */ .child-px { font-size: 20px; } /* 20px - always 20px */

.parent has 2rem (32px). Its child-em at 1.5em resolves to 48px, not 24px. rem ignores the parent entirely and reads the root.

Key difference

The reference point is everything. px has none - it is always the same number. em multiplies its parent's computed font-size, so deep nesting creates compound growth. rem always reads the html element's computed font-size, so 1.5rem is 24px whether the element sits at the top of the page or inside five nested components.

When to use

  • Typography and spacing (most cases): rem scales with browser and OS font preferences
  • Borders, box-shadow, outlines: px keeps edges crisp and does not benefit from scaling
  • Padding or margin proportional to component font-size: em, like padding: 0.75em on a button that grows and shrinks with its own text
  • Deeply nested menus or components: rem over em to avoid compound sizing math
  • 1px lines and small icons: px only

Comparison table

Aspectpxemrem
ReferenceFixed (1/96 inch)Parent font-sizeRoot (html) font-size
Nesting effectNoneCompounds (1.2em x 1.2em = 1.44em)Consistent
User zoomFixedScales with parentScales from root
AccessibilityIgnores font prefsTracks parent chainRespects root prefs
Best forBorders, shadowsComponent-local ratiosTypography, spacing, layout

How the browser handles this

Browsers resolve units during the cascade. html font-size is computed first (default 16px unless overridden). rem reads that root value directly. em walks up the style tree to find the parent's computed font-size, then multiplies. Both convert to absolute pixels before layout begins. px skips all of it.

Common mistakes

Using em for site-wide spacing

css
/* breaks in nested structures */ .menu { font-size: 1.2em; } .menu li { font-size: 1.2em; } /* 1.44em already */ .menu li a { font-size: 1.2em; } /* 1.728em - unintended */ /* fix */ .menu li a { font-size: 1rem; }

Three levels of 1.2em gives 1.728 times the base. That breaks layouts in ways that are hard to trace back.

Using px for body text

css
/* wrong: user font preferences are ignored */ body { font-size: 16px; } /* right: scales with user and OS settings */ body { font-size: 1rem; }

WCAG requires text to scale with user font preferences. px blocks that. It is an accessibility issue, not just a style preference.

Using em for borders

css
/* fine */ button { border: 1px solid #333; font-size: 1rem; } /* looks off at large font sizes */ button { border: 0.1em solid #333; }

Borders need a fixed, crisp edge. em on a border makes it thicker or thinner as the component font-size changes, which is rarely the intent.

Real-world usage

  • Tailwind CSS: p-4 = 1rem, border classes use px
  • Bootstrap 5: $spacer: 1rem base, fixed elements in px
  • Material-UI (MUI): theme typography with h1: { fontSize: '2rem' }
  • Chakra UI: rem-based spacing tokens across the board

Follow-up questions

Q: What is the browser default root font-size?
A: 16px. Users can change it in browser settings. You can check the actual computed value with getComputedStyle(document.documentElement).fontSize.

Q: Does viewport meta affect rem?
A: Indirectly. width=device-width sets the logical pixel base on mobile. rem still resolves from html font-size, but the overall layout scales to fit the viewport.

Q: Why use px for box-shadow instead of rem?
A: Shadows in rem can look blurry at non-standard zoom levels. px keeps them sharp because shadow rendering happens at the pixel layer, not the font layer.

Q: In CSS-in-JS (Emotion, Styled Components), how do you handle rem with a dynamic root?
A: Read the root value at runtime with getComputedStyle(document.documentElement).fontSize, then convert px to rem programmatically. Most theme utilities in those libraries handle this automatically.

Q: Browser zoom scales px too. So what is the actual accessibility difference?
A: Browser zoom scales everything, including px. But OS-level font size settings (not zoom) only affect relative units like rem. A user who sets their system font to 20px benefits from rem, not from px.

Examples

em compounding breaks nested nav

css
:root { font-size: 16px; } .nav { font-size: 1.125rem; } /* 18px */ .nav > li { font-size: 1.2em; } /* 21.6px */ .nav > li > a { font-size: 1.1em; /* 23.76px - ballooned unintentionally */ padding: 0.5em; /* 11.88px - also unpredictable */ } /* fix: use rem for inner elements */ .nav > li > a { font-size: 1rem; /* 16px - predictable */ padding: 0.5rem; /* 8px - predictable */ }

Two levels of em nesting pushed the font past 23px. Switching inner elements to rem keeps everything tied to the root, not to whoever sits above in the DOM tree.

rem-based button that respects user preferences

css
:root { font-size: 16px; } .btn { font-size: 1rem; /* 16px, scales with root */ padding: 0.75rem 1.5rem; /* 12px top/bottom, 24px sides */ border-radius: 4px; /* px - sharp corners */ border: 1px solid #333; /* px - crisp 1px line */ }

If a user sets their browser base to 20px, the button font and padding scale automatically. The border stays at 1px, which is exactly right for a thin dividing line. I have seen codebases where everything was in px, and the team spent days debugging why accessibility users with large-font settings got a broken layout. Switching typography to rem fixed most of it in one line.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?