em = relative to the PARENT, compounds when nested. rem = relative to the ROOT, always the same value no matter how deep.
Rule of thumb: rem for font-size, em for padding/margins within components.
Use forFont sizes, spacing systems, layout widths — anything that should respect user's browser font settings.
Avoid forBorders and shadows (too small at 1px, too big at 1rem).
%Percentage
Relative to: Parent element (width for width, height for height, font-size for font-size)
/* Modern alternative (accounts for mobile browser bars) */ .hero { height: 100dvh; }
Mobile gotcha
On mobile browsers, 100vh can include the area behind the address bar, causing content to be hidden. Use 100dvh (dynamic viewport height) instead for reliable full-screen sections.
Use forFull-screen sections, viewport-relative sizing, scroll-based layouts.
Avoid forText sizing (use clamp() instead), widths on text containers (too wide on large screens).
chCharacter Width
Relative to: Width of the "0" character in the current font
width: 40ch
This paragraph is set to 40ch wide, roughly 40 characters per line. It creates comfortable reading widths.
width: 70ch
This paragraph is set to 70ch wide. The ideal line length for reading is 45-75 characters, and ch units let you control this precisely regardless of font size.
/* Readable paragraph width */ p { max-width: 65ch; }
clamp(min, preferred, max) = fluid value with floor and ceiling. min(a, b) = picks the SMALLER value. Great for "max-width without max-width". max(a, b) = picks the LARGER value. Great for minimum sizes.
Use forFluid typography, responsive containers without media queries, smooth scaling.
Avoid forFixed-size elements like borders. Keep it simple — don't nest clamp() inside clamp().