TH The Sass Way
CSS Fundamentals

CSS Units Explained: px vs em vs rem vs % vs vw

CSS Units Explained: px vs em vs rem vs % vs vw
tldrCSS units are either absolute (px, which never changes) or relative (em, rem, %, vw, vh, which scale against a reference). Use rem for font sizes and spacing because it respects user settings, px for borders and fine details that shouldn't scale, % for fluid widths, and vw/vh for viewport-based sizing like full-height sections and fluid type.

The Short Answer: Which CSS Unit to Use

CSS units fall into two camps: absolute units like px that never change, and relative units like em, rem, %, vw, and vh that scale against something else. The right choice depends on what you want to happen when a user zooms, changes their default font size, or resizes the window.

Here is the rule most professionals actually follow:

Everything below explains why each unit behaves the way it does, so you can reach for the right one without guessing.

Absolute vs Relative Units

An absolute unit is fixed. A px is a px no matter where you put it or what the user does. Absolute units include px, cm, mm, in, pt, and pc, but in practice px is the only one you'll use on screen — the physical units (cm, in, pt) are meant for print stylesheets.

A relative unit measures against a reference point. Change the reference and the computed value changes with it. This is what makes relative units powerful for responsive and accessible design: one rule can adapt to many contexts.

Unit Type Relative to Best for
px Absolute Nothing (fixed) Borders, shadows, fine details
em Relative The element's own font-size Padding/margins that track text
rem Relative The root (<html>) font-size Font sizes, spacing, layout
% Relative The parent's corresponding value Fluid widths
vw Relative 1% of viewport width Fluid type, full-width elements
vh Relative 1% of viewport height Full-height sections

px: The Fixed Unit

px (pixel) is the CSS reference pixel. On modern high-density screens it isn't literally one hardware dot, but it stays visually consistent across devices.

