The Five Whys of Web Font Loading Performance

A presentation at performance.now() in November 2018 in Amsterdam, Netherlands by Zach Leatherman

Slide 1

Slide 1

@zachleat @zachleat @zachleat.com @Anvil transition enthusiast

Slide 2

Slide 2

WHYs THE 5 WHYs OF WEB WHYs LOADIN WHYs PERFOR WHYs

Slide 3

Slide 3

WHYs WHYs OF WEB FONT WHYs LOADING WHYs PERFORMANCE WHYs

Slide 4

Slide 4

@zachleat HY WHY WHY WHY W HY WHY WHY WHY W HY WHY WHY WHY W HY WHY WHY WHY W HY WHY WHY WHY W

Slide 5

Slide 5

@zachleat WHY WHY WHY WHY YWHY WHY WHY WHY WHY WHY WHY WHY WHY YWHY WHY WHY WHY WHY WHY WHY WHY WHY YWHY WHY WHY WHY WHY WHY WHY WHY WHY YWHY WHY WHY WHY WHY WHY WHY WHY WHY YWHY WHY WHY WHY WHY

Slide 6

Slide 6

Y THE 5Y OF WEB FONT Y LOADING Y PERFORMANCE Y

Slide 7

Slide 7

THE DEFAULT Wordpress Site

Slide 8

Slide 8

THE DEFAULT Wordpress Site 595 KB 35 requests

Slide 9

Slide 9

17% WEB Fonts 100 KB in 5 requests YYYYY 48% Images, 8 requests 22% JavaScript, 12 requests 17% Fonts, 5 requests 12% CSS, 8 requests 1% HTML, 2 requests

Slide 10

Slide 10

Y SANS SERIF 15.3 KB

Slide 11

Slide 11

YY SANS SERIF BOLD 15.3 KB

Slide 12

Slide 12

YYY SERIF 22.9 KB

Slide 13

Slide 13

YYYY 20.8 KB SERIF ITALIC

Slide 14

Slide 14

YYYY Y 26.2 KB SERIF BOLD

Slide 15

Slide 15

All demos on GitHub https://github.com/zachleat/performance-sometime

Slide 16

Slide 16

https://www.webpagetest.org/ METHODOLOGY 3G Network Speed HTTP/2 and HTTPS CHROME & WOFF2 ONLY FONT OPTIMIZATIONS @zachleat

Slide 17

Slide 17

THE default Wordpress Site SERIF SANS-SERIF BOLD SERIF BOLD SERIF ITALIC SANS-SERIF first render 3.3s visually complete 5.7s

Slide 18

Slide 18

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION

Slide 19

Slide 19

OUR OPPONENTS: INVISIBLE TEXT FOIT TEXT IN MOTION

Slide 20

Slide 20

@zachleat https://twitter.com/jmuspratt/status/561239961924403200

Slide 21

Slide 21

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION FOUT

Slide 22

Slide 22

CRITICAL REQUEST CHAINS DOMCONTENTLOADED FIRST CONTENTFUL PAINT FIRST PAINT PERFORMANCE METRICS FIRST RENDER VISUALLY COMPLETE SPEED INDEX ONLOAD TIME TO INTERACTIVE CPU IDLE

Slide 23

Slide 23

FIRST RENDER FIRST CONTENTFUL PAINT

Slide 24

Slide 24

FIRST MEANINGFUL PAINT Finally a metric that PROPERLY accounts for web fonts 🎉 MEASURE using LIGHTHOUSE

Slide 25

Slide 25

OPPONENTS: INVISIBLE TEXT TEXT IN MOTION NEW METRIC: ALL TEXT VISIBLE

Slide 26

Slide 26

THE default Wordpress Site ALL TEXT VISIBLE first render 3.3s 👀 4.9s visually complete 5.7s

Slide 27

Slide 27

OPPONENTS: INVISIBLE TEXT TEXT IN MOTION NEW METRIC: WEB FONT REFLOW COUNT * After first render

Slide 28

Slide 28

REFLOW COUNTER 0 @zachleat

Slide 29

Slide 29

REFLOW COUNTER 10 @zachleat

Slide 30

Slide 30

REFLOW COUNTER 21 @zachleat

Slide 31

Slide 31

REFLOW COUNTER 32 @zachleat

Slide 32

Slide 32

REFLOW COUNTER 43 @zachleat

Slide 33

Slide 33

REFLOW COUNTER 54 @zachleat

Slide 34

Slide 34

REFLOW COUNTER 65 @zachleat

Slide 35

Slide 35

RE F RE LO FLO W W RE F R L E RE FL OW FLO OW W THE default Wordpress Site first render 3.3s visually complete 5.7s

