Almost every CSS property that controls size takes a number followed by a unit — 16px, 1.5em, 100%, 50vw. You’ve already been typing these without thinking too hard about them, and for a while that’s fine. But the moment you start building layouts that need to scale across phones, tablets, and big desktop screens, the unit you pick stops being a detail and starts being the whole game.
Some units are fixed and never budge. Others are relative — they grow and shrink based on something else, like the font size of a parent or the width of the browser window. Knowing which is which, and what each one is relative to, is the difference between a layout that adapts gracefully and one that breaks the moment someone zooms in or rotates their phone. Let’s go through the whole family.
Absolute units: px and friends
Absolute units are fixed sizes. They don’t care about the parent element, the user’s settings, or the screen — 10px is 10px everywhere. The one you’ll use constantly is the pixel:
.box {
width: 200px;
padding: 16px;
border: 1px solid black;
}
A pixel in CSS isn’t quite a physical screen pixel anymore (modern high-density displays muddy that), but for everyday purposes you can treat px as a small, reliable, fixed unit. It’s precise and predictable, which is exactly why people reach for it — and also exactly why it can hurt you. A heading fixed at 40px looks the same whether the user has set their browser font to tiny or huge, which means it ignores their preferences entirely.
There are other absolute units — pt (points), cm, in, mm — but they’re meant for print stylesheets, not screens. On the web, px is the only absolute unit you’ll genuinely use.
Pixels are fine — until they aren't
There’s nothing wrong with px for borders, small fixed spacing, or things that genuinely shouldn’t scale. The trap is using px for font sizes and large layout dimensions, because that locks them in place and ignores both user preferences and screen size. For those, you’ll usually want a relative unit instead.
Relative to font size: em and rem
These two are where things get interesting. Both are tied to a font size, but to different font sizes — and mixing them up is one of the most common CSS confusions out there.
rem stands for “root em.” It’s always relative to the root font size — the font size set on the <html> element, which defaults to 16px in every browser. So 1rem is 16px, 2rem is 32px, 0.5rem is 8px, no matter where in the page you use it:
html {
font-size: 16px; /* the root — 1rem now equals 16px */
}
.title {
font-size: 2rem; /* 32px */
margin-bottom: 1rem; /* 16px */
}
em, by contrast, is relative to the font size of the current element (or, for properties like padding, the element’s own computed font size). That makes it powerful but slippery, because it changes depending on context:
.card {
font-size: 20px;
padding: 1em; /* 20px — based on the card's own font size */
}
.card .label {
font-size: 0.5em; /* 10px — half of the card's 20px */
}
The tricky part with em is that it compounds when nested. If a .menu is 1.5em and a .menu inside another .menu is also 1.5em, the inner one is 1.5 × 1.5 = 2.25em of the original — sizes snowball in ways that surprise people. rem sidesteps this entirely because it always points back to the same root.
Why rem is the safer default for font sizes
Because rem is anchored to the root font size, sizing your text in rem means a single change to the html font size scales your entire site proportionally. Even better, it respects the user’s browser font-size setting — someone who bumps their default up for readability gets a site that grows with them. That’s a real accessibility win, and it’s why rem is the go-to unit for typography in modern CSS.
Percentages: relative to the parent
A percentage is relative to the parent element, but which dimension of the parent depends on the property. For width, 100% means the full width of the parent; for height, it’s the parent’s height; for horizontal padding and margins, it’s also based on the parent’s width (a quirk worth remembering).
.container {
width: 600px;
}
.half {
width: 50%; /* 300px — half of the 600px parent */
}
Percentages shine for fluid widths — a column that’s always half its container, a card that fills its row no matter the screen size. They’re a core building block of layouts that stretch and shrink, and you’ll see them constantly alongside Flexbox and Grid. If you’ve worked through responsive design, you’ve already felt how percentages let a layout breathe.
Viewport units: vw and vh
Viewport units are relative to the size of the browser viewport — the visible area of the window itself, not any parent element:
1vw= 1% of the viewport width1vh= 1% of the viewport heightvmin= 1% of the smaller of the twovmax= 1% of the larger of the two
So 100vw is the full width of the window, and 50vh is half its height. This is how you build things like a hero section that always fills the entire screen:
.hero {
width: 100vw;
height: 100vh; /* full screen, every screen */
}
Viewport units are fantastic for full-bleed sections and for typography that scales with the screen. But they have a sharp edge: because they ignore parent elements entirely, 100vw can be slightly wider than the visible content area when a vertical scrollbar is present, causing an unwanted horizontal scroll. Reach for them deliberately, not as a default for everything.
A quick comparison
Here’s the whole family at a glance — what each unit is measured against:
| Unit | Relative to | Typical use |
|---|---|---|
px |
Nothing (fixed) | Borders, small fixed spacing |
rem |
Root (html) font size |
Font sizes, consistent spacing |
em |
Current element’s font size | Spacing that scales with local text |
% |
Parent element | Fluid widths and heights |
vw / vh |
Viewport width / height | Full-screen sections, fluid type |
Putting it together: a sensible default strategy
You don’t have to overthink this. A simple, reliable approach covers the vast majority of cases:
html {
font-size: 16px; /* the root — usually leave the browser default */
}
.card {
font-size: 1rem; /* text scales with the root + user settings */
padding: 1.5rem; /* spacing tied to a predictable root */
border: 1px solid #ddd; /* a fixed, crisp 1px border */
width: 100%; /* fluid — fills whatever holds it */
max-width: 40rem; /* but never gets uncomfortably wide */
}
That single block shows the whole philosophy: rem for text and spacing so the layout scales as one piece and respects the user, px for the things that genuinely shouldn’t move (a hairline border), and % so the card flexes to fit its container. It’s the kind of mix you’ll write again and again.
Don't size everything in px out of habit
The most common beginner mistake is reaching for px for everything because it feels concrete and easy to picture. It works on your screen, but it quietly breaks accessibility — text won’t respond to a user’s font-size preference, and the layout won’t scale cleanly when zoomed. Default to rem for type and spacing, % and viewport units for layout, and save px for the small, fixed details.
Wrapping up
Units are the vocabulary of sizing in CSS, and once you know what each one is relative to, picking the right one stops being guesswork:
pxis fixed and absolute — perfect for borders and tiny details, risky for fonts and big layout dimensions.remis relative to the root font size — the safest default for typography and consistent spacing, and it respects user settings.emis relative to the current element’s font size, so it compounds when nested — handy but use it with care.%is relative to the parent, making it the workhorse of fluid, flexible widths.vwandvhare relative to the viewport — great for full-screen sections and screen-scaling type.
Get comfortable with the difference between absolute and relative, and you’ll reach for the right unit on instinct. Up next we’ll dig into the display property — the setting that decides, at the most fundamental level, how an element behaves in the flow of the page.