.card {
  border: 1px solid #ddd;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

The upside: total predictability. 16px is 16px everywhere.

The downside: it ignores user preferences. If someone bumps their browser's default font size up to 20px because they have trouble reading small text, a font-size: 16px rule stays stubbornly at 16. That's an accessibility problem. (Modern browsers still enlarge px on page zoom, but not when a user changes their default text size — the two are different settings.)

Use px for details that genuinely shouldn't scale with text, and reach for a relative unit for anything typographic.

em: Relative to the Element

1em equals the current element's font-size. If an element's font size is 20px, then 1em is 20px for that element — for padding, margins, widths, and even for a nested font size.

.button {
  font-size: 18px;
  padding: 0.75em 1.5em; /* 13.5px vertical, 27px horizontal */
}

Because the padding is in em, a bigger button (larger font size) automatically gets proportionally bigger padding. That's exactly what you want for a component that ships at several sizes.

The em compounding trap

em inherits, and that causes surprises when you nest. Font sizes multiply down the tree:

.menu    { font-size: 1.2em; }
.menu li { font-size: 1.2em; } /* now 1.2 × 1.2 = 1.44em of the parent */

Three levels deep and your text is unexpectedly huge. This compounding is the single biggest reason developers moved font sizing to rem.

rem: Relative to the Root

rem means "root em." It's always relative to the font-size of the <html> element — no compounding, no matter how deep you nest. Since the browser default root size is 16px, 1rem is 16px unless you change it.

:root {
  font-size: 100%; /* = 16px by default; respects user settings */
}

h1   { font-size: 2rem; }    /* 32px */
p    { font-size: 1rem; }    /* 16px */
small{ font-size: 0.875rem; }/* 14px */

rem is the modern default for typography and spacing because it gives you two things at once: predictability (one reference point) and accessibility (it scales when a user changes their default font size). A handy trick is to build your spacing scale in rem so the whole layout breathes together when the root size changes.

If you're managing a design system, pair rem sizing with CSS custom properties so your scale lives in one place:

:root {
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
}

Percentages: Relative to the Parent

% is relative to the parent's corresponding property — but which property matters, and it trips people up:

That last rule is the classic gotcha. Vertical padding in percent is calculated from width, which is actually useful for maintaining aspect ratios but confusing the first time you meet it.

.column {
  width: 33.333%; /* three across in a flexible row */
}

Percentages shine for fluid widths inside a container. For layout structure, though, Flexbox and Grid usually give you cleaner control — see how sizing interacts with spacing in the CSS box model.

vw and vh: Relative to the Viewport

Viewport units measure against the browser window itself:

.hero {
  min-height: 100vh; /* fills the screen height */
}

Viewport units are unbeatable for full-bleed sections and fluid effects. But a few caveats:

Fluid typography with clamp()

Viewport units really pay off when combined with clamp() for text that scales smoothly between a floor and a ceiling:

h1 {
  font-size: clamp(1.75rem, 1rem + 3vw, 3rem);
}

This reads as "never smaller than 1.75rem, never larger than 3rem, and in between grow with the viewport." No media query needed. This technique pairs naturally with a mobile-first CSS approach, where you design for the small screen first and let type scale up.

em vs rem: The Question Everyone Asks

They differ in exactly one way — their reference point.

em rem
Relative to Element's own font-size Root font-size
Compounds when nested Yes No
Best for Component-local spacing Global type & layout
Predictability Lower (context-dependent) Higher (single source)

Practical guidance: use rem for font sizes across the site so nothing compounds unexpectedly. Use em inside a component when you want a value to track that component's text size — button padding, an icon sized to match the label, letter-spacing on a heading.

px vs rem: Which for Font Size?

Use rem. The reason is accessibility: a font size set in px ignores a user who has raised their browser's default text size, while rem honors it. Roughly 1 in 20 users runs a non-default font size, often because they need larger text — coding your type in px quietly locks them out.

Keep px for the things that should stay crisp regardless of text size: borders, outlines, and shadow offsets.

A Quick Decision Cheat-Sheet

You're sizing… Reach for
Body and heading font sizes rem
Margins and padding (global spacing) rem
Padding that should track a component's text em
A 1px border or hairline px
Column width in a flexible row %
Full-height hero section 100dvh (or 100vh fallback)
Type that scales with the screen clamp() with a vw middle value

The Bottom Line

You don't need every unit. A clean, accessible stylesheet mostly runs on rem for type and spacing, px for fine details, % for fluid widths, and vw/vh (or the newer dvh) for viewport-scaled effects. em earns its place for component-local sizing where you deliberately want values to follow the text.

Get those defaults into your muscle memory and the "which unit?" question mostly answers itself. When you're ready to see how units interact with sizing and spacing in practice, the CSS box model is the next piece of the puzzle.

FAQ

What is the difference between em and rem?

Both are relative units, but their reference differs. em is relative to the element's own font-size, so it compounds when you nest elements. rem is always relative to the root (html) font-size, so it never compounds. Use rem for global font sizes and spacing to stay predictable, and em when you want a value to track a specific component's text size.

Should I use px or rem for font size?

Use rem. A font size set in px ignores users who raise their browser's default text size, which is an accessibility problem. rem scales with that setting while staying predictable. Reserve px for details that shouldn't scale with text, such as 1px borders, outlines, and box-shadow offsets.

When should I use vw and vh?

Use viewport units when sizing against the browser window: 100vh (or 100dvh on mobile) for full-height hero sections, and a vw value inside clamp() for fluid typography that scales with screen width. Avoid 100vw for full-width blocks because it includes the scrollbar and can cause horizontal scroll; use 100% instead.

What is 1rem in pixels?

By default, 1rem equals 16px, because the browser's default root font-size is 16px. That default changes if the user adjusts their browser text-size setting or if you set a different font-size on the html element. This is exactly why rem is accessible: it honors the user's chosen base size rather than locking it.

What does percentage refer to in CSS?

A percentage is relative to the parent's corresponding property. width: 50% is half the parent's width, and height: 50% is half its height (if the parent has an explicit height). The catch: padding and margin percentages are always calculated from the parent's width, even for top and bottom values.

Are px still used in modern CSS?

Yes, but selectively. px is ideal for values that should stay fixed regardless of text scaling: 1px borders, hairline dividers, shadow offsets, and outlines. For anything typographic or layout-related that should adapt to users and screens, relative units like rem, %, and viewport units are the modern default.