rem for anything that should grow when a user raises their default font size — type, spacing, breakpoints, max-widths. px for things that should not — borders, hairlines, small radii. em when a value should scale with the element's own text, such as button padding.
| Property | Unit | Reasoning |
|---|---|---|
font-size | rem | The whole point of rem. A user who sets a 20px default gets a proportionally larger interface instead of the same 16px you assumed. |
margin / padding (layout) | rem | Spacing should scale with type, or a larger font size makes the layout feel cramped rather than larger. |
padding (inside a button or chip) | em | Scales with the element's own font-size, so one .btn rule works at every size variant. |
border-width | px | A hairline is a hairline. Scaling a 1px border to 1.25px produces a blurred line on non-integer ratios. |
border-radius (small) | px | A 4px radius scaled to 5px is not more accessible, just different. |
max-width on text | ch or rem | ch measures in character widths, which is what a reading measure actually is. 65–75ch. |
| Media query breakpoints | rem | Moves the breakpoint when the user enlarges text, so the layout switches at the right apparent size. |
line-height | unitless | A unitless value multiplies the element's own font-size and inherits correctly. line-height: 1.5, never 1.5rem. |
| Shadows, outlines | px | Visual effects, not layout. They do not need to grow with type. |
The 62.5% trick, and why to skip it
The trick sets html { font-size: 62.5%; } so 1rem equals 10px and the mental arithmetic gets easier — 1.6rem is 16px. It works, and it has a real cost: you have overridden the user's chosen default font size for the whole document. Someone who set their browser to 20px because they need 20px now gets 12.5px as the base.
Modern tooling makes the arithmetic argument moot anyway. Write the values you mean, or generate them — the px to rem table covers the whole scale.
rem in media queries has a catch
rem and em in a media query both resolve against the browser default font size, not against your :root rule. This is by design — media queries are evaluated before the document, so there is no root to reference yet. It also means the 62.5% trick does not shift your breakpoints, which surprises people who expect it to.
The type scale is where rem pays off
| rem | px @ 16px root | px @ 20px root | Typical role |
|---|---|---|---|
| 0.75 | 12 | 15 | Captions, legal text — the smallest defensible size |
| 0.875 | 14 | 17.5 | Secondary text, table cells, form hints |
| 1 | 16 | 20 | Body text. The baseline everything else references |
| 1.125 | 18 | 22.5 | Comfortable long-form body |
| 1.25 | 20 | 25 | Lede paragraphs, small headings |
| 1.5 | 24 | 30 | Section headings |
| 2 | 32 | 40 | Page headings |
| 3 | 48 | 60 | Display |
The right-hand column is the argument in one image: a user who raised their default gets an interface that is proportionally larger everywhere, with no per-element work from you. Had those been px values, they would have got the same 12px caption and given up.
:root {
/* No 62.5% override — 1rem is whatever the user chose */
--step--1: 0.875rem;
--step-0: 1rem;
--step-1: 1.25rem;
--step-2: 1.5rem;
--step-3: 2rem;
/* Fluid where it helps, still anchored in rem */
--step-4: clamp(2.5rem, 1.8rem + 3vw, 3.5rem);
}
body { font-size: var(--step-0); line-height: 1.55; }
h2 { font-size: var(--step-2); line-height: 1.2; }
/* em: padding follows the button's own size */
.btn { padding: 0.6em 1.2em; border: 1px solid; border-radius: 6px; }
Questions
Is 1rem always 16px?
Only if the user has not changed their browser default and no stylesheet has overridden html { font-size }. 16px is the default default — treating it as a guarantee is what breaks accessibility for people who changed it deliberately.
Should I use px for anything at all?
Yes — borders, hairlines, small radii, shadow offsets, and anywhere a value is a visual constant rather than a piece of typographic rhythm. The rule is not "px is bad", it is "px does not scale, so use it only where scaling would be wrong".
What about vw and vh for font sizes?
Never alone: a pure vw font size cannot be enlarged by the user at all, which fails WCAG 1.4.4. Inside a clamp() with rem bounds it is fine, because the rem terms keep it responsive to user preference.
How do I convert px to rem quickly?
Divide by the root size — 24px ÷ 16 = 1.5rem. The px to rem converter has the full table at every common root size.