Slide 36

Slide 36

OUR METRICS: ALL TEXT VISIBLE WEB FONT REFLOW COUNT

Slide 37

Slide 37

YYYYY

Slide 38

Slide 38

YYYYY “WEB-SAFE”/SYSTEM/INSTALLED FONTS

Slide 39

Slide 39

YYYYY System fonts System Fonts: Baseline: ▼ -6% 3.1s first render 3.3s ▼ -10% 5.1s visually complete 5.7s

Slide 40

Slide 40

YYYYY System fonts No network requests INSTANT RENDERING @zachleat

Slide 41

Slide 41

@zachleat THANK YOU @zachleat @zachleat.com

Slide 42

Slide 42

@zachleat

Slide 43

Slide 43

@zachleat

Slide 44

Slide 44

BEST VIEWED @zachleat IN CHROME

Slide 45

Slide 45

@zachleat https://github.com/alrra/browser-logos

Slide 46

Slide 46

@zachleat

Slide 47

Slide 47

THE default Wordpress Site first render 3.3s visually complete 5.7s

Slide 48

Slide 48

Slide 49

Slide 49

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 50

Slide 50

YYYYY PRECONNECT

Slide 51

Slide 51

<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin>

Slide 52

Slide 52

<!-- CSS --> <link rel='dns-prefetch' href='//fonts.googleapis.com'> <!-- Fonts --> <link rel='preconnect' href='https://fonts.gstatic.com' crossorigin> <link rel='alternate' …> <link rel='alternate' …> <script>…</script> <script>…</script> <style>…</style> <link rel='stylesheet' …> <link rel='stylesheet' href=' https://fonts.googleapis.com/css?family= Noto Sans:400italic,700italic,400,700| Noto Serif:400italic,700italic,400,700| Inconsolata:400,700&subset=latin,latin-ext'>

Slide 53

Slide 53

PRECONNECT 🎉 WITHOUT CSS WITH FONTS @zachleat

Slide 54

Slide 54

Preconnect Browser SUPPORT https://caniuse.com/#feat=link-rel-preconnect

Slide 55

Slide 55

PRECONNECT: GREAT FOR CROSS-ORIGIN FONT REQUESTS @zachleat

Slide 56

Slide 56

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 57

Slide 57

YYYYY THIRD PARTY ▸ SELF HOSTING

Slide 58

Slide 58

YYYYY SELF HOSTING BASE LINE ▼ 👀 Self Hosting: Baseline: ▼ -6% 3.1s first render 3.3s ▼ -1% 5.6s visually complete 5.7s

Slide 59

Slide 59

WHAT HAPPENED? SUPER LONG TTFB’S NETWORK CONGESTION BAD HTTP/2 prioritiZATION @zachleat Read more: https://blog.cloudflare.com/http-2-prioritization-with-nginx/

Slide 60

Slide 60

