Web

CSS Units Explained: px, em, rem, vw, vh, and the Rest

Eight CSS units, four mental models, and one decision tree for picking the right one. With the actual rules browsers apply when they compute final pixel values.

11 min read

CSS gives you eight common length units, and they all reduce to pixels at render time. The interesting part is how they reduce — that's where designs scale gracefully or break in ways that take 30 minutes to debug.

The four families of units

  • Absolute: px, pt, pc. Fixed regardless of context.
  • Font-relative: em, rem, ex, ch. Scale with font size.
  • Viewport-relative: vw, vh, vmin, vmax. Scale with the viewport.
  • Container-relative: %, cqw, cqh, cqi, cqb. Scale with the parent or container.

px — the absolute "pixel"

CSS pixels are not physical pixels. They're a logical unit defined as 1/96th of an inch at a "reference viewing distance." On a typical monitor, 1 CSS px ≈ 1 device pixel. On a Retina or 4K display, 1 CSS px ≈ 2 or more device pixels — the browser doubles up so 16px text doesn't look microscopic.

When to use: borders, shadow offsets, hairline rules, anything you want pinned to a real visual size regardless of the user's zoom or font preference.

em — relative to the parent's font size

1em equals the computed font size of the element itself (or its parent for non-font-size properties). This is a footgun for compounding: a child element with font-size: 1.2em inside a parent with font-size: 1.2em ends up at 1.44× the grandparent's size.

.outer { font-size: 18px; } /* 18px */ .middle { font-size: 1.2em; } /* 21.6px (18 × 1.2) */ .inner { font-size: 1.2em; } /* 25.92px (21.6 × 1.2) */

When to use: component-internal scaling. If you want padding to grow with the button's text size, use em for the padding. The button gets larger and smaller in proportion to its own type.

rem — relative to the root

1rem equals the computed font size of <html>. Default is 16px in every modern browser. Unlike em, rem doesn't compound — every 1rem on the page is the same value.

When to use: most font sizes, spacing, and layout dimensions. The root font-size respects user accessibility zoom settings, so rem-based designs scale gracefully when users bump up their browser font preference.

The 62.5% trick

Setting html { font-size: 62.5%; } makes 1rem = 10px, so 1.6rem = 16px and so on. Cute math trick, but it breaks accessibility — users who set their browser default to 20px now get 12.5px. Use 16px defaults; live with the math.

% — relative to the parent property

Percent is interpreted in terms of the parent element's value of the same property. So 50% width is half the parent's width; 50% font-size is half the parent's font-size; 50% padding-top is half the parent's width (yes, top padding uses parent width — surprise gotcha).

When to use: child widths within a flex/grid container, image aspect ratios, anything tied to a parent box.

vw, vh, vmin, vmax — the viewport units

  • 1vw = 1% of the viewport width.
  • 1vh = 1% of the viewport height.
  • vmin = the smaller of vw and vh.
  • vmax = the larger of vw and vh.

When to use: hero sections that should always fill the screen, fluid typography (font-size: clamp(16px, 1vw + 12px, 24px)), full-bleed elements that need to ignore parent containers.

The mobile vh problem

On mobile browsers, 100vh historically included the area covered by the address bar — so a 100vh hero would have a chunk hidden under Safari's collapsing toolbar. The newer units svh (small viewport height), lvh (large), and dvh (dynamic) solve this. Use 100dvh when you want the hero to always be exactly the visible viewport regardless of toolbar state.

ch and ex — the type-aware units

  • 1ch = the width of the "0" character in the current font.
  • 1ex = the x-height of the current font.

When to use: max-width: 70ch on body text gives roughly the optimal reading line length (60–80 characters per line) regardless of font. Excellent for long-form content.

Container query units (cqw, cqh, cqi, cqb)

Newer in 2023+. Like vw/vh but relative to the nearest container declared with container-type: inline-size. Lets you size things based on the component's actual width rather than the viewport — a long-overdue piece of CSS finally landed.

The decision tree

  1. Hairline borders, shadows, sub-pixel UI: px.
  2. Most font sizes and spacing: rem.
  3. Component-internal proportional scaling: em.
  4. Hero / full-bleed: vw / vh (or dvh on mobile).
  5. Reading-friendly text width: ch.
  6. Component-aware sizing: cqw / cqi.
  7. Children inside flex/grid containers: %.

Convert anything

For quick conversions between units, use the Toolisk CSS Unit Converter. It supports custom root and viewport contexts so you can match your project's actual setup.

Key Takeaways

  • px is absolute (browser-managed); pt/pc are print-era leftovers.
  • em compounds (relative to parent); rem does not (relative to root).
  • rem-based sizing respects user accessibility zoom — px-only designs do not.
  • Use dvh, not vh, for mobile heros to handle Safari's collapsing toolbar.
  • Container query units (cqw, cqi) finally let components size relative to themselves.