Read more

Minify Font Awesome fonts with webpack

Tobias Kraze
September 20, 2018Software engineer at makandra GmbH

Font Awesome 5 Show archive.org snapshot is a comprehensive solution for vector icons on your website.

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

Originally, Font Awesome came as an icon font (plus stylesheets), but recently it can also be used as a pure JavaScript solution (which will render icons as inline <svg> tags), or even as SVG sprites.

All solutions have their pros and cons:

Icon font:

  • little CPU load (no JavaScript)
  • fonts are relatively large
  • 1 extra HTTP request

Javascript + inline SVG:

SVG sprites:

  • largest file size, hard to subset
  • 1 extra HTTP request

In practice there are probably additional performance tradeoffs.

Luckily, there is a webpack plugin Show archive.org snapshot that will subset an icon font "on the fly" to remove all unused glyphs. However, there is (currently) on major caveat: When subsetting the font, the (digest) hash of the compiled font will not change. That means that if you add a new icon, browsers will not automatically reload the expanded font. There is a workaround at the bottom, but it is not very pretty.

In my opinion using this plus the icon font you get the best of all worlds, with low CPU usage, no JavaScript, and file-sizes in the one-digit kB range.

You can set it up like this:

Add npm modules

  • add @fortawesome/fontawesome-free
  • add fontmin-webpack. For webpack 3, use version ^1.0.2.

Load the fontmin plugin

Add the plugin in your webpacker config (example for webpacker Show archive.org snapshot )

const FontminPlugin = require('fontmin-webpack')

environment.plugins.append(
  'Fontmin', new FontminPlugin({
    autodetect: true,
  })
)

Only include the icons you need

Fontmin-webpacker works by only keeping glyphs it sees in your CSS content attributes. To make this work, you need to selectively only add the icons you require in your CSS.

Create and require a file font-awesome.scss with the following content:

$fa-font-path: '~@fortawesome/fontawesome-free/webfonts';

@import '~@fortawesome/fontawesome-free/scss/variables';
@import '~@fortawesome/fontawesome-free/scss/mixins';
@import '~@fortawesome/fontawesome-free/scss/core';

// you can pick and choose the features you actually need:
@import '~@fortawesome/fontawesome-free/scss/larger';
@import '~@fortawesome/fontawesome-free/scss/fixed-width';
@import '~@fortawesome/fontawesome-free/scss/list';
@import '~@fortawesome/fontawesome-free/scss/bordered-pulled';
@import '~@fortawesome/fontawesome-free/scss/animated';
@import '~@fortawesome/fontawesome-free/scss/rotated-flipped';
@import '~@fortawesome/fontawesome-free/scss/stacked';
@import '~@fortawesome/fontawesome-free/scss/screen-reader';

// the fonts itself, perhaps you only need "solid"
@import '~@fortawesome/fontawesome-free/scss/regular';
@import '~@fortawesome/fontawesome-free/scss/solid';
@import '~@fortawesome/fontawesome-free/scss/brands';

// add your icons here
$used-icons: (
  bars: $fa-var-bars,
  calendar: $fa-var-calendar,
  search: $fa-var-search,
);

@each $name, $content in $used-icons {
  .#{$fa-css-prefix}-#{$name}:before {
    content: fa-content($content);
  }
}

Workaround for digest issue

As mentioned, if you add additional icons, the filename of the generated icon font will not change. This means browser will keep using the cached font and not see new icons. When deploying with capistrano, the following workaround will cause a new font file to be generate on every deploy:

Add the following to your config/webpacker/environment.js (if using webpacker):

const fs = require('fs')

let iconFontVersion = 'dev'
try {
  iconFontVersion = fs.readFileSync('REVISION', 'utf-8').trim()
}
catch(_error) {
  // okay, probably in dev mode
}

environment.loaders.prepend('fa-font', {
  test: /fa-.*\.(woff2|woff|eot|svg|ttf)$/,
  use: [{
    loader: 'file-loader',
    options: {
      name: '[path][name]-[hash]-' + iconFontVersion + '.[ext]',
      context: join(config.source_path),
    }
  }]
})

environment.loaders.get('file').exclude = /fa-.*\.(woff2|woff|eot|svg|ttf)$/
Tobias Kraze
September 20, 2018Software engineer at makandra GmbH
Posted by Tobias Kraze to makandra dev (2018-09-20 19:16)