On this page
Pixels are intuitive. They map directly to design specs, Figma values, and what you see on screen. So it’s tempting to use them everywhere.
The problem is that pixels are fixed. They don’t respond to user preferences.
When someone increases their browser’s default font size from 16px to 20px because they have trouble reading small text, px values stay exactly where they are. rem values scale with the root font size. That’s the core difference, and it’s why the choice matters.
Convert px values to rem quickly.
How rem works
rem stands for root em. One rem equals the font size of the <html> element. Browsers default to 16px, so:
1rem = 16px
1.5rem = 24px
0.75rem = 12px
0.875rem = 14px
The formula is just division: target px / base font size = rem value. For a 24px value at a 16px base: 24 / 16 = 1.5rem.
What makes this useful is that if the user sets their browser default to 20px, 1.5rem becomes 30px. Everything scales proportionally. Fixed pixel values don’t participate in this at all.
How em works — and why it gets complicated
em is relative to the font size of the current element, not the root. That sounds similar to rem but behaves very differently once you nest elements:
.parent {
font-size: 1.25em; /* 20px at default 16px root */
}
.child {
font-size: 1.25em; /* 25px -- calculated from 20px, not 16px */
}
This compounding makes em values hard to reason about across nested elements. A value that looks like “1.25x” turns out to be different depending on where in the DOM you are.
Em is useful in specific situations: spacing inside a component that should stay proportional to that component’s own font size. Button padding is a common example. But for most layout and typography decisions, rem is more predictable.
When to use each unit
Use rem for font sizes, margin, padding, gap, and most layout dimensions. Anything that should scale when the user adjusts their preferences or zoom level.
Use em for spacing that’s genuinely relative to a component’s font size and should scale with it. Button padding and input height are classic cases.
Keep px for borders, box shadows, outlines, and decorative elements that shouldn’t scale. A 1px border should be 1px. A box shadow with a 2px offset doesn’t need to grow when someone zooms in.
There are exceptions. Media query breakpoints are typically better in em (not rem — the root font size doesn’t affect breakpoints in the same way). Icon sizes often stay in px if they’re fixed-dimension SVGs. Use judgment.
The accessibility case for rem
WCAG 1.4.4 requires that text can be resized up to 200% without loss of content or functionality. Pixel-based layouts often fail this because they don’t reflow when the user increases their browser font size or zooms.
Rem-based layouts adapt. Sections don’t overlap, text doesn’t overflow its containers, and the experience stays usable at larger sizes.
This isn’t just about edge cases. Around 300 million people globally have low vision. Many adjust their browser font size regularly. Building with rem means they don’t have to fight your layout to read the page.
Converting Figma values
Design tools export everything in pixels. The fastest conversion is dividing by your base font size (px / 16 = rem for the standard 16px root). But when you’re translating a whole component’s CSS from Figma, doing that math one value at a time gets tedious.
A batch converter lets you paste a block of CSS with pixel values and get the whole thing back with rem equivalents. You can also set a custom base font size if your project uses something other than 16px.
/* Paste this */
font-size: 18px;
line-height: 28px;
padding: 12px 24px;
margin-bottom: 32px;
/* Get back this (base 16px) */
font-size: 1.125rem;
line-height: 1.75rem;
padding: 0.75rem 1.5rem;
margin-bottom: 2rem;