Almost every CSS property that takes a size needs a unit: 16px, 1.5rem, 50%. Picking the right unit is what makes a design scale gracefully across screen sizes and user settings. This lesson explains the CSS units you will actually use and when to choose each one.
Absolute units: px
The px (CSS pixel) is the main absolute unit. Despite the name, it is not a physical screen dot: on high-density phone screens one CSS pixel covers several device pixels. Pixels are predictable, which makes them a good choice for borders, shadows and small details. Other absolute units such as cm and pt exist but are really only useful for print stylesheets.
Relative CSS units
| Unit | Relative to | Good for |
|---|---|---|
rem | The root (html) font size, usually 16px | Font sizes, spacing, most sizes |
em | The current element’s font size | Padding on buttons, icons next to text |
% | Usually the parent’s size | Widths inside a container |
ch | Width of the “0” character | Readable line lengths, input widths |
vw / vh | 1% of the viewport width / height | Full-screen sections, fluid type |
dvh, svh, lvh | Dynamic, small and large viewport height | Full-height sections on mobile |
fr | A share of free space in a grid | Grid columns and rows |
rem vs em
Both are based on font size, but they look in different places. rem always uses the root element, so 2rem is 32px everywhere (with the default 16px root). em uses the font size of the element itself, so it changes depending on where the element sits.
This makes em great for things that should scale with their own text. A button with padding: 0.5em 1em gets bigger padding automatically when you make its text bigger. But em on font-size compounds: a list inside a list with font-size: 1.2em gets 1.2 × 1.2 larger, which is rarely what you want. Use rem for font sizes and save em for component spacing.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>rem, em and compounding</title>
<style>
html { font-size: 100%; } /* respects the user's browser setting */
body { font-family: system-ui, sans-serif; padding: 20px; }
.btn {
font-size: 1rem;
padding: 0.5em 1em; /* scales with the button's own text */
border-radius: 0.4em;
border: 0;
background: #5f3dc4;
color: white;
}
.btn.large { font-size: 1.5rem; }
.em-list li { font-size: 1.2em; } /* compounds when nested */
.rem-list li { font-size: 1.2rem; } /* stays the same */
</style>
</head>
<body>
<button class="btn">Book now</button>
<button class="btn large">Book now</button>
<h3>em (compounds)</h3>
<ul class="em-list"><li>Kerala<ul><li>Kochi<ul><li>Fort Kochi</li></ul></li></ul></li></ul>
<h3>rem (consistent)</h3>
<ul class="rem-list"><li>Kerala<ul><li>Kochi<ul><li>Fort Kochi</li></ul></li></ul></li></ul>
</body>
</html>Watch out: Avoid setting html { font-size: 62.5%; } or a fixed pixel root size just to make rem maths easier. Respecting the browser default, and writing sizes like 1.125rem, keeps text readable for people who increase their default font size.
Viewport units and mobile browsers
100vw is the full width of the viewport and 100vh the full height. On phones, though, the browser’s address bar appears and disappears while scrolling, so the “full height” changes. 100vh uses the largest size, which can hide content behind the toolbar. The newer units fix this: svh uses the smallest height, lvh the largest, and dvh updates dynamically. For a full-screen hero, min-height: 100dvh is the modern choice.
Tip: Viewport units also have logical versions: vi and vb follow the inline and block directions. For container queries there are matching container units such as cqi, which is 1% of the container’s inline size.
Math functions: calc(), min(), max() and clamp()
calc()mixes units:width: calc(100% - 2rem).min()picks the smaller value:width: min(100%, 700px)means “full width, but never more than 700px”. It replaceswidthplusmax-widthin one line.max()picks the larger value:padding: max(1rem, 4vw)never gets smaller than 1rem.clamp(MIN, PREFERRED, MAX)keeps a value between a minimum and a maximum while it scales in between.
clamp() is perfect for fluid typography. font-size: clamp(1.75rem, 1rem + 3vw, 3rem) gives a heading that is 1.75rem on phones, grows smoothly with the screen and stops at 3rem on large monitors, all with no media queries. Including a rem part in the preferred value matters: a size made only of vw does not grow when the user zooms, which is an accessibility problem.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fluid sizes with clamp() and min()</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; }
.hero {
min-height: 60dvh;
display: grid;
place-items: center;
padding: clamp(1rem, 5vw, 4rem);
background: linear-gradient(135deg, #0b7285, #3bc9db);
color: white;
text-align: center;
}
.hero h1 {
font-size: clamp(1.75rem, 1rem + 3vw, 3rem);
margin: 0;
}
.content {
width: min(100% - 2rem, 65ch);
margin-inline: auto;
line-height: 1.6;
}
</style>
</head>
<body>
<section class="hero"><h1>Monsoon trails in the Western Ghats</h1></section>
<div class="content">
<p>Resize the preview. The heading and the padding grow smoothly, while this text column stays a comfortable width with a 1rem gap at each side on small screens.</p>
</div>
</body>
</html>Quick guide: which unit when?
- Font sizes:
rem, orclamp()with rem for headings. - Spacing (margin, padding, gap):
rem;eminside components that scale with their text. - Borders and shadows:
px. - Layout widths:
%,fr,min()andchfor text columns. - Full-screen sections:
dvhorsvhwithmin-height, not a fixed height. - Line height: a plain number such as
1.5, no unit at all.
For a deeper comparison, read CSS units: px, rem, em and vw. Next, bring your designs to life with CSS Transitions and Animations.
