Skip to main content

Units & Sizing

CSS has many units for expressing sizes. Picking the right one for the right job is critical for building layouts that scale well across screen sizes and respect user preferences.

Absolute units

Absolute units are fixed -- they do not change based on context.

px (pixels)

The most common absolute unit. One px is one device-independent pixel:

h1 {
font-size: 32px;
margin-bottom: 16px;
}

Pixels are predictable and easy to reason about. Their downside: they do not scale when a user changes their browser's default font size.

Note: On high-DPI (Retina) screens, one CSS pixel may be rendered by two or more physical screen pixels. CSS handles this automatically -- you do not need to worry about it.

Other absolute units (cm, mm, in, pt) exist but are meant for print stylesheets. Stick with px for screens.

Relative units

Relative units scale based on some reference value. They are the key to responsive, accessible designs.

em

1em equals the font size of the current element (or, for the font-size property itself, the parent's font size):

.parent {
font-size: 20px;
}

.parent .child {
font-size: 0.8em; /* 0.8 × 20px = 16px */
padding: 1.5em; /* 1.5 × 16px = 24px (uses the child's own font-size) */
}

The tricky part: em compounds when elements are nested. If three nested elements each set font-size: 1.2em, the innermost text is 1.2 × 1.2 × 1.2 = 1.728 times the base size. This compounding makes em unreliable for font sizes in deep component trees.

em is still useful for spacing that should scale with the local font size -- for example, padding and margins inside a component.

rem (root em)

1rem equals the root element's font size -- the <html> element. The default root font size in all browsers is 16px (unless the user has changed it).

h1 {
font-size: 2rem; /* 2 × 16px = 32px */
margin-bottom: 1rem; /* 1 × 16px = 16px */
}

p {
font-size: 1.125rem; /* 1.125 × 16px = 18px */
}

Unlike em, rem does not compound. No matter how deeply nested the element is, 1rem always refers to the same base value.

Tip: Use rem for font sizes and major spacing. It respects the user's browser font-size setting (an important accessibility feature) and never compounds.

Common rem values

When the root font size is 16px:

remPixels
0.75rem12px
0.875rem14px
1rem16px
1.125rem18px
1.25rem20px
1.5rem24px
2rem32px
2.5rem40px
3rem48px

Percentage (%)

Percentages are relative to the parent element. The reference property depends on context:

.container {
width: 80%; /* 80% of the parent's width */
}

.sidebar {
width: 25%;
}

.main {
width: 75%;
}

For width, the percentage refers to the parent's width. For padding and margin, the percentage also refers to the parent's width -- even for padding-top and margin-top. This catches many people off guard.

.box {
padding-top: 10%; /* 10% of the parent's WIDTH, not height */
}

Viewport units

Viewport units are relative to the browser window (viewport):

UnitReference
vw1% of the viewport width
vh1% of the viewport height
vmin1% of the smaller dimension
vmax1% of the larger dimension
.hero {
height: 100vh;
width: 100vw;
}

.half-screen {
height: 50vh;
}

Common use cases:

  • Full-screen sections: height: 100vh
  • Fluid font sizes: font-size: 5vw (but see clamp() below)
  • Full-width backgrounds: width: 100vw

Note: On mobile browsers, 100vh can be larger than the visible area because the browser's address bar takes up space. Modern CSS offers dvh (dynamic viewport height), svh (small), and lvh (large) to handle this. Use dvh when you need precise full-screen behaviour on mobile.

ch

1ch equals the width of the 0 (zero) character in the current font. It is useful for setting widths based on character count:

.article {
max-width: 65ch;
}

This limits the article to roughly 65 characters per line -- the recommended range for comfortable reading (45--75 characters).

Which unit to use

A practical cheat sheet:

Use caseRecommended unitWhy
Font sizesremRespects user settings, no compounding
Component padding/marginsrem or emScales with text; em for local, rem for global
Layout widths% or frFluid, adapts to container
Max-width for readabilitychLimits line length to ~65 characters
Full-screen hero sectionsvh / dvhFills the viewport height
Borders, box-shadows, small detailpxNeeds to stay crisp at any zoom level
Media query breakpointsem or pxBoth work; em for zoom-friendly breakpoints

min, max, and clamp

CSS provides three functions for setting bounds on sizes:

min()

Uses the smaller of the given values:

.container {
width: min(800px, 90%);
}

The container is 90% of the parent, but never wider than 800px. This is equivalent to max-width: 800px; width: 90% in a single declaration.

max()

Uses the larger of the given values:

.sidebar {
width: max(200px, 20%);
}

The sidebar is 20% of the parent, but never narrower than 200px.

clamp()

Sets a preferred value with a minimum and maximum:

h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}

This reads as: "The font size should be 4vw, but never smaller than 1.5rem and never larger than 3rem."

clamp() is ideal for fluid typography that scales smoothly between breakpoints without media queries:

body {
font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
}

h1 {
font-size: clamp(1.75rem, 1.5rem + 2vw, 3.5rem);
}

Tip: clamp() is one of the most useful modern CSS features. Whenever you find yourself writing a size with a min-width or max-width guard, consider whether clamp() can replace all three declarations.

The width and height trap

Setting fixed widths and heights on elements is the most common source of overflow bugs:

/* Risky */
.card {
width: 400px;
height: 300px;
}

If the content inside .card exceeds 300px of height, it overflows and clips or bleeds outside the box.

Prefer flexible sizing:

.card {
max-width: 400px;
width: 100%;
min-height: 300px;
}
  • max-width prevents the card from growing too wide
  • width: 100% makes it fill the available space up to the max
  • min-height ensures a minimum height but allows the card to grow with its content

Tip: As a rule of thumb, set max-width instead of width and min-height instead of height. Let content determine the actual size whenever possible.

Practical example

Here is a responsive article layout using multiple unit types:

*,
*::before,
*::after {
box-sizing: border-box;
}

body {
font-size: clamp(1rem, 0.9rem + 0.4vw, 1.2rem);
line-height: 1.6;
margin: 0;
padding: 2rem;
color: #333;
}

.article {
max-width: 65ch;
margin: 0 auto;
}

.article h1 {
font-size: clamp(1.75rem, 1.5rem + 1.5vw, 3rem);
line-height: 1.2;
margin-bottom: 0.5em;
}

.article h2 {
font-size: clamp(1.25rem, 1.1rem + 1vw, 2rem);
margin-top: 2rem;
margin-bottom: 0.5rem;
}

.article p {
margin-bottom: 1rem;
}

.article img {
max-width: 100%;
height: auto;
}

This layout uses ch for readable line length, rem for spacing, clamp() for fluid font sizes, and % for responsive images.

What you learned

  • px is fixed; rem scales with the root font size; em scales with the local font size and compounds
  • Percentages are relative to the parent; viewport units (vw, vh) are relative to the browser window
  • ch is great for limiting line length to ~65 characters
  • clamp(min, preferred, max) creates fluid values that scale smoothly
  • Use max-width instead of width and min-height instead of height to avoid overflow
  • The right unit depends on context -- font sizes get rem, spacing gets rem or em, layout gets % or viewport units

Next step

You now control how things are sized. In the next chapter, you will learn about display and positioning -- how elements flow on the page and how to move them out of the normal flow.