@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 FONTY LOADINGY PERFORMANCEY

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

YYSANS SERIF BOLD 15.3 KBYYYSERIF 22.9 KB

YYYY
20.8 KBSERIF ITALICYYYY Y26.2 KBSERIF BOLD

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

https://www.webpagetest.org/

METHODOLOGY
3G Network Speed
HTTP/2 and HTTPSCHROME & WOFF2ONLY FONT OPTIMIZATIONS@zachleatTHE default
Wordpress Site

SERIF
SANS-SERIF BOLDSERIF BOLDSERIF ITALICSANS-SERIF
first render 3.3s

visually complete 5.7s

OUR OPPONENTS:
INVISIBLE TEXTTEXT IN MOTIONOUR OPPONENTS:
INVISIBLE TEXT FOITTEXT IN MOTION

@zachleat

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

OUR OPPONENTS:
INVISIBLE TEXTTEXT IN MOTION FOUT

CRITICAL REQUEST CHAINSDOMCONTENTLOADEDFIRST CONTENTFUL PAINT
FIRST PAINTPERFORMANCE METRICS

FIRST RENDER

VISUALLY COMPLETE
SPEED INDEXONLOADTIME
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 TEXTTEXT IN MOTIONNEW METRIC:

ALL TEXT VISIBLE

THE default
Wordpress Site
ALL TEXT VISIBLE

first render 3.3s

👀

4.9s

visually complete 5.7s

OPPONENTS:
INVISIBLE TEXTTEXT IN MOTIONNEW METRIC:WEB FONT REFLOW COUNT* After first render

REFLOW
COUNTER0@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 OWFLO OWW

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.1svisually 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:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

YYYYYPRECONNECT

<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:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

YYYYYTHIRD PARTY ▸ SELF HOSTINGYYYYYSELF 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’SNETWORK CONGESTIONBAD 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');}

YYYYYSELF HOSTING
SELF
HOST
▼

👀

Self Hosting:Baseline:

▼ -6%

3.1s

first render 3.3s

▼ -1%

5.6s

visually complete 5.7s

YYYYYSELF 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-PATTERNON 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:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

YYYYYFONT-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 NEEDJavaScript
to load a WEB FONT*WITH VISIBLE FALLBACK TEXT

@zachleat

YYYYYSELF 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 TEXTTEXT IN MOTIONOUR TOOLS:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

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

PRELOAD BROWSER SUPPORT

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

YYYYYSELF 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 INCURSCONNECTION COSTS AT A
BAD TIME ⚠
@zachleat

YYYYYSELF 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 WILLCOST YOU in FIRST RENDER
TIME ⚠
@zachleatFIRST
RENDERPRELOADPRELOAD@zachleat

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

LET’S GET…

SUBJECTIVEPRIORIZATION STRATEGY:USER DISRUPTIONMETRICS@zachleat

Y

USER DISRUPTION

Y

Y
Y
Y

PRIORIZATION

YYYYY

Y
Y
Y

@zachleat

YWhat reflows are most
likely to BE THE MOSTDISRUPTIVE?
@zachleat

Y

USER DISRUPTION

Y

Y
Y
Y

PRIORIZATION

YYYYY

Y
Y
Y

@zachleatYMETRICSPRIORIZATION: PRELOAD ONE OF EACH FAMILY@zachleatYY YYYYMETRICSPRIORIZATION:
PRELOAD ONE OF
EACH FAMILYYYYY YYYY@zachleat❤

ROMANFALLBACKFAUX-BOLD

FONT-SYNTHESIS❤

BOLD 🏁
@zachleat

FONT-SYNTHESISFALLBACKY@zachleat🚫

YDEAR WEB BROWSERS,CAN YOU COPY WEBKIT Please?
NON-WEBKITIT’S ME, MARGARET❤

FONT-SYNTHESISOUR TOOLS:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOAD & FONT-SYNTHESISCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

MINI-

REVIEW

OUR OPPONENTS:
INVISIBLE TEXTTEXT IN MOTION

RE
F
RE LO
FLO W
W
RE
F
R
L
E
RE FL OWFLO OWW

THE default
Wordpress Site

first render 3.3s

visually complete 5.7s

PRELOADEVERYTHINGNO REFLOWS!

FIRST
😭 RENDER🗑
New:

Baseline:▲ 15% 3.8s
first render 3.3s

▼ -1%

5.6s

visually complete 5.7s

R
RE EFLFLO OWW
RE
FLOW

PRELOADTWO FONTS❤
FONT-SYNTHESIS

New:

3.3s

Baseline:

first render 3.3s

▼ -1% 5.6s
visually complete 5.7s

YYYYYCSS 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 MAINTENANCEREQUIRED 🎉

YYYYY

ON
ER
EF
LO
W

SELF
HOSTINGAND 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

ROMANFALLBACKFAUX-BOLD

FONT-SYNTHESISBOLD 🏁
@zachleat

ROMAN

FONT-SYNTHESISFAUX-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 inaccessibility preferences.

OUR TOOLS:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

YYYY YVARIABLE 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://stuﬀ.djr.com/gimlet-vf-size-test/

YYYYYSELF HOSTING
WITH A SINGLE
115KB VARIABLE FONT

BASE
LINE
▼

👀

New:

Baseline:

first render 3.3s

visually complete 5.7s

YYYYYSELF HOSTING
WITH A SINGLE
115KB VARIABLE FONT

👀

New:

Baseline:

first render 3.3s

visually complete 5.7s

YYYYYSELF 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 Median300

OUR TOOLS:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

YYYYYSELFHOSTINGAND 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:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

OUR TOOLS:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

OUR TOOLS:WOFF2PRECONNECT
SELF HOSTINGSHARDING 😱

FONT-DISPLAYPRELOADCSS FONT LOADING API
SUBSETTINGVARIABLE FONTS

OUR OPPONENTS:
INVISIBLE TEXTTEXT IN MOTION-ISHAll demos on GitHub

https://github.com/zachleat/performance-sometime

A COMPREHENSIVE GUIDE TO

FONT LOADINGSTRATEGIES
zachleat.com/web/comprehensive-webfonts/@zachleat

45 (and counting) font loading blog posts atzachleat.com/web/fonts/
Web Font Loading Recipes

https://github.com/zachleat/web-font-loading-recipes

@zachleat

INSPIRED BY

@zachleat

THANK YOU@zachleat@zachleat.com\