High performance
web fonts

High performance
web fonts

Image Source: Formula 1

First Paint

Is it happening?

Did the navigation start successfully?

Has the server responded?

First Meaningful Paint

Is it useful?

Has enough content rendered that users can engage with it?

User Experience

~70%

use web fonts

Hello World!

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Hello World</title>

  <link rel="stylesheet" href="style.css">

</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Hello World</title>

  <link rel="stylesheet"
        href="https://fonts.googleapis.com/css?family=Tangerine:700">

  <link rel="stylesheet" href="style.css">

</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>

Moto 4G, slow 3G connection (global average)

System Fonts vs Google Fonts
A user experience

Why SO SLOW?

CSS loads are render blocking

Fonts only load when rendering

New connections are expensive

0.0s

4.1s

6.5s

Is it happening?

First paint

First meaningful paint

font limbo

FONT LIMBO

How low can you go?

Reducing limbo time

Reducing limbo time

Taking control

Image Source: Julius LeBlanc Stewart

Self-hosting Google Fonts

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Hello World</title>

  <style>
    @font-face {
      font-family: 'Tangerine';
      font-style: normal;
      font-weight: 700;
      src: url('fonts/tangerine-v9-latin-700.woff2') format('woff2'),
           url('fonts/tangerine-v9-latin-700.woff') format('woff');
    }
  </style>

  <link rel="stylesheet" href="style.css">

</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>

Preload

Image Source: Oversize Load

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  
  <!-- Tell the browser to load this immediately: 68.19% global usage-->
  <link rel="preload" as="font" type="font/woff2" crossorigin="anonymous"
        href="fonts/tangerine-v9-latin-700.woff2">

  <title>Hello World</title>

  <style>
    @font-face {
      /* tangerine-700 - latin */
      font-family: 'Tangerine';
      font-style: normal;
      font-weight: 700;
      src: url('fonts/tangerine-v9-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
           url('fonts/tangerine-v9-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
    }
  </style>

  <link rel="stylesheet" href="style.css">

</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>
<style>
  @font-face {
    font-family: 'Tangerine';
    font-style: normal;
    font-weight: 700;
    src: url('fonts/tangerine-v9-latin-700.woff2') format('woff2'),
         url('fonts/tangerine-v9-latin-700.woff') format('woff');
  }
</style>

<script>
  /* 86.3% global usage support: https://caniuse.com/#feat=font-loading /*
  try {
    document.fonts.forEach(function(f) {
      f.family.indexOf('Tangerine') !== -1 && f.load()
    })
  } catch (e) {}
</script>

A Preload Polyfill

15%
EXTRA

subsetting

Image Source: Bram Stein

Google fonts subsetting

/* cyrillic */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFUZ0bbck.woff2) format('woff2');
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}


/* greek */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVp0bbck.woff2) format('woff2');
  unicode-range: U+0370-03FF;
}


/* latin */
@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVZ0b.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Aggressive Subsetting

Image Source: Bram Stein

Graceful superset fallback

@font-face {
  font-family: 'Tangerine';
  src: url('fonts/tangerine-full.woff2') format('woff2');
}

@font-face {
  font-family: 'Tangerine__subset';
  src: url('fonts/tangerine-subset.woff2') format('woff2');
}

h1 {
  font-family: 'Tangerine__subset', 'Tangerine', sans-serif;
}

NEW!

Technique:

What characters am I using?

20%
OFF

High performance web fonts

Self-host Google Fonts
Create Font Subsets
Preload Subsets
CSS Font Loading API (Preload Fallback)

Graceful Superset Fallback

Limbo bonus: font-display

High performance web fonts

Self-host Google Fonts
Create Font Subsets
Preload Subsets
CSS Font Loading API (Preload Fallback)

Graceful Superset Fallback

+ Font-display: swap

Could this all be automated?

Self-host Google Fonts
Create Font Subsets
Preload Subsets
CSS Font Loading API (Preload Fallback)

Graceful Superset Fallback

+ Font-display: swap

Introducing: Subfont

npx subfont hello-world-google-fonts.html -i --inline-css

Nearest competitor: System fonts!

When to use subfont

When not to use subfont

  • Static site:
    Jekyll, Gatsby, Hugo, Wintersmith, etc
  • Build step
  • Google fonts
  • Local fonts
  • Any initial JS render is a rehydration, not a completely different rendering
  • Dynamic server content
  • Critical dynamic JS content
  • No build step
  • Using Typekit, Webtype, Fontspring
  • Your font license doesn't allow subsetting

Apply manual techniques

Standing on the shoulders of giants

THANKS!

High Performance Web Fonts, Front-trends 2018

By Peter Müller

High Performance Web Fonts, Front-trends 2018

  • 6,058