Skip to content

Instantly share code, notes, and snippets.

@evancz
Last active March 14, 2019 09:07
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save evancz/e243e9cc824f7b3f23e79fb3fb590e81 to your computer and use it in GitHub Desktop.
Save evancz/e243e9cc824f7b3f23e79fb3fb590e81 to your computer and use it in GitHub Desktop.
It seems really complicated to use custom fonts well. This explores some ideas of how to make it easier.

Making it easier to load fonts

I have been doing a lot of work on making Elm assets really tiny. As part of some exploratory research I did ages ago, I read this document on font loading. It is a super helpful resource, but I was confused by all the different terms: FOIT, FOUT, FOFT, etc. It reminded me of the old "how do you center things?" blog posts from before flexbox. So my instinct was that probably lots of folks are confused about how to do it well, and maybe there is a better way!

So brainstormed some some ideas to:

  1. Save even more bits.
  2. Have an ideal visual experience.
  3. Be really easy to set up.

I did this brainstorm maybe six months ago, so at this point, it is very clear that I cannot do it myself! So here they are, and I hope they inspire someone to get us to a better font world!

Idea #1

What if you could specify a dummy version of fonts that was a much smaller file?

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  src: url(fonts/roboto.woff2);
  src-placeholder: url(fonts/roboto-tiny.woff2);
}

Notice the additional src-placeholder descriptor. The idea is that fonts/roboto-tiny.woff2 would be a tiny subset of the full font. Maybe it only has width, kerning, and ligature information, but no actual curves for the letters? Or maybe it only has the Latin1 characters? Or maybe some other trick. The point is that it'd be small enough to load quickly and let folks see things properly laid out.

But if you cut too much from roboto-tiny.woff2 then it may cause things to move around when the real thing arrives.

Idea #2

What if you could specify a placeholder that (1) allows you to show text really soon and (2) does not cause any reflows or text movement?

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  placeholder: url(fonts/roboto.widths) local(arial);
  src: url(fonts/roboto.woff2);
}

Notice the additional placeholder descriptor. The idea is that:

  1. fonts/roboto.widths would only have the width, kerning, and ligature information.
  2. local(arial) would specify system fonts similar to what you will ultimately load.

So the browser now renders things with arial characters, but it does the layout based on roboto.widths instead. So things will look a bit goofy, but when the real font loads, nothing will move. The letters will just be better.

Note 1: I am assuming you would put fonts/roboto.widths in a <link preload> tag in the header. Or use data URLs. Whatever path you take, the main idea is that this file is the smallest possible thing that allows proper layout.

Note 2: Maybe the .widths file can be a .woff2 instead? In case someone wants to put Latin1 in there as well as the widths? I do not know enough about the file format to know the tradeoffs here.

But what if you have many @font-face rules, and you only want the text to change once, in one big batch?

Idea #3

What if you could synchronize the display of src typefaces?

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  placeholder: url(fonts/roboto.widths) local(arial);
  src: url(fonts/roboto.woff2) sync(id);
}

Notice that the src descriptor now has lits sync(id).

Say you have four @font-face rules that all specify sync(my-app-body). The idea is that they will all stay on placeholder until all four my-app-body fonts are loaded.

Note: I do not know if people would want this in practice. Perhaps it would need to be more nuanced and allow timeouts as well. Like sync(id, 1000) so you have a 1000 millisecond buffer where it waits for any other font with the same id. I do not know. My point is only that there seems to be a path to adding reasonable synchronization behavior if it is required.

Summary

It seems like idea #2 would make font loading a lot simpler for people. Just add a placeholder, and be sure to put the placeholder in a <link preload>.

It would be pretty easy to make tools that transform roboto.woff2 into roboto.widths files, so supporting tools could help folks get on the right path as well. E.g. if you are into servers that set everything up for you, it could do it all automatically.

Finally, I would not personally put idea #3 in a first draft of anything. It is just to show that there exists a way to address that concern if necessary.

@jweir
Copy link

jweir commented Jan 23, 2018

What if the font files first N bytes contained the layout data you seek instead of a place holder file? As soon as the browser has received those bytes it layouts. One file with a progressive streaming protocol.

Remember progressive jpegs?

@markmiro
Copy link

markmiro commented Feb 9, 2018

These are pretty brilliant ideas. Another idea:

  1. On first page load, download the fonts but don't display them.
  2. On subsequent page loads delay displaying content until you get the font.

The main downside of my approach is that a website might look dramatically different between page loads.

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