If you have ever wondered whether to write padding: 16px or padding: 1rem, this guide is for you. We'll cover what rem actually means, why accessibility-conscious developers reach for it, the exact conversion formula, and how to migrate an existing stylesheet without breaking your design.
Need to convert a value right now?
Open Px to Rem Converter →The conversion formula
One formula, no surprises:
rem = px ÷ root_font_size
px = rem × root_font_size
The browser's default root font size is 16px. So the math people memorise:
16px → 1rem
24px → 1.5rem
32px → 2rem
8px → 0.5rem
14px → 0.875rem
If your stylesheet sets html { font-size: ... } to anything other than 16px, divide by that value instead.
What rem actually is
rem stands for "root em". It is a length unit relative to the font size of the root element — the <html> element. The browser sets a default of 16px on html, but the user can change this in their browser settings, and your CSS can change it too.
Compare it to its cousin em:
emis relative to the parent element's font size — and compounds when nested. A1.2emheading inside a1.2emcontainer is actually 1.44× the parent's parent size.remis relative to the root only — no compounding.1remis always the same size everywhere on the page.
This is why most modern design systems standardise on rem: a single source of truth that scales the entire UI when needed.
Why rem beats px for accessibility
Open Chrome's settings → Appearance → Font size. You'll see options for "Very small", "Small", "Medium", "Large", "Very large". This setting changes the default font size of the html element. A user with low vision may set it to "Very large" (typically 24px instead of 16px).
Now imagine your button:
/* Pixel-based — ignores user preference */
.button { padding: 12px 24px; font-size: 16px; }
/* Rem-based — scales with user preference */
.button { padding: 0.75rem 1.5rem; font-size: 1rem; }
For a user with the "Very large" setting, the rem version's padding becomes 18px 36px and font becomes 24px — the entire button grows proportionally. The pixel version stays at 12px 24px, with text the user can barely read inside an undersized button.
The WCAG 2.2 — Resize Text success criterion expects content to scale up to 200% without loss of content or functionality. Rem-based layouts pass this naturally; pixel-based layouts often fail.
The 62.5% trick — convenient but controversial
You'll often see this in production CSS:
html { font-size: 62.5%; }
/* 62.5% of 16px = 10px */
body { font-size: 1.6rem; } /* = 16px */
.card { padding: 2.4rem; } /* = 24px */
Setting the root to 62.5% makes 1rem equal 10px, and the math becomes trivial: 1.6rem is 16px, 2.4rem is 24px, 0.8rem is 8px. Many designers love this because they can think in pixels but write rems.
The problem: you've overridden the user's browser font size preference. A user who set their browser to 20px now gets 20px × 62.5% = 12.5px as the root — smaller than they wanted. The accessibility benefit of rem is partially defeated.
The fix: always restore body text to a sensible size: body { font-size: 1.6rem; } means default text is 16px-equivalent of their root, not yours. The 62.5% only affects sizes you explicitly set. This is a reasonable compromise — but plain rem with no 62.5% override is the purest path.
When to still use px
Don't convert blindly. Some things genuinely belong in pixels:
- Borders —
border: 1px solid #cccstays a hairline at any zoom level.0.0625remrounds inconsistently across browsers and can look fuzzy. - Box shadows — Same idea. A
0 2px 4px rgba(0,0,0,0.1)shadow has a designed look that doesn't benefit from scaling. - Outline width — Focus rings and outlines are usually defined as 2px or 3px for visual consistency.
- Image dimensions — When you need an image to fit a known pixel grid (favicons, sprites, app icons), use px or no unit at all.
- Media query breakpoints — Modern recommendation is
em(which respects browser zoom), butpxis still common and acceptable.
Migrating an existing stylesheet
You don't have to convert everything at once. A pragmatic migration:
- Convert font sizes first. They have the biggest accessibility impact.
/* Before */ h1 { font-size: 32px; } p { font-size: 16px; } /* After */ h1 { font-size: 2rem; } p { font-size: 1rem; } - Convert spacing — padding, margin, gap. These also benefit from scaling.
/* Before */ .card { padding: 24px; margin-bottom: 16px; } /* After */ .card { padding: 1.5rem; margin-bottom: 1rem; } - Leave borders, shadows, and 1–2px decorative widths alone. They are fine as px.
- Test with browser zoom + custom font size. Set Chrome to "Very large" font and zoom to 200%. Your layout should remain usable.
For large stylesheets, our Px to Rem Converter has a bulk mode — paste your CSS, click "Convert px → rem", and review the output before committing.
Common conversions reference
Assuming the standard 16px root:
| Pixels | Rem | Common use |
|---|---|---|
| 8px | 0.5rem | Tight spacing, small gaps |
| 12px | 0.75rem | Captions, helper text |
| 14px | 0.875rem | Secondary text, labels |
| 16px | 1rem | Body text (default) |
| 18px | 1.125rem | Slightly larger body |
| 20px | 1.25rem | Lead paragraphs, h4 |
| 24px | 1.5rem | Card padding, h3 |
| 32px | 2rem | Section spacing, h2 |
| 48px | 3rem | Large headings, h1 |
| 64px | 4rem | Hero text, big margins |
Sass / SCSS helpers
If you write Sass, a small function turns the conversion into a one-liner:
// _functions.scss
@function rem($px, $base: 16) {
@return ($px / $base) * 1rem;
}
// usage
.card {
padding: rem(24); // → 1.5rem
font-size: rem(14); // → 0.875rem
}
Note: in modern Sass (1.65+), division uses math.div():
@use "sass:math";
@function rem($px, $base: 16) {
@return math.div($px, $base) * 1rem;
}
CSS-only with custom properties
You can do the same in plain CSS using a custom property:
:root {
--base-font-size: 16;
}
.card {
/* calc with unit-less variable */
padding: calc(24 / var(--base-font-size) * 1rem);
}
This is verbose but eliminates the build step. Most teams just compute the rem values upfront and write them directly.
Need to convert a stylesheet? Use the free Px to Rem Converter →