Which unit should I use?

32 situations, each with the unit to reach for, the reason, a line of CSS and one thing to watch. Any of them is two choices away.

Find your unit in two steps

Step 1 of 2

What will vary once this is live?

Pick the closest match. Each choice leads to one more question and then to an answer.

Every scenario

The same answers as the steps above, in one list. Each heading has an address of its own.

The reader's text size or zoom

Second question: What are you setting?

Type sizes and the space between blocks

Userem

rem multiplies the font size of the html element, so everything written in it follows the default text size the reader picked.

Example
h1 {
  font-size: 2rem;
  margin-block-end: 1.5rem;
}

Watch out Leave the font size on html alone. A root size in pixels switches that setting off for every rem on the page.

Padding inside a button, badge or tag that has its own text size

Useem

Padding in em is a multiple of the font size on that very tag, so when the tag's text gets bigger or smaller the padding goes with it.

Example
.tag {
  font-size: 0.875rem;
  padding: 0.25em 0.6em;
}

Watch out Keep em out of font-size on nested elements: the factor is applied again at every level of nesting.

A media query breakpoint

Useem

A breakpoint in em moves with the reader's default font size, where one in px stays at a fixed number of pixels.

Example
@media (min-width: 40em) {
  .nav { display: flex; }
}

Watch out Write all breakpoints in one unit. Em ones shift with the default size and px ones do not, so a mix can change order.

A thin border, outline or shadow that should not grow with the text

Usepx

px ignores the default text size, which is what a one-pixel line needs.

Example
.rule {
  border-block-end: 1px solid;
}

Watch out Page zoom still scales px, and a CSS pixel is not always one screen pixel.

The width or orientation of the window

Second question: What should follow the window?

A heading that grows with the window

Usevwrem

Put vw inside clamp() beside a rem term: the vw part tracks the window and the rem part ties the size to the reader's settings.

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

Watch out Text sized in vw alone ignores zoom and may fail the text-resize requirement, WCAG 1.4.4.

A band that must span the whole window from inside a narrow parent

Usevw

vw is measured against the window, so the element does not care how wide its parent is.

Example
.band {
  inline-size: 100vw;
}

Watch out 100vw includes the width of a classic vertical scrollbar, which can add a horizontal one.

A square or circle that has to fit on screen in both orientations

Usevmin

vmin is a hundredth of the shorter side of the window, so a box under 100vmin cannot be bigger than the window along either axis.

Example
.dial {
  inline-size: 70vmin;
  aspect-ratio: 1;
}

Watch out Do not size text with it alone. Like vw, it ignores zoom.

A decoration that has to cover the screen however it is turned

Usevmax

vmax is a hundredth of the longer side, so a shape sized in it reaches past the shorter side as well.

Example
.glow {
  inline-size: 150vmax;
  aspect-ratio: 1;
  border-radius: 50%;
}

Watch out A shape this big is larger than the screen in the other direction and overflows unless a parent clips it.

The browser toolbars on a phone

Second question: What should a full-height box do while the toolbars move?

Keep the first screen's button in view before anyone scrolls

Usesvh

svh is the height with the toolbars fully shown, the smallest the screen gets, so nothing sized to it hides behind them.

Example
.opening {
  min-block-size: 100svh;
}

Watch out When the toolbars retract, the box is shorter than the screen and a strip behind it shows.

Match the visible area at every moment, as a chat composer or a bottom sheet does

Usedvh

dvh follows the toolbars as they show and hide, so the box is always exactly as tall as what can be seen.

Example
.thread {
  block-size: 100dvh;
}

Watch out The box is resized each time the toolbars move, so give it children that can take a change of height: a scrolling middle between a fixed top and bottom.

Fill the whole screen once the toolbars have retracted

Uselvh

lvh is the height with the toolbars retracted, the largest the screen gets.

Example
.scenery {
  min-block-size: 100lvh;
}

Watch out Text and buttons that must stay visible while the toolbars are shown do not belong in a box sized this way.

A full-height panel in a layout made for desktop windows

Usevh

vh is a hundredth of the viewport height and the shortest thing to write when toolbars are not part of the problem.

Example
.panel {
  min-block-size: 60vh;
}

Watch out On phones 100vh matches the viewport with the toolbars retracted, so the bottom can hide behind the browser's controls.

The box the component is placed in

Second question: What does the component do with the width it gets?

Scale a heading or a gap with the width of its box

Usecqi

cqi is a hundredth of the container's inline size, so it measures the box the component sits in and not the window.

Example
.card-wrap {
  container-type: inline-size;
}
.card h2 {
  font-size: clamp(1.5rem, 8cqi, 3rem);
}

Watch out Set container-type on the wrapper, not on the heading: an element's own container-type serves its descendants and not itself.

Split the leftover width of a grid between columns

Usefr

fr hands out the free space of a grid track list in proportion to the numbers you write.

Example
.gallery {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 1rem;
}

Watch out A plain 1fr track will not shrink below its content. Write minmax(0, 1fr) when a long word or wide image must not widen the grid.

A child that takes a share of its parent's width

Use%

A percentage is a share of a reference value, and for a width the reference is the width of the parent.

Example
.sidebar {
  inline-size: 30%;
}

Watch out Percentages in padding and margin are measured against width on every side, so padding-block: 10% is not a tenth of the height.

Size a chart to the fixed height of its panel

Usecqh

cqh is a hundredth of the container's height, so the chart keeps its share of the panel.

