AniUI Academy

Units and Sizing

px, em, rem, percentages and viewport units, what each is relative to, the em compounding gotcha, why rem is the common default, and why unitless line-height usually wins.

8 min read

Every size in CSS — a font-size, a margin, a width — needs a unit, and which one you reach for has real consequences beyond just "does the number look right." Some units are relative to things you might not expect, and picking the wrong one is a common source of layouts that look right until something else on the page changes.

px: absolute, and exactly what it says

A pixel is a fixed, absolute unit — 16px is 16px, regardless of any parent's font-size or the viewport size. (In modern CSS, a "CSS pixel" is technically a device-independent unit that maps predictably across screen densities, but for everyday purposes: it doesn't scale relative to anything else on the page.)

.box { width: 300px; padding: 16px; }

px is predictable and simple, which makes it fine for things that genuinely shouldn't scale — a 1px border, for instance. It's a weaker choice for font sizes and spacing across a whole site, for the accessibility reason covered below.

em: relative to the element's own font-size

em is relative to the font-size that already applies to that element — which usually means its parent's computed font-size, since an element that hasn't set its own font-size inherits its parent's.

.parent { font-size: 20px; }
.child  { font-size: 1.5em; } /* 1.5 × 20px = 30px */

When em is used for something other than font-size itself — like padding: 1em — it's relative to that element's own computed font-size (after any font-size the element itself set), not its parent's.

The real compounding gotcha: if several nested elements each set their own font-size in em, each one multiplies against the one before it, compounding fast.

.level-1 { font-size: 1.2em; } /* relative to its parent, say 16px → 19.2px */
.level-2 { font-size: 1.2em; } /* relative to level-1's 19.2px → 23px */
.level-3 { font-size: 1.2em; } /* relative to level-2's 23px → 27.7px */

Three levels of nesting, each asking for a modest 1.2× increase, and the actual result has grown by nearly 75% — not the 20% any single rule seems to suggest. This is genuinely confusing to debug, because no single rule looks wrong in isolation; the problem only appears once you account for every ancestor's em font-size stacking together.

rem: relative to the root, once

rem ("root em") is always relative to the <html> element's font-size, no matter how deeply nested the element using it is.

html { font-size: 16px; }        /* the reference point for every rem in the document */
.level-1 { font-size: 1.2rem; }  /* 1.2 × 16px = 19.2px */
.level-2 { font-size: 1.2rem; }  /* also 1.2 × 16px = 19.2px — no compounding */

Nest rem-based font sizes as deeply as you like — every single one still traces back to the same one number. This is exactly why rem is the common default for both font sizes and spacing in real stylesheets today: the compounding problem em has simply doesn't exist, because there's only ever one thing to be relative to.

%: relative to the containing block — and that varies

A percentage is relative to the containing block, and what "containing block" means depends on the property:

.child { width: 50%; }   /* 50% of the parent's content width */
.positioned {
  position: absolute;
  top: 25%;   /* 25% of the positioned ancestor's height, not width */
}

Percentage width and height are relative to the parent's width and height respectively. Percentage values for offset properties like top and bottom on a positioned element are relative to the height of that element's containing block (you'll cover exactly what "containing block" means for positioned elements in the positioning lesson) — the key habit to build now is: don't assume every percentage in CSS is relative to the same thing. Always check which dimension of which box a given property's percentage refers to.

Viewport units: relative to the browser window

vw, vh, vmin and vmax are relative to the size of the viewport — the visible browser window — rather than to any element.

.hero {
  width: 100vw;   /* 100% of viewport width */
  height: 50vh;   /* 50% of viewport height */
}
  • vw — 1% of viewport width.
  • vh — 1% of viewport height.
  • vmin — 1% of whichever of width/height is currently smaller.
  • vmax — 1% of whichever is currently larger.

These are useful for full-bleed sections that should genuinely track the window size (a hero banner at 100vh), but they're a poor choice for body text — text sized purely in vw can become illegibly small on a narrow phone or absurdly large on an ultra-wide monitor, since it's tracking window size rather than a sensible, user-controllable baseline.

The accessibility angle: rem respects user preferences

This is a genuinely important, verified reason to default to rem for text: if a visitor increases their browser's default font-size setting, or zooms using text-only zoom, rem-based sizing scales with that preference, because it's still anchored to the root font-size the browser is now computing differently. Fixed px text sizing does not track that preference the same way — a 16px heading stays a fixed 16 CSS pixels regardless of the user's font-size preference, so it doesn't grow proportionally the way a 1rem heading built on the user's adjusted root size would. For visitors who rely on larger text, that difference is the gap between a page that respects their settings and one that doesn't.

Unitless line-height

line-height accepts a unitless number, and it's usually the right choice over a fixed unit:

body { line-height: 1.5; }   /* 1.5 × each element's own font-size */

A unitless line-height is inherited as the number itself, and then each descendant computes its own actual line-height by multiplying that number against its own font-size. So a 1.5 set once on body gives a normal 16px paragraph an 24px line-height, and a 32px heading a proportional 48px line-height, automatically, with no extra rule.

A fixed value like line-height: 24px is inherited as a literal 24px — useful for that one 16px paragraph, but wrong the moment a descendant with a much larger font-size inherits the same fixed 24px and ends up with lines crammed tighter than its own text is tall. Unitless is the one that scales correctly by default.

Try it yourself

Compare em compounding across three nested levels against rem staying flat across the same nesting, and see unitless line-height scale correctly against two very different font sizes in the same block.

Try it yourself
Loading playground...

What to remember

  • px is absolute and predictable, but doesn't scale with anything — a weak default for text.
  • em is relative to the element's own (usually inherited) font-size, and compounds when nested elements each set their own em font-size.
  • rem is always relative to the root <html> font-size, so it never compounds — the common default for spacing and font sizes.
  • % is relative to the containing block, and exactly what that means (parent width, parent height, ancestor's positioned box) depends on the property.
  • vw/vh/vmin/vmax are relative to the viewport — good for full-bleed sections, risky for body text.
  • rem text respects a user's browser font-size preference and zoom; fixed px text does not scale the same way — a real accessibility reason to prefer it.
  • Unitless line-height scales proportionally with each element's own font-size; a fixed unit doesn't adjust once a descendant's font-size differs.

Check yourself

4 questions · pass 3/4 to unlock Flexbox Fundamentals

up to 50
  1. 1.An element has font-size: 2em and its parent has font-size: 20px. What is the element's computed font-size?

  2. 2.Why is rem generally preferred over em for spacing and font sizes in a large stylesheet?

  3. 3.What is a percentage width relative to?

  4. 4.Why is line-height: 1.5 (unitless) usually preferred over line-height: 24px?

4 left to answer