CSS Units Explained: px, rem, em, %, and When Each One Is the Right Call
2026-08-24
font-size: 16px and font-size: 1rem can render identically — until they don’t. The unit you choose isn’t a flavor, it’s a resolution rule: it decides what the length resolves against, and therefore what happens when the browser zooms, the root font-size changes, the parent resizes, or the viewport does. Most CSS bugs that look like “layout broke for no reason” are unit bugs. This guide gives you a decision table for picking the right one the first time.
Absolute vs Relative
Every CSS length is either absolute or relative:
- Absolute units (
px, and the print-orientedpt,cm,mm,in) are fixed.16pxis 16 CSS pixels, always. They don’t scale with anything. - Relative units resolve against something else: the root font-size (
rem), the element’s own font-size (em), the parent (%), the viewport (vw,vh,dvh), or the font metrics (ch,ex).
The moment zoom or layout changes, absolute units stay put and relative units move. That’s the entire job of this guide — deciding which behavior each property wants.
px: The Design Baseline
Pixels are the design unit: most designs are handed over in px, and 1px is the crispness floor for borders, hairline dividers, and box-shadow offsets.
border: 1px solid #ddd; /* always one crisp line */
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
The trap is applying px to text. font-size: 14px ignores the user’s font-size preference and browser zoom settings — which is why px text is the classic accessibility complaint. Rule of thumb: px for structure and effects, rem for text.
rem: The Root-Anchored Scale
rem is “root em”: one rem equals the font-size of the <html> element. Change the root and every rem in the document scales together:
html { font-size: 100%; } /* 16px by default */
.title { font-size: 2rem; } /* 32px when root is 16px */
.body { font-size: 1rem; } /* 16px */
Because it’s anchored to one root value, rem gives you a document-wide scale: bump the root and the whole typography system scales. And because browsers and user preferences set that root, rem text respects user font-size settings — the accessible default. This is why modern CSS resets encourage sizing text in rem. (Convert between the two on the fly with the Px to Rem Converter.)
em: Relative to the Element — and It Compounds
em is relative to the element’s own font-size. Used on the element itself it’s elegant: padding: 0.5em on a button scales its padding with its text.
The danger is compounding inside nested elements. em on descendants is measured against the nearest ancestor’s font-size, and ancestors’ ems multiply:
/* Each nesting level multiplies: 16px → 25.6px → 41px → 65px */
div { font-size: 1.6em; } /* inside a 16px parent → 25.6px */
div div { font-size: 1.6em; } /* 41px */
div div div { font-size: 1.6em; } /* 65px */
A rule that looks like “1.6×” becomes “1.6ⁿ×” as it nests. em is great for component-internal spacing that should scale with that component’s text; it’s dangerous for a global type scale, where rem is the safer anchor.
%: Two Very Different Rules
% is the most overloaded unit because its meaning depends on the property:
- On font-size,
%is relative to the inherited font-size:font-size: 150%= 1.5 × parent’s font-size (same behavior as1.5em). - On layout properties (width, margin, padding, top/left),
%is relative to the containing block — and percentages of width vs height differ (height percentages are often unresolvable without an explicit parent height).
.child { width: 50%; } /* half the parent's width */
.parent { font-size: 120%; } /* 1.2 × grandparent's font-size */
So % for widths/margins is fluid-layout bread and butter, and % for font-size is effectively em — pick based on which one you actually mean.
Viewport Units: vw, vh, dvh
Viewport units track the browser window: 1vw = 1% of viewport width, 1vh = 1% of viewport height, vmin/vmax the smaller/larger of the two.
.hero { height: 100vh; } /* full window height */
.fluid { font-size: clamp(1rem, 4vw, 2.5rem); } /* fluid type, clamped */
Two practical notes:
100vhand mobile. On phones the address bar eats part of the “viewport,” so100vhcan overshoot and clip.dvh(dynamic viewport height) tracks the visible viewport and is the modern fix:height: 100dvh.- Fluid type.
vw-based font-size withclamp()gives type that scales with screen width but stays within bounds — the modern replacement for several breakpoint hack lines.
ch, ex, and the Rest
ch equals the width of the 0 character in the current font — ideal for monospace-ish sizing: width: 80ch on a <code> block gives a comfortable line length in characters. ex is the height of an x; rarely useful. Others (cm, mm, in, pt) matter only for print stylesheets.
Choosing a Unit: A Decision Table
| What you’re sizing | Unit | Why |
|---|---|---|
| Text (font-size) | rem | Respects user settings; scales with the root |
| Borders, shadows, crisp lines | px | Fixed and crisp at any zoom |
| Spacing inside a component that scales with its text | em | Scales with that component — but watch nesting |
| Widths / fluid layout | % (or flex/grid) | Follows the container |
| Full-screen heroes, overlays | vw/dvh | Follows the visible viewport |
| Monospace line lengths | ch | ”Characters” is the right unit for text columns |
The cleanest modern strategy: rem for type, px for borders and shadows, % or grid/flex tracks for layout, em only inside self-contained components, and dvh for anything that must fill the screen.
Quick Reference
- Absolute units (
px,pt,cm,mm) are fixed; relative units resolve against a parent, root, or viewport. rem= root font-size — document-wide scale, respects user settings. Default for text.em= the element’s own font-size; compounds in nested elements (1.6ⁿ), great inside components, risky for a global scale.%is overloaded: on font-size it means ”× inherited font-size” (likeem); on width/margin it means ”× containing block.”1vw/1vh= 1% of viewport width/height; usedvhfor mobile full-screen (address bars).ch= width of0; ideal for line lengths in monospace.- Strategy:
remtype +pxeffects +%/flex/grid layout +eminside components +dvhfor full-screen.
To move a design from px to rem without arithmetic, the Px to Rem Converter converts any value and shows the scale. Units are the quiet layer under every layout, grid, and gradient you write — get the resolution rule right and the layout holds at every zoom level you never tested.