px — pixels (absolute)

pxis 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: 320px for 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'sfont 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) or 1.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 height
  • 1vw = 1% of the viewport width
  • 1vmin = 1% of the smaller of vh or vw
  • 1vmax = 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, 100vhincludes 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

PropertyRecommended unitWhy
Font size (body)remRespects user browser preference
Font size (component-relative)emScales with component's own font size
Spacing (margin, padding)rem or pxrem for type-related spacing; px for fixed gaps
BorderpxAlways 1px — doesn't need to scale
Width (layout)% or vwResponsive to container/viewport
Height (full screen)100svhExcludes mobile browser chrome
Media queriespx or remrem-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


Written by Achraf A., founder of TheFreeAITools.