CSS has several length units, and picking the wrong one is a common source of layouts that don't scale or don't respect user font-size preferences.
| Unit | Relative to | Common use |
|---|---|---|
px | Nothing — an absolute (device) pixel | Borders, fine-grained values that shouldn't scale with text |
% | The relevant dimension of the containing block | Fluid widths, max-width |
em | The current element's font-size (or, for font-size itself, the parent's font-size) | Spacing that should scale with a specific element's text size |
rem | The root (<html>) element's font-size, always | Most font-size and spacing values — predictable regardless of nesting |
vw / vh | 1% of the viewport's width / height | Full-bleed hero sections, typography that scales with screen size |
em compounds when nested, which is the classic em gotcha:
.parent { font-size: 20px; }
.child { font-size: 1.5em; } /* 30px — 1.5 × parent's 20px */
.grandchild { font-size: 1.5em; } /* 45px — 1.5 × child's 30px, NOT 1.5 × the root */
rem avoids this entirely by always referring to the root font-size, no matter how deeply nested the element is — which is why most component libraries default to rem for font sizes and spacing, reserving em for the rare case where you specifically want a value to scale relative to its own element's font-size (like padding on a button that should grow if the button's own text size changes).