CSS has more than a dozen length units, and the choice between them affects accessibility, responsiveness and how easy your code is to maintain. Luckily, you only need a handful in daily work, and the big everyday decision is usually px vs rem. This guide explains the important ones, shows them side by side and ends with simple rules you can follow on every project.
For a shorter introduction, see the CSS Units lesson.
Absolute vs relative units
Absolute units have a fixed size. On screens, the only one you will use is px. Units like cm, mm and pt exist mainly for print stylesheets.
Relative units are calculated from something else: the font size of the root element, the parent element, the viewport, or the container. Relative units are what make layouts flexible.
px – the CSS pixel
A CSS pixel is not one physical dot on the screen. On a high-density phone display, one CSS pixel may be drawn with two or three device pixels. That is why a 16px font looks a similar size on a cheap laptop and an expensive phone.
Pixels are predictable, which makes them good for things that should not scale with text: borders, box shadows, small icon sizes and hairline dividers.
Watch out: Setting all font sizes in px can stop text from following the user’s browser font-size preference. Some people set a larger default text size because of low vision. Browser zoom still works with px, but the default font-size setting is not respected the same way. Use rem for font sizes.
rem – relative to the root font size
1rem equals the font size of the <html> element. In most browsers that is 16px by default, so 1.5rem is 24px. If a user sets their default to 20px, 1.5rem becomes 30px – your whole design scales with their choice.
Because rem always refers to the same root value, it does not compound. A 1.2rem heading inside a 0.9rem card is still 1.2rem. That predictability is why rem is the default choice for font sizes and most spacing.
Tip: Avoid the old trick of setting html { font-size: 62.5%; } to make 1rem equal 10px. It works, but it forces you to reset body text everywhere and can confuse third-party components. Thinking in sixteenths is easier than it sounds: 0.75rem = 12px, 0.875rem = 14px, 1.25rem = 20px.
em – relative to the current font size
For most properties, 1em equals the element’s own computed font size. (For the font-size property itself, it refers to the parent’s font size.) This is useful when something should scale with its own text.
The classic example is a button. If padding is in em, a small button and a large button keep the same proportions automatically:
.btn { font-size: 1rem; padding: 0.5em 1em; }
.btn-small { font-size: 0.875rem; }
.btn-large { font-size: 1.25rem; }
/* each size gets proportional padding without extra rules */The downside of em is compounding. If a list sets font-size: 1.2em and you nest a list inside it, the inner list is 1.2 × 1.2 = 1.44 times the base size. Use em on purpose, for local scaling, not as a general replacement for rem.
% – relative to the parent
Percentages depend on the property. For width, it is the parent’s width. For font-size, it is the parent’s font size. For padding and margin – even vertical ones – it is the parent’s width, which surprises many people. Use % mainly for widths, such as width: 100% or max-width: 100% on images.
vw, vh and the newer viewport units
1vw is 1% of the viewport width and 1vh is 1% of the viewport height. They are handy for full-screen sections and fluid sizing.
On mobile, 100vh has a known problem: the browser address bar appears and disappears, so the visible height changes. Newer units solve this and are supported in current major browsers:
| Unit | Meaning | Good for |
|---|---|---|
svh | Small viewport height – browser UI fully shown | Hero sections that must never be cut off |
lvh | Large viewport height – browser UI hidden | Backgrounds that can extend under the bar |
dvh | Dynamic – updates as browser UI shows or hides | Full-height app layouts (can cause small jumps) |
vmin / vmax | The smaller / larger of vw and vh | Square elements that fit any orientation |
Watch out: Never set font size in vw alone, such as font-size: 4vw. On a phone it becomes tiny, on a big monitor huge, and zooming has little effect. Combine it with rem inside clamp() instead (see below).
ch – the width of a character
1ch is the width of the “0” character in the current font. It is ideal for limiting line length: max-width: 65ch keeps paragraphs at a comfortable reading width whatever the font size. This simple rule improves readability more than most people expect – see Typography Rules for UI.
px vs rem vs em vs vw side by side
This demo shows boxes sized in different units. Change the root font size with the buttons and watch which boxes respond. Then resize the preview to see the vw box change.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CSS units demo</title>
<style>
body { font-family: system-ui, sans-serif; margin: 16px; }
.row { display: flex; align-items: center; gap: 12px; margin: 8px 0; }
.bar { height: 24px; background: #3b6fd8; border-radius: 4px; }
.label { width: 90px; font-size: 14px; }
.px { width: 160px; }
.rem { width: 10rem; }
.em-parent { font-size: 1.5rem; }
.em { width: 10em; }
.vw { width: 30vw; }
button { font: inherit; padding: 0.4em 0.8em; margin-right: 6px; }
</style>
</head>
<body>
<p>Root font size: <strong id="size">16px</strong></p>
<button data-size="12">12px</button>
<button data-size="16">16px</button>
<button data-size="22">22px</button>
<div class="row"><span class="label">160px</span><div class="bar px"></div></div>
<div class="row"><span class="label">10rem</span><div class="bar rem"></div></div>
<div class="row em-parent"><span class="label">10em*</span><div class="bar em"></div></div>
<div class="row"><span class="label">30vw</span><div class="bar vw"></div></div>
<p style="font-size:14px">* the em bar sits inside a parent with font-size 1.5rem</p>
<script>
document.querySelectorAll('button').forEach(function (btn) {
btn.addEventListener('click', function () {
var s = btn.dataset.size + 'px';
document.documentElement.style.fontSize = s;
document.getElementById('size').textContent = s;
});
});
</script>
</body>
</html>Fluid sizes with clamp()
clamp(min, preferred, max) lets a value grow with the viewport but never go below a minimum or above a maximum. Mixing rem and vw in the middle value keeps it responsive and zoom-friendly.
h1 {
font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem);
}
.section {
padding-block: clamp(2rem, 5vw, 5rem);
}A cheat sheet you can follow
| What you are sizing | Recommended unit |
|---|---|
| Body and heading font sizes | rem (or clamp() with rem + vw) |
| Spacing (margin, padding, gap) | rem; em inside components that should scale with their text |
| Borders, outlines, shadows | px |
| Line height | Unitless number, e.g. 1.5 |
| Widths of layout columns | %, fr (in Grid) or rem max-widths |
| Readable text width | ch, e.g. max-width: 65ch |
| Full-screen sections | svh / dvh with a min-height |
| Media query breakpoints | px or em, used consistently |
Why unitless line height? If you write line-height: 1.5, each child calculates its own line height from its own font size. With line-height: 24px, a larger heading inside would inherit 24px and its lines would overlap.
Units in design tools
Figma works in pixels. That is fine – designers can keep using a 4px or 8px spacing scale, and developers convert: 8px = 0.5rem, 16px = 1rem, 24px = 1.5rem, 32px = 2rem. Agreeing on this scale in a design system makes handoff much smoother. Store the values as Figma variables and as CSS custom properties with the same names. See Design Systems for Beginners.
Once you have settled px vs rem for each job, apply them in layouts with Responsive Design Breakpoints and Flexbox vs Grid.
Frequently asked questions
Is rem better than px?
For font sizes and most spacing, yes, because rem respects the user’s default font-size setting. Px is still the right choice for borders, shadows and other fine details.
What is the difference between em and rem?
Rem is always relative to the root html element’s font size. Em is relative to the current element’s font size (or the parent’s, when used for font-size), so it can compound when elements are nested.
How many pixels is 1rem?
By default 16px in most browsers, but it changes if the user or your CSS changes the root font size. That flexibility is the reason to use it.
Why does 100vh cause problems on phones?
Mobile browser toolbars change the visible height while scrolling. Use svh, lvh or dvh, which were designed for this, depending on whether you want the smallest, largest or live viewport height.
Should line-height have a unit?
Usually no. A unitless value such as 1.5 is multiplied by each element’s own font size, so nested text with different sizes stays readable.


