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.
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
16pxDrag to change html { font-size }. Every rem value follows; px does not.
rem vs px (both bars are 12 units wide)
em compounding (each level font-size: 1.2em)
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.
Remember this
pxis absolute (1/96th inch).rem= root font-size.em= parent font-size (for font-size) or own font-size (for spacing).remfor font-size,emfor spacing,chfor line length,%for fluid widths,pxfor borders.- Percentage
paddingandmarginalways resolve against the parent's WIDTH, on every side. - Use
100dvh(not100vh) for full-height mobile layouts to handle the address bar. Prefer unitlessline-height.
Done with this concept?
Mark it complete to track your progress. No login needed.