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
| Unit | Relative to | 1 unit by default |
|---|---|---|
px | nothing (a CSS pixel) | 1px |
rem | font size of the root element (html) | 16px |
em | font size of the element; for font-size itself, the parent's | 16px at the top level |
% | depends on the property (see below) | |
vw / vh | 1% of the viewport's width / height | |
dvh, svh, lvh | 1% of the dynamic, small or large viewport height | |
vmin / vmax | 1% of the smaller / larger viewport side | |
ch | the width of the "0" in the current font | about half an em |
ex | the x-height of the current font | about 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, right | the parent's (containing block's) width |
height, top, bottom | the parent's height, and only if the parent has a set height |
padding, margin (all four sides) | the parent's width |
font-size | the parent's font size (150% = 1.5em) |
line-height | the 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:
| Unit | Height used |
|---|---|
100vh | the large viewport (bars hidden) on most mobile browsers |
100svh | the small viewport (bars shown): always fits |
100lvh | the large viewport, explicitly |
100dvh | the 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
| For | Use | Why |
|---|---|---|
| Font sizes | rem | Consistent everywhere, respects the reader's font size setting |
| Padding and gaps in components | rem or em | em when it should grow with the component's own text |
| Borders, hairlines, shadows | px | Should stay thin at any font size |
| Layout widths | %, fr, max-width in rem or ch | Follow the container, cap the line length |
| Full-screen sections | svh / dvh (with a vh fallback) | Handle mobile address bars |
| Fluid type | clamp() mixing rem and vw | Grows 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
emfont sizes in nested elements. They multiply at every level. Useremfor 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: 100vwcausing a horizontal scrollbar. It includes the scrollbar. Use100%.height: 100vhcut off on phones. Use100svhor100dvh.- 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.