Custom fonts with no cramp

26 November 2013

A developer using custom fonts in web (don't say this is evil) usually face a dilemma.

The users' main interest is the content of the page not the view. It is nice to provide it as fast as possible and then apply a custom font. This blog used to work this way. I provided a script among others in the bundle linked by the end of the page.

(function (d) {
  function addFont(url, h, l) {
    h = d.getElementsByTagName("head")[0];
    l = d.createElement("link");
    l.href = url;
    l.rel = "stylesheet";
    h.appendChild(l);
  }

  addFont(
    "//fonts.googleapis.com/css?" +
      "family=Noto+Serif&subset=latin,cyrillic-ext,cyrillic"
  );
})(document);

The drawback is obvious. Once a font is loaded and apllied, a user notice twitching as a page is being re-rendering. It is recommended to define a suitable system font in the font-face. But there never is a good fit. Otherwise there was no need in a custom font.

The only way to avoid the font rendering jerk is to provide the font in advance. With inlining a base64 of a font into CSS and linking it in <head> no twitching is guaranteed as well as increasing page loading time.

For a while I was deciding between the two variants until my colleague Roman Komarov aka kizu suggested an elegant solution which is in use right now.

At the first time a user opens any page of this site, a CSS with the font is loaded and stored as a piece of text in the localStorage.

$(function () {
  if (typeof Storage === "undefined") {
    return;
  }
  if (localStorage.getItem("varya.me.fonts") === null) {
    $.ajax({
      url: "../../data/fonts.css",
      success: function (response) {
        localStorage.setItem("varya.me.fonts", response);
      },
      dataType: "text",
    });
  }
});

But I do not apply this CSS after loading. So, not jerks. As request is only to fill up the storage, it goes after the domReady.

By the time of requesting the next page the custom font is already in the user's computer. Checking that it is available I apply it to the page. The faster the better, and as the action costs almost nothing an inline <head> script goes here.

(function (d, s, l, r) {
  if (typeof s !== "undefined" && l.getItem("varya.me.fonts")) {
    r = ["<style>", l.getItem("varya.me.fonts"), "</style>"];
    document.write(r.join(""));
  }
})(document, Storage, localStorage);

With that the blog looks as it should do with 2+ pages loaded. I consider this price for no-twitching custom font as reasonable.

Any ideas of making it even better?

You can hire me and the whole Bridge-the-Gap team to set up, manage, develop, and champion your design system. I can align the design and development processes in your organisation for a larger business impact.

© Varya Stepanova 2024