Advanced15 min read

Web Fonts & @font-face

Learn how to use custom fonts on the web with @font-face, Google Fonts, font-display strategies, and proper fallback stacks.

Beyond System Fonts

By default, browsers can only display fonts installed on the user's operating system. These are called system fonts or web-safe fonts — families like Arial, Times New Roman, Georgia, and Courier New that are available on virtually every device.

To use custom typefaces, you need web fonts — font files that the browser downloads alongside your CSS. There are two main approaches:

  1. Self-hosted fonts — You download font files (.woff2, .woff) and serve them from your own server using the @font-face rule.
  2. Hosted font services — Services like Google Fonts, Adobe Fonts, or Bunny Fonts host the files for you. You link to their stylesheet and the fonts are loaded automatically.

The @font-face rule tells the browser: "Here is a custom font family name, and here is where to find the file."

The @font-face Rule

css
/* Self-hosted font */
@font-face {
  font-family: "MyCustomFont";
  src: url("/fonts/my-font.woff2") format("woff2"),
       url("/fonts/my-font.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Using the custom font with fallbacks */
body {
  font-family: "MyCustomFont", Arial, Helvetica, sans-serif;
}

/* Google Fonts approach (in HTML <head>): */
/* <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet"> */
/* Then in CSS: */
h1 {
  font-family: "Inter", sans-serif;
}

Font-Display and Performance

Loading custom fonts introduces a performance concern: what does the browser show while the font file is downloading?

The font-display property in @font-face controls this behavior:

ValueBehavior
autoBrowser default (usually similar to block)
blockHide text for up to 3 seconds while loading, then swap
swapShow fallback font immediately, swap to custom font when loaded
fallbackVery short block period (~100ms), then fallback. If the font loads within ~3s it swaps; otherwise the fallback stays
optionalVery short block period. Browser may decide not to use the custom font at all on slow connections

Best practice: Use font-display: swap for body text so users can read content immediately. Use font-display: optional for non-critical decorative fonts.

Font stack best practices

Always provide a fallback stack — a list of fonts the browser tries in order:

css
font-family: "Custom Font", system-ui, -apple-system, sans-serif;

Choose fallbacks with similar metrics (x-height, character width) to minimize layout shift when the custom font loads.

What does `font-display: swap` do?

Ready to practice?

Create your free account to access the interactive code editor, run challenges, and track your progress.