CSS Advanced
CSS Custom Fonts
Custom fonts give a site its own voice. You can host font files yourself with the @font-face rule or link a service like Google Fonts. Good practice includes a fallback stack and a font-display strategy to avoid invisible text while fonts load.
The @font-face rule
@font-face defines a custom font family by pointing to font files. Once declared, you use the chosen family name anywhere in font-family. Provide modern woff2 first for the smallest download.
@font-face {
font-family: "Brandon";
src: url("/fonts/brandon.woff2") format("woff2"),
url("/fonts/brandon.woff") format("woff");
font-weight: 400;
font-style: normal;
font-display: swap;
}
body { font-family: "Brandon", system-ui, sans-serif; }Using Google Fonts
Google Fonts serves the CSS and font files for you. You add a <link> in the HTML head (or an @import in CSS), then reference the family name. The link method is generally preferred for performance.
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
<style>
h1 { font-family: "Poppins", sans-serif; font-weight: 700; }
</style>The live preview in this tutorial cannot fetch external font files, so remote fonts fall back to a system font. The examples below use system font stacks to demonstrate typography concepts that render everywhere.
font-display strategies
| Value | Behavior |
|---|---|
| swap | Show fallback immediately, swap in web font when ready |
| block | Hide text briefly, then show web font (risk of invisible text) |
| fallback | Very short block, then fallback if font is slow |
| optional | Use web font only if it loads almost instantly |
Font stacks and fallbacks
Always list fallback fonts after your custom one. The browser uses the first family that is available, ending with a generic keyword like serif or sans-serif so text always renders.
<!DOCTYPE html>
<html>
<head>
<style>
body { padding: 24px; background:#f8fafc; color:#0f172a; }
.sans { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
.serif { font-family: Georgia, "Times New Roman", serif; }
.mono { font-family: "SF Mono", Menlo, Consolas, monospace; }
p { font-size: 22px; margin: 8px 0; }
</style>
</head>
<body>
<p class="sans">Sans-serif stack: clean and modern.</p>
<p class="serif">Serif stack: classic and editorial.</p>
<p class="mono">Monospace stack: code and data.</p>
</body>
</html>Weight, style, and variable fonts
You can register multiple @font-face rules for the same family with different font-weight and font-style values. Variable fonts pack many weights into one file and expose them through a range, reducing requests.
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Georgia, serif; padding: 24px; background:#fff; }
p { font-size: 22px; margin: 6px 0; }
.light { font-weight: 300; }
.bold { font-weight: 700; }
.italic { font-style: italic; }
</style>
</head>
<body>
<p class="light">Light weight text</p>
<p>Normal weight text</p>
<p class="bold">Bold weight text</p>
<p class="italic">Italic style text</p>
</body>
</html>Use font-display: swap and a preconnect to font hosts so users see readable fallback text immediately instead of a flash of invisible text (FOIT).
Only load the weights and styles you actually use. Each extra font file adds download weight and slows page load.
Key points
- @font-face registers a self-hosted font family from file URLs.
- Google Fonts is added via a <link> plus the family name.
- font-display: swap avoids invisible text during load.
- Always end font-family with a generic fallback keyword.
- Variable fonts provide many weights from a single file.
