rem vs em vs px in CSS: Which Unit Should You Use?
px is fixed, rem is relative to the root font size, em is relative to the current element's font size. What each unit means, why rem is the safe default for font sizes and spacing, where em and px still make sense, and when to reach for %, vw, vh and dvh.
CSS has a lot of units. Three cover most sizing decisions: px, rem and em. The short answer:
Use
remfor font sizes and spacing,pxfor thin details like borders, andemwhen something should scale with its own text.
px — pixels
.card { padding: 16px; border: 1px solid #ddd; }
A CSS pixel is a fixed size (not necessarily one physical screen pixel — high-resolution screens use several). It doesn't change based on anything else on the page.
Problem: if a user increases their browser's default font size for readability, text set in px doesn't grow. That's an accessibility issue. (Web accessibility basics)
rem — relative to the root
h1 { font-size: 2rem; } /* 2 × root font size */
.card { padding: 1rem; } /* 1 × root font size */
rem means "root em": a multiple of the font size on the <html> element. Browsers default that to 16px, so 1rem = 16px, 1.5rem = 24px, 0.875rem = 14px.
Why it's the safe default: if a user sets their browser to larger text, everything in rem scales with it, proportionally. And it's predictable — 1rem is the same everywhere on the page.
Tailwind CSS uses rem for its spacing and type scale for this reason. (What is Tailwind CSS?)
em — relative to the current font size
.button { font-size: 1.125rem; padding: 0.5em 1em; }
em is a multiple of the current element's font size. Here the padding scales with the button's text: make the text bigger and the padding grows to match. That's exactly what you want for buttons, badges and icons next to text.
The catch: em compounds. Used for font sizes in nested elements, it multiplies:
li { font-size: 1.2em; }
A list inside a list inside a list gets 1.2 × 1.2 × 1.2 = 1.73 times bigger. This is why rem replaced em for most font sizes.
Side by side
| Unit | Relative to | Good for |
|---|---|---|
px |
Nothing (fixed) | Borders, hairlines, shadows, fine details |
rem |
Root (<html>) font size |
Font sizes, spacing, layout widths |
em |
The element's own font size | Padding/margins that scale with text, icons |
% |
The parent element (usually) | Widths inside a container |
vw / vh |
Viewport width / height | Full-screen sections, fluid type |
dvh |
Visible viewport height on mobile | Full-height layouts on phones |
ch |
Width of the "0" character | Readable line lengths (max-width: 65ch) |
The other units worth knowing
%—width: 50%is half the parent's width.vw/vh—100vhis the full height of the browser window. On phones,100vhcan be taller than the visible area because of the address bar;100dvh(dynamic viewport height) fixes that. (Responsive design basics)clamp()— not a unit, but useful:font-size: clamp(1.5rem, 4vw, 3rem)grows with the screen but stays within a minimum and maximum.
The old "62.5%" trick
Some code sets html { font-size: 62.5%; } so that 1rem = 10px for easier maths. It works, but it overrides the user's preferred base size in a confusing way and fights with libraries that assume 16px. Modern advice: leave the root alone and think in rem directly.
A sensible setup
body { font-size: 1rem; line-height: 1.5; }
h1 { font-size: clamp(2rem, 5vw, 3rem); }
.container { max-width: 70rem; padding-inline: 1rem; }
.prose { max-width: 65ch; }
.card { border: 1px solid var(--border); border-radius: 0.5rem; }
.button { padding: 0.5em 1em; }
(For the layout itself, see Flexbox vs Grid.)
EasySpawn hosts your site on a fast server with HTTPS, and Claude Code can tidy your CSS units for you while you watch the result live. See how it works or join the waitlist.
Related: Responsive Design Basics · Flexbox vs Grid · CSS Position Explained · Web Accessibility Basics
Keep reading
"ReferenceError: window is not defined" in Next.js: How to Fix It
window, document and localStorage only exist in the browser. Next.js renders components on the server first, where they don't exist. Why it happens, and the fixes: useEffect, 'use client', typeof checks, dynamic imports with ssr: false, and libraries that touch window on import.
What Is TypeScript? JavaScript With Labels, Explained for Beginners
TypeScript is JavaScript plus types: labels that say what kind of value each thing holds, checked before your code runs. What it looks like, why AI tools default to it, what .ts and .tsx files are, and how to read the red squiggles.