Skip to main content
CodeOath
← All posts

HTML & CSS82 min total · 18 parts

CSS Fundamentals: The Box Model, Specificity, Positioning, and Layout

Part 11 of 18 · ~2 min

Units: px, %, em, rem, and Viewport Units

CSS offers several length units, and reaching for the wrong one is a reliable way to end up with a layout that doesn't scale, or doesn't respect a visitor's own font-size preference.

UnitWhat it's measured againstCommon use on the card
pxNothing else — a fixed unit that never scales relative to anything on the pageThe card's border width; anything meant to stay put even if a visitor bumps up their text size
%Whichever dimension of the containing block applies — width for a width, height for a heightThe card's width inside a fluid grid column
emThe font-size already active on that same element (its parent's, specifically, when the property being set is font-size itself)Icon spacing that should track one particular element's text size
remThe <html> element's font-size — one fixed reference point, however deep the element is nestedThe card's padding and type scale — predictable no matter how deep it's nested
vw / vhOne hundredth of the browser viewport's width or height, respectivelyA full-bleed featured card above the grid

The card's spacing is built on rem from the start:

.card {
  padding: 1rem;
  font-size: 1rem;
}
.card h3 {
  font-size: 1.125rem;
}

Add a "Featured" variant of the card — larger, meant to stand alone at the top of the category page — and the trap shows up the moment someone reaches for em instead, because em compounds every time it nests:

.card--featured {
  font-size: 1.2rem; /* the featured card's own base size */
}
.card__wishlist-count {
  font-size: 1.5em;   /* 1.5 × the card's 1.2rem → fine, on its own */
}
.card__wishlist-count .badge-number {
  font-size: 1.5em;   /* 1.5 × the wishlist-count's OWN size, not the card's — 2.25× total */
}

Nobody set out to make the little number inside the wishlist badge more than double the size of its own label — each 1.5em looked reasonable in isolation, and the compounding only appears once both are nested together and rendered. Switch either declaration to rem and the compounding simply can't happen — rem always measures against one fixed reference point, <html>'s own font-size, and nesting depth never enters into it. That's why the card's padding and type scale are pinned to rem throughout this reference, with em reserved for the narrower case where a value is genuinely meant to track its own element's text size rather than the page's.

The featured card also needs to run edge-to-edge on a phone screen, which is what viewport units are for:

.card--featured {
  width: 100vw;
  margin-left: calc(-50vw + 50%); /* pull it out of a centered, max-width container */
}