WHYsTHE 5 WHYs OF WEB
WHYs LOADIN
WHYs PERFOR
WHYsWHYs
WHYs OF WEB FONT
WHYs LOADING
WHYs PERFORMANCE
WHYs

@zachleat@zachleat.com@Web Craftsperson

@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

WHYWHYWHYWHYYWHY
WHYWHYWHYWHYWHYWHY
WHYWHYYWHYWHYWHYWHYWHYWHYWHYWHYWHYYWHY
WHYWHYWHYWHYWHYWHYWHYWHYYWHYWHYWHYWHYWHYWHYWHYWHYWHYYWHYWHYWHYWHYWHYYTHE 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 INDEXONLOADTIMETOINTERACTIVE
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
COUNTER43@zachleat

REFLOW
COUNTER54@zachleat

REFLOW
COUNTER

65@zachleat

REF
RE LO
FLO W
W
REFRL
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.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: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 !
WITHOUTCSS

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 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');}

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-PATTERNTREASURE” ”
ANTI-PATTERN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@zachleat

WE NO LONGER NEEDJavaScript
to load a WEB FONT!

WE NO LONGER NEEDJavaScript
to load a WEB FONT*WITH VISIBLE FALLBACK TEXT

@zachleat

YYYYYSELF HOSTING

WITH A BIT OF SHARDING
USING PRECONNECTBASELINE▼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

WHY SELF HOST?
@zachleat<link href="https://fonts.googleapis.com/css?
family=Lobster&display=swap"
rel="stylesheet">https://www.zachleat.com/web/google-fonts-display/"

GOOGLE FONTS CDN
WITH Font-DISPLAY

SELF HOSTING

WITH A BIT OF SHARDING
USING PRECONNECT

AND Font-DISPLAY"

https://csswizardry.com/2019/05/self-host-your-static-assets/@zachleat

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 PRECONNECTBASELINE▼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 HOSTINGWITH A BIT OF SHARDING
AND Font-DISPLAY
AND PRELOADBASE
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

PRELOAD

PRELOADFIRSTRENDER@zachleat

FIRST
RENDERPRELOADPRELOADPRELOAD@zachleatFIRST
RENDERPRELOADPRELOAD@zachleat

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

LET’S GET…

SUBJECTIVEPRIORIZATION STRATEGY:USER DISRUPTIONMETRICS@zachleat

Y

USER DISRUPTIONYYYYPRIORIZATIONYYYYYYYY@zachleat

YWhat reflows are mostlikely to BE THE MOSTDISRUPTIVE?
@zachleat

Y

USER DISRUPTIONYYYYPRIORIZATIONYYYYYYYY@zachleat

YMETRICSPRIORIZATION: PRELOAD ONE OF EACH FAMILY@zachleat

YY YYYYMETRICS

PRIORIZATION:PRELOAD ONE OFEACH FAMILYYYYY YYYY@zachleat❤

ROMANFALLBACKFAUX-BOLD

FONT-SYNTHESIS❤

BOLD (@zachleat

FONT-SYNTHESISFALLBACKY@zachleat)Y@zachleat)

YY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

REF
RE LO
FLO WW
REFRLERE FL OWFLO OWWTHE 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

RRE EFLFLO OWWREFLO
W

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 CHANGES TO CSSREQUIRED !

YYYYYONEREFLOWSELF
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 APISUBSETTINGVARIABLE 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

YY

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 FONTBASE
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 FONTWLOEFERON

New:

Baseline:

first render 3.3s

visually complete 5.7s

v-fonts.com

Region 1
ntitled 2
ntitled 5
ntitled 8
titled 11titled 14titled 17titled 20
titled 23075150

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 APISUBSETTINGVARIABLE FONTS

OUR OPPONENTS:
INVISIBLE TEXTTEXT IN MOTION-ISH

All demos on GitHub

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

@zachleat

A COMPREHENSIVE GUIDE TO

FONT LOADINGSTRATEGIES
zachleat.com/web/comprehensive-webfonts/
55 (and counting) font loading 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