Absolute and relative
px is absolute: it is the same size whatever surrounds it. Everything else is relative to something — the font size of the element, of the root, of the viewport, or of the parent box. Choosing well means choosing what you want it to follow.
rem and em
html { font-size: 16px; } /* 1rem = 16px everywhere */
.card {
font-size: 1.25rem; /* 20px */
padding: 1em; /* 20px — follows THIS element's font */
margin-bottom: 1rem; /* 16px — follows the root */
}
| Unit | Relative to | Use for |
|---|---|---|
rem | The root font size — html | Almost everything: type scale, spacing, widths. |
em | The element's own font size | Spacing that must scale with the text it belongs to. |
Nest three elements that each set font-size: 0.9em and the innermost is at 0.73 of the original. That is occasionally what you want and usually a bug. rem never compounds.
Never set the root font size in px
A reader who has set a larger default font in their browser is telling you they need it. html { font-size: 16px } overrides that and freezes the whole page at your size. Leave the root alone, or set it as a percentage.
/* WRONG: ignores the reader's preference */
html { font-size: 16px; }
/* fine: scales with whatever they chose */
html { font-size: 100%; }
Percentages
A percentage is always relative to something, but what depends on the property, and that catches everyone:
width: 50%— half the containing block's width.height: 50%— half the containing block's height, and it only works if that height is known.padding: 5%andmargin: 5%— of the containing block's width, even top and bottom.font-size: 120%— of the parent's font size, exactly likeem.translate: 50%— of the element's own size, which is what makes the centring trick work.
Viewport units
.hero {
min-height: 100vh; /* viewport height */
width: 100vw; /* viewport width — includes the scrollbar */
padding: 2vmin; /* the smaller of vw and vh */
font-size: 4vmax; /* the larger */
}
/* mobile browsers: the toolbar appears and disappears */
.hero-safe {
min-height: 100svh; /* smallest — toolbars showing */
min-height: 100lvh; /* largest — toolbars hidden */
min-height: 100dvh; /* dynamic — follows, and can cause reflow while scrolling */
}
100vw includes the scrollbar width, so on a desktop page that scrolls it is wider than the page and causes a horizontal scrollbar — use 100%. And 100vh on a phone is taller than the visible area while the toolbar is showing; 100svh or 100dvh is what you mean.
ch and ex
1ch is the width of the 0 character in the current font. It is the honest unit for line length: max-width: 65ch gives roughly 65 characters per line, which is the range most readable for body text.
.prose p {
max-width: 68ch;
}
clamp, min and max
These three take the place of most media queries for sizing. clamp(MIN, PREFERRED, MAX) is the one you will use daily: a value that scales with the viewport but never leaves its bounds.
h1 {
font-size: clamp(2rem, 1.2rem + 3vw, 4.5rem);
}
.wrap {
width: min(1100px, 100% - 3rem); /* never wider than 1100, always with a gutter */
margin-inline: auto;
}
.panel {
padding: max(1rem, 3vw); /* never tighter than 1rem */
}
Always include a rem term in the middle value of a clamp for font size. clamp(2rem, 5vw, 4rem) with a pure viewport term stops the text scaling when the reader zooms, which is an accessibility failure.
fr, and the sizing keywords
fr— a fraction of the free space in a grid. Only valid in grid track sizing.min-content— the narrowest the content can be without overflowing: the longest word.max-content— as wide as the content wants on one line.fit-content—max-content, capped by the space available.auto— "work it out", and what it means depends on the property and the layout mode.
Angles, times and resolution
transform: rotate(45deg); /* deg, grad, rad, turn */
transition: opacity 200ms; /* s, ms */
animation-duration: 0.4s;
@media (min-resolution: 2dppx) { } /* device pixels per CSS pixel */
A working rule
- Type and spacing:
rem. Consistent, and it respects the reader's font size. - Spacing that belongs to the text:
em. - Line length:
ch. - Layout widths:
%,fr,min()/max()/clamp(). - Full-screen sections:
svh/dvh, notvh. - Borders and hairlines:
px. A 1px border should be 1px.