Skip to content

Instantly share code, notes, and snippets.

@MarcoHengstenberg
Created March 23, 2016 16:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MarcoHengstenberg/e49f53f68668a8a9c20f to your computer and use it in GitHub Desktop.
Save MarcoHengstenberg/e49f53f68668a8a9c20f to your computer and use it in GitHub Desktop.
Something about loading webfonts in different browsers and issues I encountered
So here's the story:
I have fontfiles locally (which should be considered the ideal thing - no depencies to external font-
or CSS-files). I want them to load as fast as possible and have a "Flash of Fallback Text" on
firstView with no Flash-whatsoever on repeat views.
Currently, in the Magazine, we're using this localStorage-base64encoded-fontfiles pick-pocket trick
in order to have exactly that functionality. We'd still be fine if you had a flash when returning
after your session expired, so sessionStorage is an option:
- flash on first view
- no flashes on subsequent visits
- returning after session expired -> flash
I would like to use `<link rel="preload" />` in order to mimic something like that using the browser cache
and no storage at all - because no JS dependency. With loadCSS as a polyfill for browsers which don't
understand preload yet, this goal is in reach already - or at least I sense it near but can't grab it.
Here the markup I got in my `<head>`:
<!doctype html>
<html lang="en-us"><head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<title>Fontloading with fontfiles - Tested</title>
<style>main{display:block;}html{text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga","kern";background:#e5e5e5;}body{font-family:Verdana,sans-serif;font-size:1.125em;font-weight:400;font-style:normal;line-height:1.55;margin:0 auto;padding:1.618em;max-width:630px;color:#353738;background:#fafaf8;}header{margin-bottom:2em;border-bottom:.08em solid #e53b2c;}header a,header a:hover,header a:focus,header a:active{border:none;background:none;}header img{max-width:229px;}header h1{font-size:1.75em;}
/* Font Setting Fallbacks */
.pnr {font-family:Verdana,sans-serif;}.pnb{font-family:Verdana,sans-serif;font-weight:700;}.pnr{font-family:Verdana,sans-serif;font-style:italic;}.sr{font-family:Georgia,serif;}.sb{font-family:Georgia,serif;font-weight:700;}.si{font-family:Georgia,serif;font-style:italic;}</style>
<link rel="preload" href="/css/page.css" as="style" />
<link rel="preload" href="/fontfiles/Proxima-Nova-Regular.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fontfiles/Proxima-Nova-Bold.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fontfiles/Proxima-Nova-Italic.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fontfiles/Skolar-Regular.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fontfiles/Skolar-Bold.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fontfiles/Skolar-Regular-Italic.woff2" as="font" type="font/woff2" crossorigin />
<!--[if IE]><link href="favicon.ico" rel="icon" /><![endif]-->
<link href="img/favicon.png" rel="icon" />
<noscript><link rel="stylesheet" media="all" href="css/page.css" />
</head>
Then, I'm loading the stylesheet which has the references to the webfonts asynchronously with a dumbed
down script (before `</body>`, which I'll replace with loadCSS as the next step but for now, it's cool like that:
<script>
var ps = document.createElement('link');
ps.type = 'text/css';
ps.rel = 'stylesheet';
ps.href = 'css/page.css';
ps.media = 'all';
document.getElementsByTagName("head")[0].appendChild(ps);
</script>
In Firefox (not supporting preload atm) it works. On first view I get a FOUT and on reloading the page
all fonts and the CSS are requested from the cache -> no Flash.
In Chrome, no matter the technique I ALWAYS ALWAYS ALWAYS have a FOIT (INVISIBLE TEXT IS THE WORST!)
and no matter what I do (FontFaceObserver, preload, subresource, sessionStorage) I always have that
experience.
The only way around this, working in browsers supporting the feature, is to prerender the "next" page (in this case the same page) after
the load event bcs then – tadaaa – no Flash. I do this by using another dumbed down script right
after the stylesheet-loading script:
<script>
window.onload=function(){
var hint = document.createElement("link");
hint.setAttribute("rel", "prerender");
hint.setAttribute("href", "index.html");
document.getElementsByTagName("head")[0].appendChild(hint);
}
</script>
Of course that is completely not maintainable if you got more than two pages you'd have to prerender - so for
the magazine that's a no-go.
Thoughts?
I hope at least half of my ramblings here make sense.
I tried polyfilling preload with loadCSS -> flash
I tried subresource instead of preload -> flash
I tried to ask my Mom -> ...
@zachleat
Copy link

@MarcoHengstenberg What do the font-face blocks look like?

@MarcoHengstenberg
Copy link
Author

@font-face {
font-family: 'Proxima Nova Regular';
src:
url('../fontfiles/Proxima-Nova-Regular.woff2') format('woff2'),
url('../fontfiles/Proxima-Nova-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Proxima Nova Bold';
src:
url('../fontfiles/Proxima-Nova-Bold.woff2') format('woff2'),
url('../fontfiles/Proxima-Nova-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Proxima Nova Italic';
src:
url('../fontfiles/Proxima-Nova-Italic.woff2') format('woff2'),
url('../fontfiles/Proxima-Nova-Italic.woff') format('woff');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: "Skolar Regular";
src:
url('../fontfiles/Skolar-Regular.woff2') format('woff2'),
url('../fontfiles/Skolar-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "Skolar Bold";
src:
url('../fontfiles/Skolar-Bold.woff2') format('woff2'),
url('../fontfiles/Skolar-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: "Skolar Italic";
src:
url('../fontfiles/Skolar-Regular-Italic.woff2') format('woff2'),
url('../fontfiles/Skolar-Regular-Italic.woff') format('woff');
font-weight: 400;
font-style: italic;
}

@jaicab
Copy link

jaicab commented Mar 23, 2016

Correct me if I'm wrong, but I think you don't need the page.css loader before </body>. You have some inline CSS for first render, so the link preload for page.css is the way to go to complete those styles. Adding a second loader (either with your script or loadCSS) is redundant.

@MarcoHengstenberg
Copy link
Author

It's actually not. preload is not enough.
Without a reference to the stylesheet somewhere, as in <link rel="stylesheet" media="all" href="css/page.css" />, there will be no CSS at all.

Same with the fontfiles themselves.

Edit: but of course I also tried simply going the oldschool way of using <link rel="stylesheet" ... /> in the head with no script or anything but that blocks rendering -_-

@zachleat
Copy link

preload doesn’t guarantee that the requests will finish before initial render, only that they’ll be kicked off sooner in the waterfall.

So preload is nice but doesn’t kill FOIT. There are a bunch of different scenarios you can do here: I go through a bunch of them here: http://www.zachleat.com/web/critical-webfonts/ My newest fancy font loading strategy is documented at the end of: http://www.zachleat.com/web/web-font-data-uris/

@zachleat
Copy link

Using preload with page.css has a similar problem I think. It will still block if the request hasn’t completed when the <link rel="stylesheet"> is encountered.

@MarcoHengstenberg
Copy link
Author

base64 encoding goes completely against the grain as we're on an H2 enabled server.

BUT! Inlining the @font-face rules might be a good idea here whether or not they are base64 encoded because then noone would have to wait for the stylesheet being requested asynchronously...
the only remaining issue still is: why does Chrome have issues with its cache. When Firefox has the font-files and the stylesheet in its cache, there are no flashes anymore... in Chrome I have the invisible text nonetheless.

@zachleat
Copy link

I think debugging that will likely require a reduced test case or live debugging.

@MarcoHengstenberg
Copy link
Author

I got a testcase (really dumbed down but still needs work) going. I just ran out of ideas on how I could go further – hence I was reaching out on Twitter.

I could give you the link and HTTP-Auth credentials (Twitter-DM?!) but that's a lot to ask thinking of the time that you'd have to invest, so pretend I didn't ask in the first place. =D

@MarcoHengstenberg
Copy link
Author

Alright, Github ate one of my comments just now... what I wrote was: great work on researching Webfonts and ways to load them efficiently Zach! I'm trying to follow your posts as closely as possible, so I read the one about critical Fonts already but that new one... wow, those numbers speak a clear language.

@MarcoHengstenberg
Copy link
Author

Family matters are calling. Going to return to this Gist tomorrow. =)

@jaicab
Copy link

jaicab commented Mar 23, 2016

I agree with Zach, and more importantly preload can't assure you the fonts are fully loaded when you load the styles, you can only do that with FFO nowadays.
I reproduced the case here just out of curiosity. And you're right, the CSS file is not triggered on Chrome, but it is whenever the polyfill kicks in. This can be achieved in the native implementation using onload="this.rel='stylesheet'" on your preload.

@MarcoHengstenberg
Copy link
Author

Thanks to both of you. =)
Many many thanks for taking the time to help me clear things up here.

So, in the end there is no way to ensure there's no FOIT on subsequent visits apart from using localStorage in combination with data URIs or the technique Zach wrote about in his article. Well... I'll go from there then and see what I can do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment