Lesson 16 of 32
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.
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.
What to remember
pxis absolute and predictable, but doesn't scale with anything — a weak default for text.emis relative to the element's own (usually inherited) font-size, and compounds when nested elements each set their own em font-size.remis 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/vmaxare relative to the viewport — good for full-bleed sections, risky for body text.remtext respects a user's browser font-size preference and zoom; fixedpxtext does not scale the same way — a real accessibility reason to prefer it.- Unitless
line-heightscales 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
1.An element has
font-size: 2emand its parent hasfont-size: 20px. What is the element's computed font-size?2.Why is rem generally preferred over em for spacing and font sizes in a large stylesheet?
3.What is a percentage width relative to?
4.Why is
line-height: 1.5(unitless) usually preferred overline-height: 24px?
4 left to answer