Absolute and relative units
A stylesheet written only in px breaks the moment anything resizes. Users raise their default font size, which is a common accessibility setting, phones vary in width, and text zooms.
Relative units exist so one set of styles survives all of that, and choosing the right unit is a real daily decision on working teams.
| Unit | Relative to | Good for |
|---|---|---|
px | nothing, it is fixed | borders, small gaps, max-widths |
% | the parent's size | fluid layout widths |
em | the element's own font size | padding that scales with its text |
rem | the root (html) font size | font sizes and spacing |
vw and vh | the viewport width or height | full-screen sections |
The root font size defaults to 16px, so 1rem is 16px and 1.5rem is 24px. Unlike em, rem never compounds through nesting, which is why it is the go-to unit.
100vh is exactly one screen tall, a favorite for hero sections, meaning the full-screen opening banner at the top of a landing page that holds the big headline and main button. The capstone builds one.
Text in
rem, layout widths in%with amax-width, full-screen sections invh, hairlines inpx.
Converting rem to pixels
With the root font size at the default 16px, 2.5rem is 40px.
rem multiplies the root font size, so 2.5 × 16 = 40.
| Value | Pixels |
|---|---|
1rem | 16 |
1.5rem | 24 |
2.5rem | 40 |
It does not care about the parent element's font size, only the root's, which is exactly what makes the arithmetic predictable anywhere in the document tree.
Converting a mockup to relative units
A designer hands over a mockup measured in pixels. A working stylesheet translates it with the rule of thumb above.
| Mockup says | Written as | Reason |
|---|---|---|
| h1 is 40px | font-size: 2.5rem | 40 ÷ 16 = 2.5, and it scales with the base size |
| card padding 24px | padding: 1.5rem | spacing tracks the text size |
| hairline divider 1px | border: 1px solid gray | must stay one device pixel |
| content column 960px | width: 90%; max-width: 60rem | fluid on phones, capped on monitors |
| full-screen intro | min-height: 100vh | one viewport tall on any device |
The point of the conversion is that the numbers stop being frozen. A user who sets their browser font to 20px gets an h1 of 50px and padding of 30px automatically, and nothing overlaps.
The hairline is the instructive exception. A 1px divider expressed in rem would thicken to an ugly 1.25px at that same setting, for no benefit, so the fixed unit is the correct choice there.
How em compounds
A card at font-size: 20px with an inherited paragraph declaring padding: 1em gives the paragraph 20px of padding.
em multiplies the element's own font size, which here is the inherited 20px, so 1em is 20px.
| Declared | Relative to | Result |
|---|---|---|
1em | the inherited 20px | 20px |
1rem | the root 16px | 16px |
That compounding through nesting is exactly what rem avoids, since 1rem would have been 16px regardless of the card. The compounding is occasionally useful, as in a button whose padding should grow with its label, and it is a liability everywhere else.
Two conversion helpers
The conversion runs both ways, and each direction is a single arithmetic operation.
function remToPx(rem, rootSize) { return rem * rootSize; } function pxToRem(px, rootSize) { return px / rootSize; } console.log(remToPx(1.5, 16)); console.log(remToPx(2.5, 16)); console.log(pxToRem(24, 16));
Output
24 40 1.5
| Direction | Operation |
|---|---|
| rem to px | multiply by the root size |
| px to rem | divide by the root size |
Passing rootSize in rather than hardcoding 16 keeps the helpers honest, because a project that changes the root size changes every conversion at once. That is the whole reason the unit is worth using.
The viewport height unit
The unit equal to 1% of the browser window's height is vh, for viewport height.
So height: 100vh fills the screen exactly, and vw does the same for width.
| Unit | 1% of |
|---|---|
vh | the viewport height |
vw | the viewport width |
% | the parent element |
The third row is the contrast that matters, since a percentage height depends on the parent and a vh height never does. On phones the address bar makes 100vh slightly taller than the visible area, which is why min-height is the safer property to pair it with.