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.
| Unit | What it's measured against | Common use on the card |
|---|---|---|
px | Nothing else — a fixed unit that never scales relative to anything on the page | The 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 height | The card's width inside a fluid grid column |
em | The 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 |
rem | The <html> element's font-size — one fixed reference point, however deep the element is nested | The card's padding and type scale — predictable no matter how deep it's nested |
vw / vh | One hundredth of the browser viewport's width or height, respectively | A 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 */
}