Example
.panel {
  container-type: size;
  block-size: 20rem;
}
.panel .chart {
  block-size: 80cqh;
}

Watch out A size container ignores its children when it works out its own size, so give it a height or it can collapse to zero.

The font that draws the text

Second question: What do you want to measure in the font?

A comfortable line length for reading

Usech

ch is the width of the zero in the current font, so a cap of 65ch follows whichever font draws the paragraph.

Example
.story {
  max-inline-size: 65ch;
}

Watch out ch counts one glyph, the zero. Most letters of prose are narrower, so 65ch holds more than 65 characters.

A line length that must not change when a component switches font

Userchch

rch measures the zero in the font of the root element, so the width stays put whatever family the component uses.

Example
.story {
  max-inline-size: 65ch;
}
@supports (width: 1rch) {
  .story { max-inline-size: 65rch; }
}

Watch out A browser that does not know the unit drops the whole declaration, so keep a ch line before it. The unit page shows current support.

An icon as tall as the capital letters of its label

Usecap

cap is the height of the capitals in the current font, so the icon stays level with its label in any font.

Example
.btn svg {
  block-size: 1cap;
}

Watch out Older browsers ignore the unit, so write a fallback declaration before the cap one.

An inline icon as tall as the lowercase letters around it

Useex

ex follows the x-height of the font in use, so the icon scales with the lowercase letters.

Example
.inline-icon {
  inline-size: 2ex;
  block-size: 2ex;
}

Watch out x-height varies more between fonts than font-size does, and if the intended font fails to load, every ex value changes with the fallback.

A line of Chinese, Japanese or Korean text counted in characters

Useic

ic is the width of the ideograph 水 in the current font, so 40ic is a line of about forty full-width characters.

Example
.note {
  max-inline-size: 40ic;
}

Watch out A font without CJK glyphs cannot measure the ideograph, and the unit then falls back to 1em.

How tall a line of text is

Second question: Whose line height is the measure?

A gap between paragraphs that equals one line of their own text

Uselh

lh is the line height of the paragraph that carries it, so the gap stays one line whatever the font size.

Example
p + p {
  margin-block-start: 1lh;
}

Watch out With line-height: normal, 1lh depends on the font's own metrics, and a line can grow taller when tall inline content sits in it.

Section spacing in whole lines of body text, the same everywhere

Userlh

rlh takes the line height set on html, so every section counts its gaps in the same line.

Example
section {
  padding-block: 2rlh;
}

Watch out A component with larger text gets no taller gaps from it. For the component's own lines, use lh.

Paper, or a screen with more dots

Second question: What are you preparing?

Usept

A point is 1/72 of an inch, the unit in which printers and designers quote type sizes.

Example
@media print {
  body { font-size: 11pt; }
}

Watch out On a screen 12pt is always 16px, so points say nothing about the real size of a display.

The margins of a printed page

Usein

An inch is 96px, 2.54cm and 72pt, so one inch value anchors every other physical size on the sheet.

Example
@page {
  margin: 0.75in;
}

Watch out Nothing on screen is measured with a ruler. 1in always draws as 96 CSS pixels, and only paper output makes it physical.

Serve a sharper image to dense screens

Usedppx

dppx counts device dots per CSS pixel, the same number that window.devicePixelRatio reports.

Example
@media (min-resolution: 2dppx) {
  .hero-art { background-image: url("hero-2x.webp"); }
}

Watch out Page zoom changes the ratio too, so at 200% zoom on a 1x display the query already matches 2dppx.

Offer two files for one image and let the browser choose

Usex

x is the short spelling of dppx, and the form image-set() descriptors are written in.

Example
.badge {
  background-image: image-set(
    url("badge.png") 1x,
    url("badge-2x.png") 2x,
    url("badge-3x.png") 3x
  );
}

Watch out x is accepted only where a resolution fits. It is not a multiplier for widths or font sizes.

Nothing varies: I need an angle or a duration

Second question: What turns or takes time?

A rotation or gradient direction that people will edit by hand

Usedeg

A full turn is 360deg, a number people can read and adjust at a glance.

Example
.arrow {
  rotate: 45deg;
}

Watch out A bare number is not an angle in transform functions. rotate(45) is invalid and the whole transform is dropped.

A spinner or loop that ends where it began

Useturn

1turn is one full rotation, so the last keyframe lands on the first without a number to remember.

Example
@keyframes spin {
  to { rotate: 1turn; }
}

Watch out Values above 1turn spin more than once in an animation, but a static 2.5turn looks like 0.5turn.

An angle that comes from the Math functions of JavaScript

Userad

Math.sin and its relatives work in radians, so handing CSS radians skips a conversion.

Example
.needle {
  rotate: 1.5708rad;
}

Watch out CSS trigonometric functions read a plain number as radians, so sin(1) is one radian and sin(90deg) is the one that equals 1.

Short interface feedback that you tune in small steps

Usems

ms keeps small durations as whole numbers, so 150ms reads more clearly than 0.15s.

Example
.toast {
  transition: transform 200ms ease-out;
}

Watch out For long animations 2s reads easier than 2000ms.

A fade or slide that takes about half a second

Uses

s is the base unit of time in CSS and reads best for whole and half-second timings.

Example
.menu {
  transition: transform 0.4s ease-in-out;
}

Watch out Zero is not exempt: transition-duration: 0 is invalid, and 0s is the form that works.