Every dimension in CSS requires a unit. The unit you choose determines whether your layout is rigid or responsive, whether it honors a user’s accessibility settings, and how it behaves across screen sizes. In this lesson you will master the full range of CSS length units and learn to use clamp() for truly fluid layouts.
Absolute vs Relative Units
| Unit | Type | Relative To |
|---|---|---|
px |
Absolute | CSS device-independent pixel |
rem |
Relative | Root html element font-size |
em |
Relative | Current element’s own font-size |
% |
Relative | Parent’s same dimension |
vw |
Viewport | 1% of viewport width |
vh |
Viewport | 1% of viewport height |
ch |
Relative | Width of “0” glyph in current font |
When to Use Each Unit
| Unit | Best For | Avoid For |
|---|---|---|
rem |
Font sizes and global spacing โ no compounding, scales with browser prefs | Fine pixel-level details |
em |
Component-local padding/margin that should scale with its own font size | font-size on nested elements (compounds) |
px |
Borders, shadows, fine 1px details | Font sizes (breaks accessibility) |
vw/vh |
Hero sections, full-screen layouts | Font sizes alone (use with clamp) |
clamp() Function
| Syntax | Argument | Meaning |
|---|---|---|
clamp(min, val, max) |
min | Never smaller than this |
| val | Preferred (fluid) value โ usually viewport-based | |
| max | Never larger than this |
Basic Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Units Demo</title>
<style>
:root { font-size: 16px; }
body {
font-family: system-ui, sans-serif;
padding: 2rem;
max-width: 65ch;
margin: 0 auto;
}
h1 { font-size: 2.5rem; margin-bottom: 0.5em; }
h2 { font-size: 1.75rem; margin-bottom: 0.4em; }
/* Viewport units for hero */
.hero {
height: 50vh;
background: #1e293b;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: clamp(1.25rem, 4vw, 3rem);
text-align: center;
padding: 1rem;
}
/* Percentage width */
.two-col { display: flex; gap: 1.5rem; }
.main { width: 70%; }
.sidebar { width: 30%; min-width: 200px; }
/* em padding scales with button font-size */
.btn {
display: inline-block;
padding: 0.6em 1.4em;
font-size: 0.9rem;
background: #4f46e5;
color: white;
border-radius: 0.375rem;
border: none;
cursor: pointer;
}
.btn-lg { font-size: 1.125rem; }
</style>
</head>
<body>
<div class="hero">Fluid Heading โ resize the window!</div>
<h1>CSS Units</h1>
<p>This text is constrained to 65ch for optimal readability.</p>
<button class="btn">Normal Button</button>
<button class="btn btn-lg">Large Button</button>
</body>
</html>
How It Works
Step 1 โ Root Font Size Anchors rem
:root { font-size: 16px; } means 1rem = 16px. When a user sets their browser to 20px, all rem values scale proportionally โ preserving the site’s intended proportions at the user’s preferred size.
Step 2 โ em Inherits from Current Element
h1 has font-size: 2.5rem (40px) and margin-bottom: 0.5em. The em resolves against h1’s own size: 0.5 ร 40px = 20px โ always proportional to the heading.
Step 3 โ Viewport Units Fill the Screen
height: 50vh is always half the viewport height โ 450px on a 900px screen, 600px on a 1200px screen. Updates in real time on resize.
Step 4 โ clamp() Creates Fluid Values
clamp(1.25rem, 4vw, 3rem): never below 1.25rem, never above 3rem, preferably 4vw. At 800px viewport, 4vw = 32px โ between bounds, so it wins. Smooth scaling with no media queries.
Step 5 โ em Button Padding Scales with Font Size
Adding .btn-lg increases font-size from 0.9rem to 1.125rem. The em padding auto-scales proportionally โ no separate padding overrides needed.
Real-World Example: Fluid Typography System
/* typography-system.css */
:root {
font-size: 100%;
--text-xs: clamp(0.70rem, 0.65rem + 0.25vw, 0.80rem);
--text-sm: clamp(0.875rem, 0.83rem + 0.22vw, 1rem);
--text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.6vw, 1.375rem);
--text-xl: clamp(1.25rem, 1rem + 1.2vw, 1.75rem);
--text-2xl: clamp(1.5rem, 1rem + 2vw, 2.5rem);
--text-3xl: clamp(2rem, 1.2rem + 3vw, 4rem);
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
}
body { font-size: var(--text-base); line-height: 1.65; font-family: system-ui, sans-serif; }
h1 { font-size: var(--text-3xl); line-height: 1.1; margin-bottom: var(--space-6); }
h2 { font-size: var(--text-2xl); line-height: 1.2; margin-bottom: var(--space-4); }
p { font-size: var(--text-base); margin-bottom: var(--space-4); max-width: 70ch; }
.container {
width: min(90%, 1200px);
margin-inline: auto;
padding-inline: var(--space-4);
}
Common Mistakes
Mistake 1 โ Using px for font sizes
โ Wrong โ ignores user browser font-size preference:
body { font-size: 16px; }
h1 { font-size: 32px; }
โ Correct โ rem scales with user preferences:
body { font-size: 1rem; }
h1 { font-size: 2rem; }
Mistake 2 โ em compounding in nested elements
โ Wrong โ nested em multiplies unexpectedly:
.parent { font-size: 1.25em; } /* 20px */
.child { font-size: 1.25em; } /* 25px โ compounded! */
โ Correct โ rem always anchors to root:
.parent { font-size: 1.25rem; } /* 20px */
.child { font-size: 1.25rem; } /* still 20px */
Mistake 3 โ Using vw for font sizes without a minimum
โ Wrong โ unreadably tiny on small screens:
h1 { font-size: 5vw; }
โ Correct โ clamp() provides floor and ceiling:
h1 { font-size: clamp(1.5rem, 5vw, 4rem); }
Quick Reference
| Unit / Function | Type | Resolves Against |
|---|---|---|
px |
Absolute | CSS device pixel |
rem |
Relative | Root html font-size |
em |
Relative | Current element’s font-size |
% |
Relative | Parent’s same dimension |
vw |
Viewport | 1% viewport width |
vh |
Viewport | 1% viewport height |
ch |
Relative | Width of “0” glyph |
clamp(a,b,c) |
Function | min a, preferred b, max c |