Webpack: Automatically generating an icon font from .svg files

Updated . Posted . Visible to the public. Deprecated.

Please use this approach instead.

Over the years we have tried several solution to have vector icons in our applications. There are many ways to achieve this, from SVGs inlined into the HTML, SVGs inlined in CSS, JavaScript-based solutions, to icon fonts.

Out of all these options, the tried and true icon font seems to have the most advantages, since

  • icon fonts are supported everywhere
  • they perform well and require no JavaScript at all
  • their icons align nicely with text
  • their icons automatically inherit color and size of the surrounding text

The big issue used to be building the font itself was very cumbersome. Luckily, there are webpack plugins out there, such as iconfont-plugin-webpack Show archive.org snapshot that automate the font building.

If you're still using the Rails asset pipeline, try the half-automatic approach.

Webpacker Rails: How to integrate the plugin

yarn add iconfont-plugin-webpack
yarn add resolve-url-loader

Add these lines to your environment.js:

const IconfontPlugin = require('iconfont-plugin-webpack')

environment.plugins.append(
  // https://www.npmjs.com/package/iconfont-plugin-webpack
  'Iconfont', new IconfontPlugin({
    src: './app/webpack/images/icons',
    family: 'icons',
    dest: {
      font: './tmp/webpack/generated/fonts/icon_font/[family].[type]',
      css: './tmp/webpack/generated/_icon_font_[family].scss'
    },
    watch: {
      pattern: 'app/webpack/images/icons/*',
      cwd: undefined
    },
  })
)

// unfortunately, we need to adjust the sass-loader and add an additional `resolve-url-loader`, otherwise
// the relative url(./fonts/...) entries generated by the IconfontPlugin will not work
const sassLoaderConfig = environment.loaders.get('sass')
const sassLoaderIndex = sassLoaderConfig.use.findIndex(config => { return config.loader === 'sass-loader' })
sassLoaderConfig.use[sassLoaderIndex].options.includePaths = ['tmp/webpack']
sassLoaderConfig.use.splice(sassLoaderIndex, 0, {
  loader: 'resolve-url-loader',
})

Put all your icons into app/webpack/images/icons/.

Import the icon-font by adding a file icon_font.sass:

$icon-prefix: '-'
@import "generated/icon_font_icons"

$size: 1em * $line-height-base // only works with bootstrap

.icon
  // adjust some styles
  display: inline-block
  width: 1em
  height: 1em
  font-size: $size

  -moz-osx-font-smoothing: grayscale
  -webkit-font-smoothing: antialiased
  text-rendering: auto

  font-style: normal
  font-weight: normal
  line-height: 1

  // We can not use "vertical-align: middle" to vertically center the font, or icon containers
  // cause their surrounding containers to grow. The follow produces the desired result.
  vertical-align: text-bottom
  margin-bottom: -.1em

Now, in your HTML, you can add an icon with

<i class="icon -name-of-the-icon"></i>

Icon names are simply the file names of the SVGs.

If you want to use icons in Sass, you use the following:

// probably want to do this in a generic _mixins.sass:
$create-font-face: false
$create-icon-classes: false
@import generated/icon_font_icons

.my-element
  +iconfont(icon-name)

Known issues

  • When you add a new icons, our usual live reload might not pick it up, and you have to reload your page manually.
  • I've seen one case where an SVG file was not converted properly into the fonts. The issue was a transform attribute on a <use> tag that was seemingly ignored. I was able to fix the affected SVG with my text editor.
  • evenodd is not supported and can be fixed with the File Rule plugin in Figma Show archive.org snapshot

Debugging

See also

Tobias Kraze
Last edit
Michael Leimstädtner
License
Source code in this card is licensed under the MIT License.
Posted by Tobias Kraze to makandra dev (2019-05-21 12:52)