CSS Units: px, rem, em, %, viewport

Lengths come in absolute, font-relative, viewport-relative, and percentage flavors. Knowing which base each resolves against is what makes sizing predictable.

CSS6 min readConcept 6 of 43

Four families of length

Every CSS length is either **absolute** or **relative**. Absolute units (px, pt, cm, in) resolve to a fixed physical size. px is the only one you will use on screen; the others exist mostly for print. One px is defined as 1/96th of an inch, and browsers scale it on high-DPI displays so it stays visually consistent.

**Relative units** resolve against a base value, which is what makes them powerful for responsive and accessible design. The three sub-families are font-relative (em, rem, ch), viewport-relative (vw, vh, dvh...), and percentages. Each resolves against a different base, and mixing them up is the source of most sizing bugs.

Relative units are how you respect the user

Browsers let users change their default root font-size (16px on most desktops, often larger for accessibility). If you size everything in px, your layout ignores that choice. Size text in rem and the whole document scales when the user bumps their default, which is the accessible default.

Viewport units let a value respond to the screen without a media query. width: 90vw is always 90% of the viewport. Modern dvh/svh/lvh units finally make full-height mobile layouts reliable by accounting for the browser's collapsing address bar.

What each unit resolves against

**rem** resolves against the root element's (:root / html) font-size. Default is 16px, so 2rem = 32px unless the root is overridden. Change one value (html { font-size: 100%; }) and every rem in the document follows. This is why rem is the recommended unit for font-size.

**em** has two behaviors and that is the whole trap. When em is used on font-size, it is relative to the PARENT's font-size. When em is used on any other property (padding, margin, width, border-radius...), it is relative to the ELEMENT's OWN computed font-size. So padding: 1em on an element with font-size: 1.5em gives padding equal to 1.5x the parent's font-size.

**Percentages** resolve against the parent's corresponding dimension: width: 50% is half the parent's width, height: 50% is half the parent's height. The exception that surprises everyone: padding and margin percentages are ALWAYS relative to the parent's WIDTH, even for padding-top and margin-bottom. This is why padding-top: 50% on a zero-height box creates a 2:1 aspect ratio.

**Viewport units:** 1vw = 1% of viewport width, 1vh = 1% of viewport height. vmin/vmax pick the smaller/larger of the two. The newer svh (small), lvh (large), and dvh (dynamic) viewport-height units fix the mobile browser bug where 100vh was taller than the visible area because it ignored the address bar. Use 100dvh for full-height mobile layouts.

**ch** is the width of the 0 character in the current font. It is the right unit for line-length limits: max-width: 65ch keeps paragraphs at a comfortable reading width regardless of font size.

**Unitless** values are not laziness, they are correct. line-height: 1.5 is a multiplier applied to each child's own font-size, so it scales correctly inside nested elements. line-height: 1.5em would freeze the value at the parent's size. Always prefer unitless line-height.

Try it

Drag the root font-size and watch the rem bar rescale while the px bar stays fixed. See em compound when nested three levels deep.

Root font-size

16px

Drag to change html { font-size }. Every rem value follows; px does not.

rem vs px (both bars are 12 units wide)

200pxfixed, ignores root
200px
12rem12 × 16px = 192px
192px

em compounding (each level font-size: 1.2em)

Level 1: 19.2px
Level 2: 23.0px
Level 3: 27.6px

Each 1.2em multiplies off the parent. After 3 levels: 16, then 19.2, then 23.0, then 27.6px. rem would stay 16px at every level.

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1The root element has `font-size: 20px`. What is `2rem` in pixels?
  2. 2A parent has `font-size: 16px`. Its child has `font-size: 1.5em` and `padding: 1em`. What is the child's padding in pixels?
  3. 3An element has `padding-top: 50%` inside a parent that is 400px wide and 300px tall. What is the padding-top in pixels?

Remember this

  • px is absolute (1/96th inch). rem = root font-size. em = parent font-size (for font-size) or own font-size (for spacing).
  • rem for font-size, em for spacing, ch for line length, % for fluid widths, px for borders.
  • Percentage padding and margin always resolve against the parent's WIDTH, on every side.
  • Use 100dvh (not 100vh) for full-height mobile layouts to handle the address bar. Prefer unitless line-height.

Done with this concept?

Mark it complete to track your progress. No login needed.