Most of the web is text. A few typography properties, set well once on body and the headings, make every page easier to read. This lesson covers those properties and sensible values for each.
font-family and font stacks
font-family takes a comma-separated list of fonts. The browser uses the first one that is available:
body {
font-family: "Source Sans 3", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
This list is called a font stack. It always ends with a generic family, which the browser maps to a font that definitely exists:
| Generic family | Looks like |
|---|---|
serif | Fonts with small strokes at the ends, such as Georgia |
sans-serif | Clean fonts without them, such as Arial |
monospace | Every character the same width, for code |
system-ui | The operating system's interface font |
cursive, fantasy | Rarely useful |
Quote font names that contain spaces. Never quote the generic families: "sans-serif" in quotes means a font literally called "sans-serif", which does not exist.
The system font stack
Using the operating system's own font is fast (nothing to download) and looks native on every device:
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
code,
pre {
font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
}
Loading your own fonts with @font-face comes in the web fonts lesson.
font-size and a type scale
Set body text in rem so it respects the user's settings, then size headings relative to it:
body {
font-size: 1rem;
}
h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }
h4 { font-size: 1.25rem; }
small,
.text-small {
font-size: 0.875rem;
}
A type scale is a consistent set of sizes like this, often generated by multiplying by a ratio such as 1.25. Using a fixed set, rather than a different size for every element, makes a design look deliberate.
Body text at the browser default of 16px (1rem) is a sensible minimum for most sites. Going smaller for main content makes reading harder for many people. Making sizes grow smoothly with the screen is covered in the Responsive Web Design course.
font-weight
h1, h2, h3 {
font-weight: 700;
}
.lead {
font-weight: 300;
}
Weights run from 100 (thin) to 900 (black). 400 is normal and 700 is bold; the keywords normal and bold mean the same. A font only shows the weights it actually has. If you ask for 300 and the font has no light version, the browser picks the closest available weight.
line-height
line-height sets the height of each line, which controls the space between lines of text. It has more effect on readability than any other property.
body {
line-height: 1.5;
}
h1, h2, h3 {
line-height: 1.2;
}
Always use a unitless number. Around 1.5 suits body text; large headings look better tighter, around 1.1 to 1.3, because the gap grows with the font size.
Letter and word spacing
.eyebrow {
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.08em;
}
letter-spacing adds space between characters. Use em so it scales with the text. Uppercase text and small labels often benefit from a little extra; body text rarely needs any.
text-transform: uppercase changes only the appearance. Write the text in normal case in your HTML. Some screen readers spell out text typed in capitals letter by letter, and copied text keeps its original case.
Text alignment
.hero {
text-align: center;
}
.price {
text-align: end; /* right in left-to-right languages */
}
Values include start, end, left, right, center and justify. Prefer start and end, which flip automatically for right-to-left languages.
Keep long paragraphs left-aligned (start-aligned). Centred text is fine for a short heading or two lines, but ragged left edges make longer text tiring to read. justify can create uneven gaps between words, especially in narrow columns.
Line length
Comfortable lines usually hold somewhere between 45 and 75 characters. Cap the width of text containers with ch:
.prose {
max-width: 65ch;
}
Balanced headings
text-wrap: balance asks the browser to spread a heading's words evenly across its lines, so you do not get one lonely word on the last line:
h1, h2, h3 {
text-wrap: balance;
}
p {
text-wrap: pretty;
}
text-wrap: pretty makes smarter line breaks in paragraphs, such as avoiding a single short word on the last line. Both are progressive enhancements: browsers that do not support them simply wrap normally. Check their Baseline status on web.dev.
Links and decoration
a {
color: #0b5394;
text-decoration-thickness: 0.08em;
text-underline-offset: 0.15em;
}
a:hover {
text-decoration-thickness: 0.15em;
}
text-decoration controls underlines. Rather than removing them, modern properties let you make them look good: text-decoration-thickness, text-underline-offset and text-decoration-color. Keep links in body text underlined so they do not rely on colour alone. You will style hover and focus states properly in the pseudo-classes lesson.
Other useful properties
.label {
font-style: italic;
font-variant-numeric: tabular-nums; /* digits line up in columns */
}
.long-url {
overflow-wrap: anywhere; /* break long words instead of overflowing */
}
.title {
font: 700 2rem/1.2 Georgia, serif; /* shorthand: weight size/line-height family */
}
The font shorthand resets every font property it does not mention, so use it deliberately, typically only on body.
Putting it together
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
font-size: 1rem;
line-height: 1.6;
color: #1f2933;
}
h1, h2, h3 {
font-family: Georgia, "Times New Roman", serif;
line-height: 1.2;
font-weight: 700;
text-wrap: balance;
}
h1 { font-size: 2.5rem; }
h2 { font-size: 1.75rem; }
.prose {
max-width: 65ch;
}
a {
color: #0b5394;
text-underline-offset: 0.15em;
}
Checking fonts in DevTools
Select an element and open the Computed tab. At the bottom, Chrome lists Rendered Fonts: the font that was actually used. If it shows a fallback instead of the font you asked for, the font is not installed, not loaded, or misspelt in your stack.
Summary
- Write font stacks that end with a generic family, and quote names with spaces.
- Size text in
remwith a consistent type scale; keep body text at a readable size. - Use unitless
line-height, around 1.5 for body text and tighter for headings. - Keep lines around 45 to 75 characters with
max-widthinch. - Prefer
startalignment for paragraphs, keep links underlined, and write capitals in CSS, not HTML.