📜 ⬆️ ⬇️

Performance frontend. Part 3 - font optimization

From the translator: This is the eighth article from the Node.js series from the Mozilla Identity team, which deals with the Persona project.





We were able to reduce the amount of fonts for Persona by 85%, from 300 to 45 kilobytes, using subsets of fonts. This article tells you exactly how we did it and what tools we used.
')

Introducing connect-fonts


Connect-fonts is a middleware for Connect that improves @font-face performance by distributing subsets of fonts specifically chosen for their language, thereby reducing their size. Connect-fonts also generates locale and browser-specific @font-face and CORS headers for Firefox and IE9 +. To distribute subsets of fonts, so-called font packs are created - subdirectories with subsets of fonts plus a simple JSON configuration file. Some sets of common open source fonts are available in a ready-made form in the npm package , however, it is not difficult to create your own packages.

If you are not too well versed in working with fonts on the Internet, we have collected a small collection of links on the subject of @font-face . [ From the translator: and on Habré, an article on the performance of web fonts is very useful ]

Static and dynamic font loading


When you just give one big font file to all users, it's pretty simple:

  1. write the @font-face block in your CSS file;
  2. generate a font file for the web from the source in TTF or OTF and put it in a directory to which the web server has access;
  3. configure the CORS headers if you refer to fonts from another domain, since the policy of the same Firefox and IE9 + source also applies to fonts.

These are simple steps. The first two can be made even easier with, for example, FontSquirrel , an online font generator that automatically creates font files and CSS code. To add CORS headers you will have to read the Apache or Nginx documentation, but this is not too difficult.

But if you want to use all the advantages of subsets of fonts, everything becomes more complicated. You will need separate font files for each locale, and you will need to dynamically change the @font-face declarations to point to the correct URLs. You will also have to deal with CORS. These problems are solved by connect-fonts.

Font subsets: an overview


By default, each font contains a variety of characters - Latin, accented characters for languages ​​such as French or German, additional alphabets such as Greek or Cyrillic. Many fonts also contain characters, especially if they support Unicode (for example, the snowman symbol - ). There are fonts with support for hieroglyphs. All this is contained in the font file so that it is useful to the widest possible audience. This flexibility leads to large file sizes. Microsoft Arial Unicode, which contains all Uncode 2.1 characters, weighs an incredible 22 megabytes!

At the same time, a typical web page uses a font for one specific task - displaying content, usually in the same language and without exotic characters. By limiting the font to this small subset, we can save a lot.

Performance gains when using subsets of fonts


Let's compare the size of full files of common fonts with subsets for several locales. Even if your site works only in English, you can save a lot by producing a localized subset.

Smaller font file size means faster loading and appearance of fonts on the screen. This is especially important for mobile devices. If the user happens to enter the site via a 2G connection, reducing the weight of the page by 50 kilobytes can speed up the download by 2-3 seconds. One more thing - the cache in mobile devices is often small, and a small font has more chances to stay in it.

Comparing the size of the full Open Sans Regular font with subsets for different locales:


The same in gzip compressed form:


Even with compression, you can reduce the font size by 80%, from 63 to 13 kilobytes, in the case of using only the English subset of Open Sans. And this is only one font - most sites use several. Huge potential for optimization!

Using connect-fonts, we in Mozilla Persona achieved a reduction of 85%, from 300 to 45 kilobytes. This is equivalent to a couple of seconds of download time on a typical 3G connection and ten seconds per 2G.

Further optimizations


If you want to get rid of every extra byte, you can configure connect-fonts to return CSS not as a file, but as a string that can be included in a common file. In addition, connect-fonts by default generates a minimal @font-face declaration, not including files in formats that a particular browser does not accept.

An example of using connect-fonts in an application


Suppose that we have a simple Express application that tells the client the current time:

 // app.js const ejs = require('ejs'), express = require('express'), fs = require('fs'); var app = express.createServer(), tpl = fs.readFileSync(__dirname, '/tpl.ejs', 'utf8'); app.get('/time', function(req, res) { var output = ejs.render(tpl, { currentTime: new Date() }); res.send(output); }); app.listen(8765, '127.0.0.1'); 

using the most primitive pattern:

 // tpl.ejs <!doctype html> <p>the time is <%= currentTime %> 

We connect connect-fonts to render a localized subset of Open Sans - one of several ready-made sets included in packages.

Changes in the application


First, install the necessary packages:

  $ npm install connect-fonts $ npm install connect-fonts-opensans 

Connect middleware:

  // app.js     connect-fonts const ejs = require('ejs'), express = require('express'), fs = require('fs'), // : connect_fonts = require('connect-fonts'), opensans = require('connect-fonts-opensans'); var app = express.createServer(), tpl = fs.readFileSync(__dirname, '/tpl.ejs', 'utf8'); 

Initialize the module:

 //  app.js //    app.use: app.use(connect_fonts.setup({ fonts: [opensans], allow_origin: 'http://localhost:8765' }) 

connect_fonts.setup() takes the following arguments:


Inside the route you need to transfer the locale to the template:

  //  app.js app.get('/time', function(req, res) { var output = ejs.render(tpl, { //    : userLocale: detectLocale(req), currentTime: new Date() }); res.send(output); }); 

Mozilla Persona uses i18n-abide to determine the locale. There is also a very good locale module, both of which are available via npm. But in order not to complicate an example, we simply take the first two characters from the Accept-language request field:

  //    function detectLocale(req) { return req.headers['accept-language'].slice(0,2); } app.listen(8765, '127.0.0.1'); //  app.js 

Pattern Changes


Now you need to update the template. Connect-fonts assumes that routes are written as:

 /:locale/:font-list/fonts.css 

eg:

 /fr/opensans-regular,opensans-italics/fonts.css 

In our case, we need to add a link to the style file along the path that connect-fonts expects:

 // tpl.ejs -   connect-fonts <!doctype html> <link href="/<%= userLocale %>/opensans-regular/fonts.css" rel="stylesheet"> 

and add a style to the page code to use the font:

  //  tpl.ejs <style> body { font-family: "Open Sans", "sans-serif"; } </style> <p>the time is <%= currentTime %> 

That's all. The CSS created by connect-fonts depends on the user's locale and browser. Here is an example for the English locale:

  *      ua   'all' */ @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 400; src: url('/fonts/en/opensans-regular.eot'); src: local('Open Sans'), local('OpenSans'), url('/fonts/en/opensans-regular.eot#') format('embedded-opentype'), url('/fonts/en/opensans-regular.woff') format('woff'), url('/fonts/en/opensans-regular.ttf') format('truetype'), url('/fonts/en/opensans-regular.svg#Open Sans') format('svg'); } 

If you want to save a single HTTP request, you can use the connect_fonts.generate_css() method to get the CSS code as a string and include it in the common CSS file.

Now our little application beautifully shows time. Its full source code is available on Github. We used ready-made font pack, but creating your own is not at all difficult. The instruction is in the readme.

Shutdown


Subsets can give a very big performance gain for sites that use web fonts. connect-fonts does a lot of work with fonts in a multilingual application. Even if your site only supports one language, you can still use this module to trim fonts to the only locale you need and automatically generate CSS and CORS headers, plus this will facilitate subsequent internationalization.

Further optimizations may consist in excluding hinting for platforms that do not support it (all but Windows), automatic font compression and setting headers for caching.



Source: https://habr.com/ru/post/197370/


All Articles