Web fonts
In the lesson "Text and fonts" fonts came from the reader's device. If you need a special font the reader does not have, its file is put on the site and connected. Such a font is called a web font: the browser downloads it together with the page.
The @font-face rule
@font-face declares a font: what name to give it and where the file lives. Each style — regular, bold, italic — gets its own rule with the same name:
@font-face {
font-family: "Inter";
src: url("fonts/inter-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "Inter";
src: url("fonts/inter-bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
}
body { font-family: "Inter", sans-serif; }Now font-weight: 700 takes the second file. The woff2 format is a compressed font for the web: it is several times lighter than older formats and understood by every modern browser, so other formats are no longer needed.
The name is yours
font-family inside @font-face is just a name you make up. The local() function takes a font from the device, so this example works without downloading any files:
<style>
@font-face {
font-family: "Headings";
src: local("Georgia"), local("Times New Roman");
}
h2 { font-family: "Headings", serif; }
</style>
<h2>A heading in the "Headings" font</h2>
<p>Regular text in the default font.</p>The browser tries the sources from src in order and takes the first one available.
While the font loads: font-display
The font file does not arrive at once. The font-display property inside @font-face decides what to show meanwhile:
| Value | What happens |
|---|---|
swap | the fallback font at once, then swapped for the real one |
block | up to 3 seconds of invisible text, then the real font |
fallback | a short pause, and if the font is late, the fallback stays |
optional | the font only if it arrived almost instantly |
For text, swap is the usual choice: the reader sees the words at once instead of a blank page.
Variable fonts
Usually each style is a separate file. A variable font keeps all weights in one file, and you can pick any, even 550. In @font-face the range is given as font-weight: 100 900. The system font of many devices is variable too — if yours is, each line is bolder than the one before:
<style>
p { font-family: system-ui, sans-serif; font-size: 20px; margin: 4px 0; }
</style>
<p style="font-weight: 200;">Weight 200</p>
<p style="font-weight: 350;">Weight 350</p>
<p style="font-weight: 550;">Weight 550</p>
<p style="font-weight: 800;">Weight 800</p>Such fonts may have other axes — smoothly adjustable parameters, such as letter width. They are controlled with font-variation-settings: "wght" 550, "wdth" 80. But for weight the regular font-weight is more convenient.
Google Fonts
In the fonts.google.com catalog you pick a font and get ready code for <head>. The preconnect lines open a connection to the font servers in advance, so the file arrives sooner:
<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=Inter:wght@100..900&display=swap" rel="stylesheet">wght@100..900 asks for the whole variable font, and display=swap is the same as font-display: swap. Then in CSS you write font-family: "Inter", sans-serif.
Self-hosting or a CDN
A CDN is someone else's server that hands out files, like Google Fonts. It is the easiest way to add a font, but the browser goes to another site, and a third party sees the visit. You can download the font files and put them next to your site — this is self-hosting. It does not depend on a foreign server, and in Europe it is often chosen for privacy. Either way, keep a fallback list: font-family: "Inter", system-ui, sans-serif.
Common mistake
The path in url() is relative to the CSS file, not to the HTML page. If the styles are in css/style.css and the fonts in a fonts folder next to css, the path url("fonts/inter.woff2") points to a non-existent css/fonts/:
my-site/
├── index.html
├── css/
│ └── style.css ← url("../fonts/inter.woff2")
└── fonts/
└── inter.woff2The browser silently shows the fallback font. You can check whether the file loaded in the Network tab of the developer tools.