px — pixels (absolute)
px is the most intuitive unit — one pixel on the screen. It's absolute: 16px is always 16 pixels regardless of screen size, parent size, or user font preferences.
When to use px:
- Borders:
border: 1px solid— you always want a 1-pixel line - Shadows:
box-shadow: 0 2px 4px rgba(0,0,0,0.1) - Minimum sizes:
min-width: 320pxfor a mobile breakpoint - Media queries:
@media (max-width: 768px)
When NOT to use px for font-size: If a user has set their browser default font to 20px (for accessibility), font-size: 16px overrides their preference. Use rem for font sizes instead.
rem — root em (relative to root)
rem is relative to the root element's (<html>) font size. By default, browsers set the root font size to 16px — so 1rem = 16px by default.
/* Default browser: 1rem = 16px */
font-size: 1rem; /* 16px */
font-size: 1.5rem; /* 24px */
font-size: 0.875rem; /* 14px */
/* If user changes browser default to 20px: */
font-size: 1rem; /* 20px — respects user preference */When to use rem:
- Font sizes —
remrespects the user's browser font preference - Spacing (padding, margin) when you want it to scale with font size
- Component sizes that should be consistent regardless of nesting depth
A common pattern: set html { font-size: 62.5%; } so that 1rem = 10px — making the math intuitive (1.6rem = 16px, 2.4rem = 24px).
em — relative to parent
em is relative to the current element's font size (or the parent's font size when used for font-size itself). This is where it gets confusing — em compounds through nesting.
/* Parent: font-size: 16px */
.parent {
font-size: 16px;
}
/* Child: 1.5em = 24px (1.5 × 16) */
.child {
font-size: 1.5em;
}
/* Grandchild: 1.5em = 36px (1.5 × 24) — compounded! */
.grandchild {
font-size: 1.5em;
}When to use em:
- Padding on buttons —
padding: 0.5em 1emscales with the button's own font size, making the button proportional regardless of font size - Line height —
line-height: 1.6(unitless) or1.6emscales with the element's font size - Components that should scale with their own font size
Avoid em for font sizes in deeply nested structures — the compounding effect creates unexpected results.
% — percentage
Percentage is relative to the parent element's value for most properties:
/* Width: 50% of parent's width */
width: 50%;
/* Font size: 125% of parent's font size */
font-size: 125%;
/* Padding/margin: % of parent's WIDTH (even for top/bottom) */
padding-top: 10%; /* 10% of parent's width — useful for aspect ratio */Key quirk: Percentage values for padding and margin are always relative to the parent's width — even for top and bottom padding. This is used intentionally to create aspect-ratio boxes.
vh and vw — viewport units
Viewport units are relative to the browser window size:
1vh= 1% of the viewport height1vw= 1% of the viewport width1vmin= 1% of the smaller of vh or vw1vmax= 1% of the larger of vh or vw
/* Full-screen hero section */
height: 100vh;
/* Full-width element regardless of parent */
width: 100vw;
/* Responsive font that scales with viewport */
font-size: clamp(1rem, 2.5vw, 2rem);Mobile caveat: On mobile browsers, 100vh includes the browser's address bar, which appears and disappears as the user scrolls. This causes layout jumps. The fix: use 100svh (small viewport height) in modern CSS — it excludes the browser chrome.
The modern unit decision guide
| Property | Recommended unit | Why |
|---|---|---|
| Font size (body) | rem | Respects user browser preference |
| Font size (component-relative) | em | Scales with component's own font size |
| Spacing (margin, padding) | rem or px | rem for type-related spacing; px for fixed gaps |
| Border | px | Always 1px — doesn't need to scale |
| Width (layout) | % or vw | Responsive to container/viewport |
| Height (full screen) | 100svh | Excludes mobile browser chrome |
| Media queries | px or rem | rem-based queries respect font zoom |
clamp() — responsive without media queries
The clamp(min, preferred, max) function creates fluid values that scale between a minimum and maximum:
/* Font scales from 1rem to 2rem based on viewport */
font-size: clamp(1rem, 2.5vw, 2rem);
/* Padding scales from 1rem to 4rem */
padding: clamp(1rem, 5vw, 4rem);
/* Width between 300px and 600px */
width: clamp(300px, 50%, 600px);This eliminates many breakpoint-based media queries for typography and spacing — the value adapts continuously rather than jumping at defined widths.
Related tools
- Free CSS Minifier — minify your CSS after building out your unit system
- Color Contrast Checker — verify text meets WCAG contrast at any font size
Written by Achraf A., founder of TheFreeAITools.