Menu

rem vs em vs px: CSS Units, Percentages, vw and vh

rem is relative to the root font size, em to the current element's font size, px is fixed, and % depends on the property. See each unit measured in a nested demo, why em compounds, when to use which, and how vw, vh and dvh work.

This page includes runnable editors - edit, run, and see output instantly.

rem vs em in one nested demo

rem is relative to the root font size (the <html> element, 16px by default). em is relative to the current font size, which for font-size means the parent's. Nest the same class three times and the difference shows:

The printed sizes are what the browser computed. Each 1.25em multiplies its parent: 20px, then 25px, then 31.25px. Each 1.25rem looks only at the root: 20px, 20px, 20px. That compounding is why em font sizes get out of hand in nested lists and menus, and why most sites set font sizes in rem.

What every unit is relative to

UnitRelative to1 unit by default
pxnothing (a CSS pixel)1px
remfont size of the root element (html)16px
emfont size of the element; for font-size itself, the parent's16px at the top level
%depends on the property (see below)
vw / vh1% of the viewport's width / height
dvh, svh, lvh1% of the dynamic, small or large viewport height
vmin / vmax1% of the smaller / larger viewport side
chthe width of the "0" in the current fontabout half an em
exthe x-height of the current fontabout half an em

A CSS pixel is not a device pixel: on a phone with a 3x screen, 1px is 3 device pixels wide. It stays roughly the same visual size across screens, which is why px is still fine for borders and shadows.

em for spacing that scales with the text

em is not only for font sizes. On padding, margin, width or border-radius it means "times this element's font size". That makes one button class work at any size:

Each button's padding is half its font size vertically and one font size horizontally, so the three look like the same button at three sizes. With px padding you would need a separate padding rule for every size.

Note that buttons do not inherit the page's font size by default (browsers give form controls their own), so the middle button here is 13.33px in Chrome, not 16px. Add font: inherit to buttons and inputs when you want them to match the page.

Percentages: it depends on the property

% has no single meaning. Each property says what it is a percentage of:

Property% is relative to
width, max-width, left, rightthe parent's (containing block's) width
height, top, bottomthe parent's height, and only if the parent has a set height
padding, margin (all four sides)the parent's width
font-sizethe parent's font size (150% = 1.5em)
line-heightthe element's own font size
transform: translate()the element's own size

width: 50% is 200px, half of the parent. padding-top: 5% is 20px: 5% of the parent's width, not its height. And height: 50% does nothing, because the parent has no set height, so the child is only as tall as its text plus that padding. The width and height page covers that rule in detail.

vw, vh and the mobile address bar

1vw is 1% of the viewport width and 1vh 1% of its height. They are useful for full-screen sections and for fluid sizes (often inside clamp()). On phones, vh has a catch: the browser's address bar slides in and out as you scroll, and 100vh is the height without the bar, so a height: 100vh hero is cut off at the bottom when the bar shows. The newer units fix it:

UnitHeight used
100vhthe large viewport (bars hidden) on most mobile browsers
100svhthe small viewport (bars shown): always fits
100lvhthe large viewport, explicitly
100dvhthe dynamic viewport: changes as the bars move
.hero {
  min-height: 100vh;  /* fallback for older browsers */
  min-height: 100svh; /* fits the screen even with the address bar visible */
}

vw has its own catch: 100vw includes the width of a vertical scrollbar when the scrollbar takes up space (as it does on Windows), so width: 100vw can be wider than the page and add a horizontal scrollbar. Use width: 100% for "the full width of the parent".

ch for readable line length

ch is the width of the "0" character, so max-width: 65ch limits a paragraph to roughly 65 characters per line, a comfortable reading length, at any font size:

Which unit to use

ForUseWhy
Font sizesremConsistent everywhere, respects the reader's font size setting
Padding and gaps in componentsrem or emem when it should grow with the component's own text
Borders, hairlines, shadowspxShould stay thin at any font size
Layout widths%, fr, max-width in rem or chFollow the container, cap the line length
Full-screen sectionssvh / dvh (with a vh fallback)Handle mobile address bars
Fluid typeclamp() mixing rem and vwGrows with the screen within limits

Why rem respects readers and px does not

Browsers have a setting for the default font size, used by people who need larger text. It changes the root font size, so everything in rem and em grows with it. A font-size: 14px stays 14px whatever the reader chose. Browser zoom scales px too, but the font size setting is the one many readers rely on. This is the main reason to set font sizes in rem.

Some sites set html { font-size: 62.5%; } so that 1rem equals 10px and the math is easier. It still respects the reader's setting (62.5% of whatever they chose), but remember to set a readable font-size on body, or every element without its own size shows 10px text.

Common mistakes

  • em font sizes in nested elements. They multiply at every level. Use rem for font sizes.
  • height: 50% that does nothing. The parent needs a set height for a percentage height to work.
  • Expecting padding-top: 10% to follow the parent's height. Vertical padding and margin percentages use the parent's width.
  • width: 100vw causing a horizontal scrollbar. It includes the scrollbar. Use 100%.
  • height: 100vh cut off on phones. Use 100svh or 100dvh.
  • Setting html { font-size: 12px }. A px value on the root overrides the reader's font size setting for the whole page. Leave the root alone or use a percentage.

Frequently Asked Questions

What is the difference between rem and em?

rem is relative to the font size of the root element (<html>), so 1.5rem is the same size everywhere on the page. em is relative to the font size of the element itself (for font-size, the parent's), so it compounds when elements are nested.

How many pixels is 1rem?

16px by default, because browsers set the root font size to 16px. If the user raises the default font size in their browser settings, or you set html { font-size: ... }, 1rem changes with it.

Should I use rem or px for font size?

Use rem for font sizes. It respects the reader's default font size setting in the browser, which px ignores. Browser zoom scales both, but not every reader uses zoom.

When should I use em instead of rem?

Use em for spacing that should scale with the element's own text, such as a button's padding or an icon's size next to text. The same class then looks right at any font size. Use rem for font sizes and page-level spacing, so nothing compounds.

What is the difference between 100vh and 100dvh?

100vh on mobile browsers is the height with the address bar hidden, so a 100vh section is taller than the visible screen while the bar shows. 100dvh (dynamic viewport height) follows the bar as it appears and disappears, and 100svh is always the smaller height.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED