px, rem, em, vh — when to use each CSS unit
A practical guide to choosing the right CSS unit for every situation: layout, typography, spacing, and responsive design.
Absolute vs relative units
CSS units fall into two broad categories. Absolute units have a fixed size that does not change based on anything — they are what they are. Relative units derive their size from some reference: a parent element, the root font size, or the viewport dimensions.
| Unit | Type | Relative to |
|---|---|---|
px | Absolute | CSS pixel (device-independent) |
pt, cm | Absolute | Physical measurement (print use) |
rem | Relative | Root <html> font-size |
em | Relative | Current element's font-size |
% | Relative | Parent element's dimension |
vh, vw | Relative | Viewport height / width |
dvh, dvw | Relative | Dynamic viewport (accounts for browser UI) |
For screen-based design, prefer relative units for typography and spacing. Absolute units are best reserved for elements that should never scale: borders, outlines, and decorative details.
px — pixel precision
A CSS pixel is not a physical screen pixel. It is a device-independent unit defined such that one CSS pixel equals approximately 1/96th of an inch at arm's length. On high-DPI (Retina) screens, one CSS pixel maps to 2 or 3 physical pixels — but from CSS's perspective it is still 1px.
When to use px
- Borders and outlines:
border: 1px solid— these should never scale - Box shadows:
box-shadow: 0 2px 8px rgba(0,0,0,.15) - Icon sizes when paired with a fixed-size icon system
- Media query breakpoints (though em is safer — see Decision guide)
When NOT to use px
- font-size — overrides the user's browser default, failing WCAG 1.4.4
- line-height — prefer unitless (
1.5) or rem - padding and margin when you want them to scale with font size
Accessibility rule: if a user sets their browser to 200% text size, px-based fonts ignore it. rem-based fonts honour it. This is not optional — it is a WCAG 2.1 Level AA requirement.
rem — root-relative scaling
rem stands for root em. It is always relative to the font-size set on the <html> element. The browser default is 16px, so 1rem = 16px unless you change it.
/* Default: 1rem = 16px */
h1 { font-size: 2rem; } /* 32px */
p { font-size: 1rem; } /* 16px */
.small { font-size: .75rem; } /* 12px */
Because rem is anchored to the root, it does not compound when elements are nested — unlike em. This makes it the default choice for font sizes and spacing throughout a project.
The 62.5% trick
Setting html { font-size: 62.5%; } makes 1rem = 10px, which simplifies mental arithmetic (1.6rem = 16px). It is widely considered an anti-pattern today because:
- It overrides the user's browser font-size preference before your CSS loads
- Third-party components that rely on the 16px baseline will render incorrectly
- Modern tooling (CSS custom properties,
clamp()) removes the need for base-10 math
Use rem with the default 16px root. Divide by 16 mentally or use a px-to-rem converter.
em — context-relative sizing
em is relative to the current element's font-size. If no font-size is set on the element, it inherits from its parent — which is why em can compound when elements are nested.
/* Compounding example */
.parent { font-size: 1.2em; } /* 19.2px if parent is 16px */
.child { font-size: 1.2em; } /* 23.04px — 1.2 × 19.2px */
When em shines
Em is the right unit when you want spacing or sizing to scale proportionally with the component's own font-size, not the global root:
.btn {
font-size: 1rem;
padding: .5em 1.2em; /* scales with btn font-size */
}
.btn-large {
font-size: 1.25rem;
/* padding automatically scales — no overrides needed */
}
This pattern — em for padding inside a component that has its own font-size set in rem — gives you the best of both worlds: global scale control with local proportional scaling.
When to avoid em
- Global font sizes — use rem to avoid compounding across the DOM tree
- Layout dimensions — use rem, %, or viewport units instead
vh and vw — viewport units
vh (viewport height) and vw (viewport width) are each equal to 1% of the viewport's corresponding dimension. They are ideal for layouts that need to fill or be proportional to the screen.
.hero {
height: 100vh; /* full viewport height */
width: 100vw; /* full viewport width */
}
The 100vh mobile problem
On mobile browsers, the address bar and bottom navigation chrome eat into the visible area — but they are not subtracted from 100vh. This means a 100vh element is taller than the visible screen, causing a scrollbar to appear even when you don't want one.
The modern fix: dvh
The dvh (dynamic viewport height) unit was introduced to solve this. It updates as the browser UI appears and disappears:
.hero {
height: 100vh; /* fallback for older browsers */
height: 100dvh; /* dynamic — accounts for collapsible browser chrome */
}
dvh has excellent browser support as of 2024. Always include the vh fallback for the rare case where it is not supported.
Other viewport units
svh/svw— small viewport (browser UI fully visible)lvh/lvw— large viewport (browser UI hidden)vmin/vmax— smaller/larger of vh and vw
Quick conversion table (base 16px)
When you know the pixel value but need the rem equivalent, divide by 16. This table covers the most common sizes:
| px | rem | em (1:1) |
|---|---|---|
| 8 | 0.5rem | 0.5em |
| 12 | 0.75rem | 0.75em |
| 14 | 0.875rem | 0.875em |
| 16 | 1rem | 1em |
| 18 | 1.125rem | 1.125em |
| 20 | 1.25rem | 1.25em |
| 24 | 1.5rem | 1.5em |
| 32 | 2rem | 2em |
| 48 | 3rem | 3em |
| 64 | 4rem | 4em |
Need to convert an entire stylesheet? Use the CSS Unit Converter tool.
Decision guide
When in doubt, use this table as a quick reference:
| Property / Use case | Recommended unit |
|---|---|
| Font size | rem |
| Padding / margin (global) | rem |
| Padding inside a component (scales with its font-size) | em |
| Borders, outlines, box shadows | px |
| Layout columns / widths | % or fr (CSS Grid) |
| Hero / full-screen sections | dvh with vh fallback |
| Media query breakpoints | em (most accessible) or rem |
| Line-height | Unitless (1.5) or rem |
| Responsive font size (fluid) | clamp() with rem values |
Fluid typography with clamp() is a powerful pattern: font-size: clamp(1rem, 2.5vw, 1.5rem) sets a minimum, a fluid middle, and a maximum — all in one line. Use the Font Size Clamp Generator to build these values visually.