High performance
web fonts

Image Source: Formula 1

~70%

use web fonts

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

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>

System Fonts vs Google Fonts
A user experience

Why SO SLOW?

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

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

flash of Invisible text

Loading fonts faster

LOADING FONTS FASTER

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

Controlling rendering: 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!

Subfont  💖  jamstack

Image Source: Michal Osmenda

Subset caveat: Kerning

Subset caveat: ligatures

Controlling order: Font face observer

THANKS!

High Performance Web Fonts, CSS-Minsk-JS 2018

By Peter Müller

High Performance Web Fonts, CSS-Minsk-JS 2018

Web fonts and web performance are diametrical opposites. Your time to meaningful paint suffers from inefficient loading. Web fonts are also empowering your designers to improve legibility and keep brand consistency. Choose one: Performance or design? I'm here to tell you how you can have both

  • 2,267