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

WHYs THE 5 WHYs OF WEB WHYs LOADIN WHYs PERFOR WHYs

WHYs WHYs OF WEB FONT WHYs LOADING WHYs PERFORMANCE WHYs

@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

@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

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

THE DEFAULT Wordpress Site

THE DEFAULT Wordpress Site 595 KB 35 requests

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

Y SANS SERIF 15.3 KB

YY SANS SERIF BOLD 15.3 KB

YYY SERIF 22.9 KB

YYYY 20.8 KB SERIF ITALIC

YYYY Y 26.2 KB SERIF BOLD

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

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

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

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION

OUR OPPONENTS: INVISIBLE TEXT FOIT TEXT IN MOTION

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

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION FOUT

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

FIRST RENDER FIRST CONTENTFUL PAINT

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

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

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

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

REFLOW COUNTER 0 @zachleat

REFLOW COUNTER 10 @zachleat

REFLOW COUNTER 21 @zachleat

REFLOW COUNTER 32 @zachleat

REFLOW COUNTER 43 @zachleat

REFLOW COUNTER 54 @zachleat

REFLOW COUNTER 65 @zachleat

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

OUR METRICS: ALL TEXT VISIBLE WEB FONT REFLOW COUNT

YYYYY

YYYYY “WEB-SAFE”/SYSTEM/INSTALLED FONTS

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

YYYYY System fonts No network requests INSTANT RENDERING @zachleat

@zachleat THANK YOU @zachleat @zachleat.com

@zachleat

@zachleat

BEST VIEWED @zachleat IN CHROME

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

@zachleat

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

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

YYYYY PRECONNECT

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

<!-- 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'>

PRECONNECT 🎉 WITHOUT CSS WITH FONTS @zachleat

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

PRECONNECT: GREAT FOR CROSS-ORIGIN FONT REQUESTS @zachleat

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

YYYYY THIRD PARTY ▸ SELF HOSTING

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

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

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

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

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

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

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

WHY SELF HOST? @zachleat

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

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

YYYYY FONT-DISPLAY

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

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

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

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

font-display: swap -ish @zachleat

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

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

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION

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

YYYYY PRELOAD

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

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

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

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

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

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

FIRST RENDER PRELOAD PRELOAD @zachleat PRELOAD

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

LET’S GET… SUBJECTIVE

PRIORIZATION STRATEGY: USER DISRUPTION METRICS @zachleat

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

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

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

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

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

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

FONT-SYNTHESIS FALLBACK Y @zachleat 🚫 Y

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

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

MINI- REVIEW

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION

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

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

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

YYYYY CSS FONT LOADING API

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

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

NO CSS MAINTENANCE REQUIRED 🎉

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

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

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

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.

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

YYYY Y VARIABLE FONTS

YY VARIABLE FONTS

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

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

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

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

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

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

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

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

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

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

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

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

OUR OPPONENTS: INVISIBLE TEXT TEXT IN MOTION -ISH

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

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

@zachleat INSPIRED BY

@zachleat THANK YOU @zachleat @zachleat.com \