Fonts and text
Font families and fallbacks
<style>
p { font-size: 18px; margin: 6px 0; }
</style>
<p style="font-family: Georgia, 'Times New Roman', serif">Georgia, a serif font</p>
<p style="font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif">Your system's own interface font</p>
<p style="font-family: 'Courier New', monospace">Courier New, a monospaced font</p>
<p style="font-family: 'Comic Sans MS', cursive">A cursive font, if one is installed</p>font-family takes a list, and the browser uses the first font that's available. Always end with a generic family, such as serif, sans-serif, monospace or system-ui, so there's a sensible fallback. Put font names that contain spaces in quotes.
Web fonts
To use a font visitors don't have installed, the page has to load it. Google Fonts gives you the <link> tags:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Inter:wght@400;600&display=swap" rel="stylesheet">
<style>
h1 { font-family: 'Playfair Display', serif; font-size: 40px; margin: 0 0 8px; }
p { font-family: 'Inter', sans-serif; font-size: 17px; }
</style>
<h1>A display heading</h1>
<p>Paired with a clean, readable body font.</p>display=swap shows the text in a fallback font straight away and swaps the web font in when it arrives, instead of hiding the text while it loads. To host font files yourself, the @font-face rule does the loading. Each extra font and weight is another download, so two families is usually plenty.
Size, weight and style
<style>
body { font-family: system-ui; }
p { margin: 6px 0; }
</style>
<p style="font-size: 2rem; font-weight: 800">2rem, weight 800</p>
<p style="font-size: 1.25rem; font-weight: 600">1.25rem, weight 600</p>
<p style="font-size: 1rem; font-weight: 400">1rem, weight 400 (normal)</p>
<p style="font-size: .875rem; font-weight: 300; font-style: italic">0.875rem, weight 300, italic</p>Weights run from 100 (thin) to 900 (black). 400 is normal and 700 is bold, and a weight only looks different if the font actually includes it.
Line height and spacing
<style>
body { font: 16px system-ui; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.tight { line-height: 1.1; }
.comfy { line-height: 1.6; }
.label { grid-column: 1 / -1; margin: 0; text-transform: uppercase; letter-spacing: .12em;
font-size: 13px; font-weight: 700; color: #5a48f5; }
</style>
<p class="label">Letter-spaced capitals for labels</p>
<p class="tight">line-height 1.1: the lines crowd each other, and a long paragraph becomes tiring, because your eye struggles to find the start of the next line.</p>
<p class="comfy">line-height 1.6: the lines have room to breathe, and a long paragraph stays easy to read, because your eye finds the next line straight away.</p>Write line-height without a unit, like 1.6: it then scales with each element's own font size.
Alignment and decoration
<style>
body { font-family: system-ui; }
.c { text-align: center; }
.r { text-align: right; }
.wavy { text-decoration: underline wavy #e0446a; text-underline-offset: 4px; }
.old { text-decoration: line-through; color: #888; }
.plain { color: #5a48f5; font-weight: 600; text-decoration: none; }
.plain:hover { text-decoration: underline; }
</style>
<p class="c">Centred</p>
<p class="r">Right-aligned</p>
<p><span class="wavy">A wavy underline</span> and <span class="old">struck-through text</span>.</p>
<p><a class="plain" href="#">A link with no underline until you hover</a></p>When text is too long
<style>
.title { width: 220px; padding: 6px; border: 1px dashed #bbb; font: 16px system-ui;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
</style>
<div class="title">An extremely long course title that would never fit</div>The three properties work as a team: don't wrap, hide whatever overflows, and show "…" where the text is cut.