Why this comparison still matters
CSS gives you dozens of length units, but for everyday layout and typography three matter: px (pixels), em (relative to parent font-size), and rem (relative to root font-size). They produce different results, behave differently under user font-size preferences, and have meaningful accessibility implications. Many developers default to px everywhere because it's predictable — and quietly ship an accessibility bug, because px does not honor the user's browser font-size setting. This article compares the three units and explains when to use each.
px: absolute, predictable, accessibility-blind
The px unit is defined in the CSS Values and Units specification as a 'reference pixel' equal to 1/96 of a CSS inch. On a standard 96-DPI display, 1 CSS pixel equals 1 device pixel; on high-DPI displays, the browser scales so that 1 CSS pixel still corresponds to roughly the same visual size. The result is a unit that looks identical across devices and ignores user font-size preferences — set body text to 16px and it stays 16px even if the user set their browser to 24px default.
That predictability is px's appeal. A 1px border is exactly 1 CSS pixel. A 320px-wide sidebar is exactly 320 CSS pixels. Designers think in pixels because design tools (Figma, Sketch) work in pixels, and px is the unit where the rendered result matches the design mockup most closely.
The accessibility cost is significant. Users who set a larger default font size in their browser (a common accommodation for low vision) expect all text to scale accordingly. Setting font-size in px overrides that preference, forcing the user to zoom the entire page (which breaks layouts and requires horizontal scrolling) rather than just enlarging text. The Web Content Accessibility Guidelines (WCAG) 1.4.4 Resize Text requires that text can be resized up to 200% without loss of content or functionality — px-based typography can violate this if users cannot override it.
px is the right choice for layout dimensions that should not scale with font size: borders (1px solid), box-shadows, fixed-width UI elements, image dimensions, and pixel-perfect details. It is the wrong choice for typography and for spacing that should scale with text.
em: relative, compounding, contextual
The em unit is relative to the font-size of the nearest parent element. If a parent has font-size: 16px, then 1em = 16px in that context. Set the child's font-size to 1.5em and the child renders at 24px — and any further em values inside the child are relative to 24px, not 16px. This compounding behavior is em's defining characteristic and its biggest source of confusion.
Compounding is powerful when you want a component to scale internally. A button with font-size: 1em, padding: 0.5em 1em, and border-radius: 0.25em scales proportionally if you change the button's font-size — everything inside stays in proportion. Drop the button into a footer with smaller text (font-size: 0.875em) and the entire button shrinks cleanly. This is why em is the unit of choice for component-level spacing, padding, and margins inside design system components.
Compounding bites you in nested lists. A list with font-size: 1.2em where each nested list inherits the same rule produces 1.2em, 1.44em, 1.728em — each level 20% larger than the last. The classic 'nested lists keep getting bigger' bug is em compounding in action. The fix is to reset font-size on nested elements or use rem where you want root-relative consistency.
em is also the right unit for media queries in some cases: an em-based media query (min-width: 40em) scales with the user's font-size preference, so a user with a larger default font gets the layout breakpoint at a wider viewport. This is more accessible than px-based breakpoints. Most modern CSS frameworks (including Bootstrap 5) use em or rem for media queries for exactly this reason.
rem: root-relative, predictable scaling
The rem (root em) unit is relative to the font-size of the root element (the <html> element), which browsers default to 16px. So 1rem = 16px unless the root font-size is overridden. Unlike em, rem does not compound — 1.5rem is the same size whether you're at the root or five levels deep in nested components.
rem's superpower is that it scales with the user's font-size preference while remaining predictable. If a user sets their browser default to 20px, every rem-based dimension scales by 25% automatically — typography, padding, margins, the works. This is the accessibility win: respect the user without giving up predictability.
A common pattern is the '62.5% trick': set html { font-size: 62.5%; } which makes 1rem = 10px (since 16px × 0.625 = 10px). Now 1.6rem = 16px, 2rem = 20px, and so on — easy mental math while still respecting user preferences. The downside is that any third-party widget on the page that expects the default 16px root will render at 62.5% size unless it uses its own sizing. Modern frameworks increasingly avoid this trick in favor of working directly with the 16px default.
rem is the right choice for typography at all levels (body text, headings, small print), for spacing that should scale with text (margins between paragraphs, padding inside cards), and for media query breakpoints. It is the unit most modern CSS methodologies (Tailwind, every-major design system) default to for typography and spacing scales. Tailwind's spacing scale (1 = 0.25rem = 4px, 2 = 0.5rem = 8px, 4 = 1rem = 16px) is built entirely on rem, which is why a user who bumps their browser default to 20px sees the entire Tailwind UI scale by 25% — accessibility baked into the framework.
Fluid typography and the 62.5% trick
For fluid typography that scales with viewport while respecting user preferences, CSS clamp() combined with rem is the modern pattern: font-size: clamp(1rem, 2vw + 1rem, 2rem) produces a value that grows with viewport width (the 2vw term) but never drops below 1rem (the user's minimum) or above 2rem (the readability cap). This gives you responsive type without JavaScript and without sacrificing accessibility — the rem floor ensures the user's font-size preference is always honored even on small screens.
The '62.5% trick' — setting html { font-size: 62.5%; } so that 1rem = 10px — deserves a word of caution. It works and was popular in the 2010s because it made mental math trivial (1.6rem = 16px, 2rem = 20px). The downside is that any third-party widget, iframe, or library on the page that expects the default 16px root renders at 62.5% size, looking broken. Modern frameworks increasingly avoid this trick in favor of working directly with the 16px default and embracing the slightly-less-clean math (1rem = 16px, 1.5rem = 24px, 0.875rem = 14px). The accessibility is identical either way; the difference is ecosystem compatibility.
A note on browser zoom versus text scaling. Most browsers offer two distinct controls: page zoom (Ctrl/Cmd + +/-) which scales everything including layout, and text-size-only settings (Firefox default, Chrome's accessibility setting) which scale only text and rem/em-based dimensions. px-based typography ignores the text-size-only setting, forcing users to fall back on full page zoom — which can break layouts and require horizontal scrolling. This is the concrete accessibility cost of px for typography, and it's the reason WCAG 1.4.4 (Resize Text) effectively requires rem or em for text.
Side-by-side comparison
When to choose which
Choose rem for typography at every level — body text, headings, captions, labels. The default browser root of 16px is the right starting point; define your type scale as rem multiples (0.875rem for small text, 1rem for body, 1.5rem for h3, 2rem for h2, etc.). Use rem for spacing that should scale with text: paragraph margins, list gaps, padding inside text-bearing components. Use rem for media query breakpoints so the layout adapts to user font preferences.
Choose em for component-internal spacing where the component should scale as a unit. A button, card, or callout with font-size: 1em, padding: 0.75em 1em, and border-radius: 0.25em will scale proportionally when dropped into contexts with different font sizes. This is the pattern design systems use for self-contained components. Be careful with nesting — reset font-size on children if compounding would cause runaway scaling.
Choose px for dimensions that should not scale with font size: 1px borders, box-shadow offsets and blur, hairline dividers, fixed-width icons, image dimensions, transform offsets, and any pixel-perfect detail where visual consistency matters more than text scaling. Use px for things that are deliberately visual rather than typographic.
Avoid px for body text and for spacing tied to text. Setting font-size: 16px on body quietly overrides the user's browser font preference, which is a real accessibility regression. If your design must use px for some reason, at least set the root font-size in px so users who override it can do so via user stylesheets. The combination of 'px for borders and shadows, em for component internals, rem for typography and layout spacing' covers 95% of real-world CSS and is accessible by default.
A note on other units: vw/vh (viewport units) are useful for full-height sections but problematic for typography (text scales with viewport, not user preference) unless paired with clamp(). ch (character width) is useful for sizing inputs to a character count. % is essential for fluid layouts. Each has its place, but for everyday layout work, px, em, and rem cover the ground.
Conclusion
px, em, and rem are not competing units — they solve different problems. px gives you pixel-perfect dimensions that ignore user preferences; use it for borders, shadows, and visual details. em gives you compounding relative sizing inside components; use it for self-contained components that should scale as a unit. rem gives you root-relative sizing that honors user font preferences without compounding; use it for typography, layout spacing, and media queries. The combination is accessible, predictable, and scales cleanly. Default to rem for anything text-related, em for component internals, px for visual details — and your CSS will be more accessible and more maintainable than the px-everything default that still dominates too much of the web.