Icons are an essential part of Design Systems and Brand Identity. At Roche we are building a UI Kit components library that should provide icons out of the box. For convenience and performance reasons we want to offer icons not only as SVG graphics but also as an icon web font.
In this article, I want to go in-depth on the technical side of things how we, at Roche, were able to automate the conversion of SVG graphics to icon web fonts.
No time to read articles? Head over to the final repository directly.
Creating the Icon Font
Whilst there are many tools and websites to generate icon web fonts on the market, not many were sufficient enough. Our goal was to have a tool(-chain) that was able to run automatically inside a CI Pipeline of a Git repository, so the process would be automated. Manually generating the font by drag-and-dropping icon images into some arbitrary website was just not doing it for us.
That is why we were looking into CLI tools, which were as customizable as we needed it to be.
Prepare the repository
The icon font shall be deployed as an NPM package. Therefore we need to create a new repository with a standard NPM setup. Make sure you have NodeJS 10.x.x and Git installed.
# Create the repository directory
mkdir my-icon-font && cd my-icon-font
# Initialize Git and NPM
git init
npm init -y
cat > .gitignore <<EOF
node_modules
# Ignore the generated assets
font/*[.svg, .eot, .css, .ttf, .woff, .woff2, .html, .json]
EOF
touch .npmignore
# Create the directory for the icons
mkdir svg font
Let's copy over our SVG icons into the newly created my-icon-font/svg
-directory.
For the sake of convenience, I am going to use icons from flaticon.com. Go to the page, click on an icon you would like and download it as SVG image.
... and copy it over.
mv /Path/To/Downloads/icon.svg svg/
Install icon-font-generator
Icon Font Generator is a CLI tool built with NodeJS and available through NPM.
Let's check whether we can run the tool.
npx icon-font-generator --help
Generate the icon font
Let's generate the icon font using icon-font-generator
like so.
npx icon-font-generator svg/*.svg \
-o font/ \
--name "my-icon-font" \
--height 100
In your my-icon-font/font
-directory you should now find the following files.
font
├── my-icon-font.css # CSS file which includes the icon fonts and generates classes
├── my-icon-font.eot # The icon font file in EOT format
├── my-icon-font.html # A preview website of the icon font
├── my-icon-font.json # Icon metadata in JSON
├── my-icon-font.svg # The icon font file in SVG format
├── my-icon-font.ttf # The icon font file in TTF format
├── my-icon-font.woff # The icon font file in WOFF format
└── my-icon-font.woff2 # The icon font file in WOFF2 format
Let's open up the font/my-icon-font.html
file to see whether the icons look fine.
Awesome!
If we sneak peek into the CSS file font/my-icon-font.css
, we will see how it all works.
These following lines define our custom font, called "my-icon-font". As you can see, it makes use of all the generated font files. Therefore if a browser does not support a font format (e.g. SVG), it will fall back to another one.
@font-face {
font-family: "my-icon-font";
src: url("./my-icon-font.eot?65e38a3078508e502b3cc8df63e55a16?#iefix") format("embedded-opentype"),
url("./my-icon-font.woff2?65e38a3078508e502b3cc8df63e55a16") format("woff2"),
url("./my-icon-font.woff?65e38a3078508e502b3cc8df63e55a16") format("woff"),
url("./my-icon-font.ttf?65e38a3078508e502b3cc8df63e55a16") format("truetype"),
url("./my-icon-font.svg?65e38a3078508e502b3cc8df63e55a16#my-icon-font") format("svg");
}
The next lines define the styling of the icon itself. Every i
-HTML Element which begins with class icon-*
will apply our icon font.
i[class^="icon-"]:before, i[class*="icon-"]:before {
font-family: my-icon-font !important;
font-style: normal;
font-weight: normal !important;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
For each specific icon, an additional class is being generated with a unique Unicode which represents our icon.
.icon-contact-list:before {
content: "\f101";
}
.icon-groups:before {
content: "\f102";
}
If we include this CSS file in an HTML document, we can access the icon like this.
<!-- Include the CSS file (only once) -->
<link rel="stylesheet" type="text/css" href="my-icon-font.css" />
<i class="icon-contact-list"></i>
Compress SVG and fix odd-looking icons
In our case, some of the icons had strange strokes or sometimes not circular, when they needed to be.
To fix this and also, in addition, compress the SVG images, we can use the SVGO CLI tool.
SVGO, in short, is a tool to optimize SVG graphics.
Lets set up the configuration we are going to need to fix the faulty icons.
cat > config.yml <<EOF
plugins:
- convertPathData:
noSpaceAfterFlags: false
- mergePaths:
noSpaceAfterFlags: false
EOF
... and execute it.
npx svgo svg/*.svg -o icons --config config.yml
Now the icons should look all fine!
So we do not forget the commands we have executed, let's add it to our package.json and install the tools locally.
npm i -D svgo icon-font-generator
package.json
{
"scripts": {
"build": "npm run icons:compress && npm run icons:font",
"icons:compress": "svgo svg/*.svg -o svg --config config.yml",
"icons:font": "icon-font-generator svg/*.svg -o font/ --name \"my-icon-font\" --height 100"
}
}
Automate it using Github Actions
We want to re-generate the icon fonts on every commit using Github Actions.
In order to get set up, we need to create a configuration file, which defines the workflow of our pipeline.
mkdir -p .github/workflows
touch .github/workflows/nodejs.yml
We can keep the configuration file simple for now. We are just going to use NodeJS 10, install our dependencies and run npm run build
.
.github/workflows/nodejs.yml
name: Node CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: npm install and build
run: |
npm ci
npm run build
env:
CI: true
To see whether everything works, let's commit it to our remote repository.
git remote add origin <YOUR_GITHUB_URL>
git add -A .
git commit -m "Initial Commit"
git push origin master
Your repository should now tell you whether everything is set up correctly.
Continous Deployment using Semantic Release
The last step to fully automate this process is to publish an NPM package and ZIP file using Semantic Release to our end customer. We can use Semantic Release for that, which offers fully automated version management and package publishing.
First things first, let's reset our package.json
-version.
package.json
{
"version": "0.0.0-development"
...
}
Semantic Release will automatically increment our versioning from here on out.
The next thing we need to do is adding an NPM Access Token to Github Actions.
Head over to npmjs.org > Login > Click on Profile > Auth Tokens > Create new Token.
Once you have gathered your NPM token, head over to Github.
Goto Your Repository > Settings > Secrets > Add a new Secret.
Call it NPM_TOKEN
and add the copied key.
Once added, we only need to update our .github/workflows/nodejs.yml
file and we should be good to go.
...
- name: Semantic Release
run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
Once a new SVG icon is now being added via a commit, it should now automatically publish the NPM package and create a Github release.
Make sure you start the commit message with feat(): <MESSAGE>
.
Conclusion
This setup is definitely pretty advanced, but the benefits are huge. Automate this process using a pipeline backed by a version control system helps us to develop an icon pack for the long term.
A UI/UX designer could create Pull Request by themselves on Github using the Web interface. Since the icon fonts get generated using Github Actions, icon contributors do not need to download any tooling.
Resources
Found a Typo? Let me know!
Top comments (4)
Hi. What is the purpose of adding generated fonts to .gitignore? I though that we want to save them in a package that we publish, so if someone downloads it they can use it immediately by importing .css file.
Hey sorry I totally missed this. Basically you should never commit generated files within a Git repository. What you publish in your package is not the same as what you store in your Git repo. That is why, for instance, you would not commit the generated JS files from a TS project
Do you know if it is possible to set icon-font-generator to work with multiple html tags? For example font-awesome icons can be used in div, span, i, etc.
Yes. icon-font-generator allows you to overwrite the CSS template.