@font-face { src: url(notosans.woff2) format('woff2'); } ▼ @font-face { src: url(https://performance-sometimeassets.netlify.com/notosans.woff2) format('woff2'); }

Slide 61

Slide 61

YYYYY SELF HOSTING SELF HOST ▼ 👀 Self Hosting: Baseline: ▼ -6% 3.1s first render 3.3s ▼ -1% 5.6s visually complete 5.7s

Slide 62

Slide 62

YYYYY SELF HOSTING WITH A BIT OF SHARDING USING PRECONNECT 👀 Self Host & Shard: Baseline: ▼ -6% 3.1s first render 3.3s 5.7s visually complete 5.7s

Slide 63

Slide 63

***DON’T DO THIS, PROBABLY SHARDING IS AN ANTI-PATTERN ON HTTP/2 @zachleat

Slide 64

Slide 64

“ ONE PERSON’S TRASH IS ANOTHER PERSON’S -PATTERN TREASURE” ” ANTI-PATTERN

Slide 65

Slide 65

WHY SELF HOST? @zachleat

Slide 66

Slide 66

WHY SELF HOST? HOLD YOUR HORSES ⭐⭐⭐⭐🐴 @zachleat

Slide 67

Slide 67

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 68

Slide 68

YYYYY FONT-DISPLAY

Slide 69

Slide 69

@font-face { font-display: swap; } Shows Fallback Font Immediately, RENDER WEB FONT WHENEVER @zachleat Learn more: https://font-display.glitch.me/

Slide 70

Slide 70

@font-face { font-display: optional; } RENDER WEB FONT ONLY IF CACHED @zachleat Learn more: https://font-display.glitch.me/

Slide 71

Slide 71

font-display Browser SUPPORT https://caniuse.com/#feat=css-font-rendering-controls

Slide 72

Slide 72

font-display Browser SUPPORT https://caniuse.com/#feat=css-font-rendering-controls

Slide 73

Slide 73

font-display: swap -ish @zachleat

Slide 74

Slide 74

WE NO LONGER NEED JavaScript to load a WEB FONT *WITH VISIBLE FALLBACK TEXT @zachleat

Slide 75

Slide 75

YYYYY SELF HOSTING WITH A BIT OF SHARDING USING PRECONNECT BASE LINE ▼ AND Font-DISPLAY 👀 New: Baseline: ▼ -3% 3.2s first render 3.3s 5.7s visually complete 5.7s

Slide 76

Slide 76

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION

Slide 77

Slide 77

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 78

Slide 78

YYYYY PRELOAD

Slide 79

Slide 79

<link rel="preload" href="notosans.woff2" as="font" type="font/woff2" crossorigin>

Slide 80

Slide 80

PRELOAD BROWSER SUPPORT https://caniuse.com/#feat=link-rel-preload

Slide 81

Slide 81

YYYYY SELF HOSTING WITH A BIT OF SHARDING USING PRECONNECT BASE LINE ▼ AND Font-DISPLAY AND PRELOAD 👀 ⏰ New: 3.3s ▲ 3% 5.9s Baseline: first render 3.3s visually complete 5.7s

Slide 82

Slide 82

PRELOADING fonts FROM A DIFFERENT ORIGIN INCURS CONNECTION COSTS AT A BAD TIME ⚠ @zachleat

Slide 83

Slide 83

YYYYY SELF HOSTING WITH A BIT OF SHARDING AND Font-DISPLAY AND PRELOAD BASE LINE ▼ 👀 🗑 New: Baseline: ▲ 15% 3.8s first render 3.3s ▼ -1% 5.6s visually complete 5.7s

Slide 84

Slide 84

OVERUSE OF PRELOAD WILL COST YOU in FIRST RENDER TIME ⚠ @zachleat

Slide 85

Slide 85

FIRST RENDER PRELOAD PRELOAD @zachleat PRELOAD

Slide 86

Slide 86

PRELOAD SOME, NOT ALL. HOW TO CHOOSE? @zachleat

Slide 87

Slide 87

LET’S GET… SUBJECTIVE

Slide 88

Slide 88

PRIORIZATION STRATEGY: USER DISRUPTION METRICS @zachleat

Slide 89

Slide 89

Y USER DISRUPTION Y Y Y Y PRIORIZATION YYYYY Y Y Y @zachleat Y

Slide 90

Slide 90

What reflows are most likely to BE THE MOST DISRUPTIVE? @zachleat

Slide 91

Slide 91

Y USER DISRUPTION Y Y Y Y PRIORIZATION YYYYY Y Y Y @zachleat Y

Slide 92

Slide 92

METRICS PRIORIZATION: PRELOAD ONE OF EACH FAMILY @zachleat YY YYY

Slide 93

Slide 93

Y METRICS PRIORIZATION: PRELOAD ONE OF EACH FAMILY Y Y YY YYY Y @zachleat ❤

Slide 94

Slide 94

ROMAN FALLBACK FAUX-BOLD FONT-SYNTHESIS ❤ BOLD 🏁 @zachleat

Slide 95

Slide 95

FONT-SYNTHESIS FALLBACK Y @zachleat 🚫 Y

Slide 96

Slide 96

DEAR WEB BROWSERS, CAN YOU COPY WEBKIT Please? NON-WEBKIT IT’S ME, MARGARET ❤ FONT-SYNTHESIS

Slide 97

Slide 97

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD & FONT-SYNTHESIS CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 98

Slide 98

MINI- REVIEW

Slide 99

Slide 99

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION

Slide 100

Slide 100

RE F RE LO FLO W W RE F R L E RE FL OW FLO OW W THE default Wordpress Site first render 3.3s visually complete 5.7s

Slide 101

Slide 101

PRELOAD EVERYTHING NO REFLOWS! FIRST 😭 RENDER 🗑 New: Baseline: ▲ 15% 3.8s first render 3.3s ▼ -1% 5.6s visually complete 5.7s

Slide 102

Slide 102

R RE EFL FLO OW W RE FLO W PRELOAD TWO FONTS ❤ FONT-SYNTHESIS New: 3.3s Baseline: first render 3.3s ▼ -1% 5.6s visually complete 5.7s

Slide 103

Slide 103

YYYYY CSS FONT LOADING API

Slide 104

Slide 104

LOAD A WEB FONT USING JS // Remove existing @font-face blocks // Make it let font = new FontFace( "Noto Serif", "url(notoserif.woff2) format('woff2')" ); // Load it let loadedFont = await font.load(); // Render it document.fonts.add(loadedFont); Full Code at https://github.com/zachleat/performance-sometime/blob/master/css-font-loading.js

Slide 105

Slide 105

LOAD TWO WEB FONTS USING JS // Remove existing @font-face blocks // Make two let font = new FontFace("Noto Serif", /* … /); let fontBold = new FontFace("Noto Serif", / … */); // Load two let fonts = await Promise.all([ font.load(), fontBold.load() ]); // Render them at the same time!!! fonts.forEach(font => document.fonts.add(font)); Full Code at https://github.com/zachleat/performance-sometime/blob/master/css-font-loading.js

Slide 106

Slide 106

NO CSS MAINTENANCE REQUIRED 🎉

Slide 107

Slide 107

YYYYY ON ER EF LO W SELF HOSTING AND Font-DISPLAY AND PRELOAD: 2 of 5 AND SHARDING: 3 of 5 WITH PRECONNECT AND CSS FONT LOADING: 3 of 5 New: 3.3s 5.7s Baseline: first render 3.3s visually complete 5.7s

Slide 108

Slide 108

ROMAN FALLBACK FAUX-BOLD FONT-SYNTHESIS BOLD 🏁 @zachleat

Slide 109

Slide 109

ROMAN FONT-SYNTHESIS FAUX-BOLD BOLD 🏁 @zachleat ❤

Slide 110

Slide 110

JavaScript FONTS CAN BE FANCY: NETWORK INFORMATION API Opt out of fonts on slow connections https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API Save-Data Opt-out of fonts when the user has enabled Data Saver mode. https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/save-data/ PREFERS-REDUCED-MOTION https://webkit.org/blog/7551/responsive-design-for-motion/#using-reduce-motion-on-the-web @zachleat Opt-out of font reflows when the user has enabled Reduce Motion in accessibility preferences.

Slide 111

Slide 111

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 112

Slide 112

YYYY Y VARIABLE FONTS

Slide 113

Slide 113

YY VARIABLE FONTS

Slide 114

Slide 114

https://www.axis-praxis.org/ https://www.preusstype.com

Slide 115

Slide 115

YYYY Y 15.3KB 15.4KB 22.9KB 20.9KB 26.4KB 100.9KB

Slide 116

Slide 116

Y Y 100.0KB CompressaPRO-GX.woff2 Read more about Variable Font size comparisons: http://stuff.djr.com/gimlet-vf-size-test/

Slide 117

Slide 117

YYYYY SELF HOSTING WITH A SINGLE 115KB VARIABLE FONT BASE LINE ▼ 👀 New: Baseline: first render 3.3s visually complete 5.7s

Slide 118

Slide 118

YYYYY SELF HOSTING WITH A SINGLE 115KB VARIABLE FONT 👀 New: Baseline: first render 3.3s visually complete 5.7s

Slide 119

Slide 119

YYYYY SELF HOSTING WITH A SINGLE 115KB VARIABLE FONT W LO EF ER ON New: Baseline: first render 3.3s visually complete 5.7s

Slide 120

Slide 120

v-fonts.com Region 1 ntitled 2 ntitled 5 ntitled 8 titled 11 titled 14 titled 17 titled 20 titled 23 0 75 150 225 69.3 KB mean 54.1 KB Median 300

Slide 121

Slide 121

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 122

Slide 122

YYYYY SELF HOSTING AND Font-DISPLAY AND PRELOAD: 2 of 5 AND SHARDING: 3 of 5 WITH PRECONNECT AND CSS FONT LOADING: 3 of 5 New: 3.3s 5.7s Baseline: first render 3.3s visually complete 5.7s

Slide 123

Slide 123

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 124

Slide 124

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 125

Slide 125

OUR TOOLS: WOFF2 PRECONNECT SELF HOSTING SHARDING 😱 FONT-DISPLAY PRELOAD CSS FONT LOADING API SUBSETTING VARIABLE FONTS

Slide 126

Slide 126

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION -ISH

Slide 127

Slide 127

All demos on GitHub https://github.com/zachleat/performance-sometime

Slide 128

Slide 128

A COMPREHENSIVE GUIDE TO FONT LOADING STRATEGIES zachleat.com/web/comprehensive-webfonts/ @zachleat 45 (and counting) font loading blog posts at zachleat.com/web/fonts/ Web Font Loading Recipes https://github.com/zachleat/web-font-loading-recipes

Slide 129

Slide 129

@zachleat INSPIRED BY

Slide 130

Slide 130

@zachleat THANK YOU @zachleat @zachleat.com \