CSS Units Explained

px, em, rem, vh, vw and when to use each

Quick Reference

UnitTypeRelative ToBest For
pxAbsoluteNothing (fixed)Borders, shadows, fine details
emRelativeParent font-sizeComponent-scoped spacing
remRelativeRoot (<html>) font-sizeFont sizes, global spacing
%RelativeParent elementWidths, fluid layouts
vh / vwViewportViewport height / widthFull-screen sections, hero areas
chRelativeWidth of "0" characterInput widths, readable line lengths
frRelativeAvailable grid spaceCSS Grid column/row sizing
clamp()Functionmin, preferred, maxResponsive typography, fluid sizing

The Units

px Pixels

Relative to: Nothing — absolute, fixed-size unit
200px
This bar is always exactly 200px wide, regardless of screen size or font settings.
border: 1px solid #333;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
border-radius: 8px;
Use forBorders, box-shadows, border-radius, fine visual details that should stay constant.
Avoid forFont sizes, padding, widths — these should scale with user preferences.

em Relative to parent font-size

Relative to: Parent element's font-size — compounds when nested
Parent: font-size 16px
1.5em = 24px
Nested 1.5em = 36px
Nested again 1.5em = 54px
em compounds! Each nested 1.5em multiplies: 16 → 24 → 36 → 54
/* Padding scales with font-size */
.button {
  font-size: 1em;
  padding: 0.5em 1em; /* scales with text */
}
Use forComponent padding/margins that should scale with the component's text size.
Avoid forGlobal font sizes — nesting makes values unpredictable. Use rem instead.

rem Root em

Relative to: <html> font-size (default 16px) — always consistent
Root: font-size 16px (browser default)
1.5rem = 24px
Nested 1.5rem = still 24px
Nested again 1.5rem = still 24px
rem never compounds! Every 1.5rem is always 24px (1.5 × 16px root).
html { font-size: 16px; } /* or 62.5% for easy math */

h1 { font-size: 2rem; } /* 32px */
h2 { font-size: 1.5rem; } /* 24px */
body { font-size: 1rem; } /* 16px */
em vs rem — the key difference
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)
Parent container (100%)
width: 75%
width: 50%
width: 25%
.sidebar { width: 30%; }
.main-content { width: 70%; }

img { max-width: 100%; } /* responsive images */
Use forFluid widths, responsive images (max-width: 100%), layout proportions.
Avoid forHeights (parent needs explicit height), font sizes (prefer rem).

vh / vw Viewport Height / Viewport Width

Relative to: Browser viewport — 1vh = 1% of viewport height, 1vw = 1% of viewport width
50vw (half your screen width)
Resize your browser to see this bar change. It's always 50% of the viewport width.
/* Full-screen hero section */
.hero {
  height: 100vh;
  width: 100vw;
}

/* 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).

ch Character 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; }

/* Consistent input sizing */
input[type="email"] { width: 30ch; }
Use forMax-width on text blocks, input field widths, readable line lengths.
Avoid forLayout widths, non-text elements, anything unrelated to character count.

fr Fraction (CSS Grid only)

Relative to: Available space in the grid container — only works in grid-template-columns / grid-template-rows
1fr
2fr
1fr
grid-template-columns: 1fr 2fr 1fr — the middle column gets twice the space.
200px (fixed)
1fr
1fr
grid-template-columns: 200px 1fr 1fr — fixed sidebar + two equal fluid columns.
/* Holy grail layout */
.layout {
  display: grid;
  grid-template-columns: 250px 1fr 200px;
  gap: 20px;
}

/* Equal columns */
.grid { grid-template-columns: repeat(3, 1fr); }
Use forCSS Grid columns and rows, dividing remaining space proportionally.
Avoid forAnything outside of CSS Grid — fr does not work in Flexbox or regular properties.

clamp() / min() / max() Modern Responsive Functions

Syntax: clamp(minimum, preferred, maximum) — picks the middle value, clamped to the range
This text uses clamp(14px, 2.5vw, 28px)
Resize your browser: the text smoothly scales between 14px minimum and 28px maximum.
width: clamp(200px, 50%, 600px)
The bar is 50% of the container, but never smaller than 200px or larger than 600px.
/* Fluid typography */
h1 { font-size: clamp(1.5rem, 4vw, 3rem); }

/* Fluid container */
.container { width: min(90%, 1200px); }

/* Minimum padding */
.card { padding: max(1rem, 3vw); }
Cheat sheet for the three functions
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().

Which Unit Should I Use?

Practical Recommendations

Font sizes?
rem
Respects user's browser settings. Predictable, doesn't compound.
Padding & margins?
rem or em
rem for global spacing, em when padding should scale with the component's own text size.
Container widths?
% or min()
width: min(90%, 1200px) creates a responsive container with a max width.
Borders & shadows?
px
These are fine details that don't need to scale. 1px border should stay 1px.
Full-screen sections?
dvh / vh
100dvh for mobile-safe full-viewport height. Fallback: 100vh.
Responsive text sizing?
clamp()
clamp(1rem, 2.5vw, 2rem) scales smoothly without breakpoints.
Grid columns?
fr
Distributes remaining space. Mix with px for fixed + fluid columns.
Readable line length?
ch
max-width: 65ch creates ideal 45-75 char reading width.
Media queries?
em (or px)
em in media queries respects zoom level. px works if you prefer simplicity.
Images?
% with max-width
max-width: 100% makes images responsive without overflowing their container.