Beginner9 min read

Web Fonts and @font-face: Custom Typography

9 min read
544 words
15 sections10 code blocks

Why Use Custom Web Fonts?

Default system fonts are limited. Custom web fonts let you use any typeface on your website, giving you full control over your design and brand identity.

There are two main ways to add custom fonts:

  • Google Fonts (or other hosted services) for easy, quick setup
  • @font-face for self-hosting fonts with full control

Using Google Fonts

Google Fonts is the easiest way to add custom fonts. It hosts over 1,500 free fonts.

Step 1: Choose a Font

Visit fonts.google.com and select your fonts. Then add the provided link to your HTML.

HTML
<head>
  <!-- Preconnect for faster loading -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

  <!-- Load the font -->
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>

Step 2: Use the Font in CSS

CSS
body {
  font-family: 'Inter', sans-serif;
}

h1, h2, h3 {
  font-weight: 700;
}

p {
  font-weight: 400;
}

Loading Multiple Fonts

HTML
<!-- Load multiple fonts in one request -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
CSS
body {
  font-family: 'Inter', sans-serif;
}

code, pre {
  font-family: 'Fira Code', monospace;
}

The @font-face Rule

The `@font-face` rule lets you self-host fonts. This gives you more control over loading and eliminates dependency on third-party services.

CSS
@font-face {
  font-family: 'MyCustomFont';
  src: url('/fonts/mycustomfont.woff2') format('woff2'),
       url('/fonts/mycustomfont.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'MyCustomFont';
  src: url('/fonts/mycustomfont-bold.woff2') format('woff2'),
       url('/fonts/mycustomfont-bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: 'MyCustomFont', sans-serif;
}

Font Formats

  • woff2: Best compression, supported by all modern browsers. Always use this first.
  • woff: Good compression, slightly wider browser support as a fallback
  • ttf/otf: Older formats, larger file sizes, use only if you must support very old browsers

For modern websites, you only need woff2 and optionally woff as a fallback.

Font Display Property

The `font-display` property controls how text is shown while the custom font is loading.

CSS
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2');
  font-display: swap;   /* Recommended for most cases */
}

The available values are:

  • swap: Shows text immediately with a fallback font, then swaps to the custom font once loaded. Best for body text.
  • block: Hides text briefly (up to 3 seconds) while the font loads. Use for icon fonts.
  • fallback: Short invisible period, then shows fallback. May never swap if loading is slow.
  • optional: Very short invisible period. Browser decides whether to use the custom font based on connection speed.
  • auto: Browser decides the behavior (default).

Use `swap` for most fonts. It ensures text is always visible and readable.

Optimizing Font Loading

Preload Critical Fonts

HTML
<head>
  <link rel="preload" href="/fonts/inter-regular.woff2" as="font" type="font/woff2" crossorigin>
  <link rel="preload" href="/fonts/inter-bold.woff2" as="font" type="font/woff2" crossorigin>
</head>

Preloading tells the browser to download the font file as early as possible, reducing the time users see the fallback font.

Only Load Weights You Need

Every font weight and style is a separate file. Only load what you actually use.

CSS
/* Bad: loading too many weights */
/* 100, 200, 300, 400, 500, 600, 700, 800, 900 = 9 files! */

/* Good: only what you need */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-semibold.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-bold.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

Subset Fonts

If you only need Latin characters, use a subset font that excludes characters for other languages. This can reduce file size by 50% or more.

Google Fonts does this automatically. For self-hosted fonts, you can use tools like glyphhanger or subfont to create subsets.

Variable Fonts

Variable fonts contain all weights and styles in a single file. Instead of loading separate files for regular, bold, and italic, one variable font file handles them all.

CSS
@font-face {
  font-family: 'InterVariable';
  src: url('/fonts/Inter-Variable.woff2') format('woff2');
  font-weight: 100 900;   /* Supports all weights from 100 to 900 */
  font-display: swap;
}

body {
  font-family: 'InterVariable', sans-serif;
}

h1 { font-weight: 750; }   /* Any weight between 100-900 */
h2 { font-weight: 650; }
p  { font-weight: 400; }
.light { font-weight: 300; }

Variable fonts are more efficient when you use multiple weights since you only download one file instead of several.

System Font Stacks

Sometimes the best approach is to use system fonts. They load instantly because they are already on the user's device.

CSS
/* Modern system font stack */
body {
  font-family:
    -apple-system,
    BlinkMacSystemFont,
    'Segoe UI',
    Roboto,
    Oxygen-Sans,
    Ubuntu,
    Cantarell,
    'Helvetica Neue',
    sans-serif;
}

/* Monospace system stack */
code {
  font-family:
    'SF Mono',
    SFMono-Regular,
    Consolas,
    'Liberation Mono',
    Menlo,
    monospace;
}

This gives you a clean, modern look with zero loading time.

Summary

Custom web fonts enhance your design and brand identity. Use Google Fonts for quick setup or @font-face for full control with self-hosted fonts. Always set `font-display: swap` for visible text during loading. Optimize by preloading critical fonts, loading only necessary weights, and considering variable fonts. For maximum performance, system font stacks eliminate loading entirely.

In the next section, you will learn about CSS visual effects like borders, shadows, and transforms.