The QueryCon 2018 Shirt Design, Created By Fritz Ifert-Miller and inspired by Kolide’s iconography.

Kolide is a security focused infrastructure analytics company. We ingest data about your devices and provide actionable insights in a thoughtful user experience. Part of that thoughtful user experience is visualizing the state of your fleet in a way that is easily understood by anyone.

Descriptive and consistent iconography is a key objective in our UX. While building our first product, Kolide Fleet, we quickly learned that free-to-use iconography to represent even simple concepts like CPU and RAM were lacking in both clarity and aesthetic consistency.

These “Hardware” icons from Google’s material design project are very hard to parse. For example, look at the memory icon in the last column in the third row. It looks like a CPU.

To solve this problem, we decided to build our own icon library, which we call “Kolidecons”. We now use this library across all of our products, both commercial and open-source.

We’ve learned a lot about creating our own icons, too much to cover in one post. For our first post on the topic, we will focus on how we’ve codified the entire icon creation process and integrated it into our code base.

We’ll be covering a range of topics including:

Sketch file organization

Command line tools for

Exporting Sketch Layers

Optimizing the exports

Creating a sprite with the optimized files

Generating a README document

Using Makefile to automate the entire process

The Old Way

When we first created “Kolidecons” the process of creating and exporting the icons was mostly manual and prone to error. The process would look something like this:

Someone on the team would decide we needed a new icon for a screen. Fritz, our designer, would open up a new Sketch document and draw a beautiful icon. Fritz would then export the icon as an unoptimized SVG and dump it into our #ux Slack channel. Developers would download the SVG and run a variety of gulp tasks in the repo that optimized, minified and exported the icon to a global spritesheet. Developers could then use the new icon in the app.

This process suffered from a number of both short and long-term problems. Most notable are:

Naming Convention: No thought was given to naming an icon within the context of other icons. As a result, icon names were inconsistent (ex: “caret-down” vs “up-arrow”). We often found ourselves naming icons after the feature they represented. For example we would call an alarm bell icon “alert” vs “bell” because it was representing the “Alerts” feature in the product.

No thought was given to naming an icon within the context of other icons. As a result, icon names were inconsistent (ex: “caret-down” vs “up-arrow”). We often found ourselves naming icons after the feature they represented. For example we would call an alarm bell icon “alert” vs “bell” because it was representing the “Alerts” feature in the product. Duplication: Since there was no set process for creating icons, and no easy way to see which icons were available to use, many similar icons were created needlessly.

Since there was no set process for creating icons, and no easy way to see which icons were available to use, many similar icons were created needlessly. Missing Icons: Icons were often created, saved to a Sketch file and sent to developers, but never used simply because they missed them in Slack.

Icons were often created, saved to a Sketch file and sent to developers, but never used simply because they missed them in Slack. Unoptimized Icons: Since the tooling to clean up the SVG code in icons was a manual process, many times a developer would forget resulting in extraneous objects and layers in icons that would cause coloring issues.

Since the tooling to clean up the SVG code in icons was a manual process, many times a developer would forget resulting in extraneous objects and layers in icons that would cause coloring issues. Sizing Inconsistencies: Since icons were normally made one at a time in their own files, they could have sizing inconsistencies that would impact how they aligned with other similar icons in the app.

As you can see, the above process was not going to scale and we needed to improve it.

The New Hotness

Today, the process is completely revamped. The primary objectives of our new Kolidecons library was to automate as much of the process as we could (to reduce human driven errors), and to build and name the icons consistently.

One Sketch File to Rule Them All

Instead of designing our icons in separate ad-hoc Sketch files, we now have a single Sketch file that holds all of our icons.

Putting all of the icons in a single file affords us the following benefits:

Design consistency: With all the icons in close proximity to each other, it’s much easier to keep design style (like border thickness and aesthetic flourishes) consistent across the variety of icons. An inconsistent icon will stick out like a sore thumb.

With all the icons in close proximity to each other, it’s much easier to keep design style (like border thickness and aesthetic flourishes) consistent across the variety of icons. An inconsistent icon will stick out like a sore thumb. Naming consistency: Like design consistency, naming consistency is important for developers to easily find the icons they need. If a developer has to constantly reference an icon library to find every icon, it slows down the process tremendously. With all the icons in one file outside of the context of a feature, it’s much easier to establish consistent naming conventions and check a new icon’s name against existing examples.

Like design consistency, naming consistency is important for developers to easily find the icons they need. If a developer has to constantly reference an icon library to find every icon, it slows down the process tremendously. With all the icons in one file outside of the context of a feature, it’s much easier to establish consistent naming conventions and check a new icon’s name against existing examples. Automation: If all the icons are in a single source of truth, it creates opportunities to programmatically extract the icons into our apps (more on this below).

Programmatic Export

Expanding on our single Sketch file and the automation opportunities it creates, we went further and set up the file to allow us to programmatically export the assets.

For those of you following along at home, to accomplish this your Sketch file should have a sliced layer for each icon. All the slices should be arranged on a single page in whatever order makes sense to you. During the export, Sketch will use each layer’s name as the filename output, so make sure you keep things consistent.

To automate this step, you need to have macOS Sierra (or newer) and Sketch.app installed on your device. A small binary is installed with Sketch called sketchtool. The tool can be found in the app’s Resources directory at /Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool .

We’ve decided to keep an icons.sketch file in our application repository so that it stays synced with any code changes and makes automation much simpler by knowing the exact file path. We will also need a directory to store all of the exported SVGs. This can be public facing or not, depending on how you want to serve your icons. We opted to use these SVGs as source files rather than referencing them directly.

Here is an example of a CLI command you could run to export all the sliced layers as separate SVG files:

/Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool export slices path/to/icons.sketch — formats=svg — output=src/assets/icons

More options can be found in this sketchtool guide.

Optimizing the Code Resource Allocation

By default, Sketch doesn’t output the most optimized version of the icons, but luckily we can fix that pretty easily!

We use SVGO to handle our optimization process. There are loads of tools that wrap SVGO for Grunt, Gulp, Webpack, and so much more. We’ve opted to stick with the command line so that we can automate the entire process in our Makefile (which we’ll discuss later).

Let’s install SVGO local to the project with yarn add -D svgo .

At Kolide, we determined that referencing a locally installed dependency was more reliable because we can then add these to the devDependencies of our package.json file. The installation will then be automatically managed, instead of relying on each individual to install and maintain the correct version.

You’ll need to decide how you want to optimize your SVGs and create a configuration file with these settings. In the root of our project, we created a svgo.json file that we’ll store these settings in.

Here is an example of our configuration:

This will remove the <title> element (so that hovering over an icon won’t just output the filename), reduce path precision to two decimal places, update fill and stroke attributes with currentColor value so that they will automatically be the same color as the text of the parent. The dimensions should be removed, but we want to keep the viewbox attribute so that as the icon grows it stays consistently shaped. We also remove IDs, as new ones will be generated later on when we create our sprite.

You can review these, and many others, on the SVGO Plugins Table.

Now that we’re all setup, we can run our command to optimize all of the icons we just exported from the Sketch file:

./node_modules/.bin/svgo — config=./svgo.json — folder=./src/assets/icons/ — output=./ui/src/assets/icons/ — multipass — quiet

You can check out the SVGO CLI docs for more information on this command and its additional options.

Symbols, CSS, Stacks, and more!

Years ago, we would generate sprites with large images, and then use CSS to slide around a background in order to have icons (and other smaller images) load in quickly and simultaneously.

Since we’re going to be using SVGs, we will be going about this differently. We chose to work with <symbol> elements for our SVG icon sprite.

We’ll be using a utility called svg-sprite that has a few other sprite options, so make sure you check out the README to see if another is more appropriate for your project.

First we’ll need to install this dependency with yarn add -D svg-sprite .

Since we’re going down a pretty simple workflow with svg-sprite, we’ll jump right into it. All we need to do is setup the symbol options in the command and choose the directory we created above with all the, now optimized, icons.

./node_modules/.bin/svg-sprite — symbol — symbol-inline — symbol-dest=public/images/ — symbol-sprite=icon-sprite.svg src/assets/icons/*.svg

Again, there are tons more options for svg-sprite CLI depending on your workflow.

But how does it look?!

No worries, we’re getting there! We now have extracted all of our icons from a Sketch document, we’ve optimized them with SVGO, and we’ve created a sprite SVG file that we will serve to our users. But, what about developers? They need to know what icons are available to them as well.

Let’s fix that by generating a Markdown document with all of our icons and their associated name.

You will need to save this file to the root of your application, such as icons.sh or something. Provide it with the executable permission: chmod +x icons.sh and then run it with ./icons.sh to see the magic.

But wait, there’s more!

Didn’t I claim this was all automated? To me, this looks like a ton of commands to remember or alias or document somewhere. Whoever wrote this blog is a scoundrel!!

Okay okay… calm down, let’s automate this.

At Kolide, we’ve standardized on using Makefile to stand up our development environments, setup databases, run tests and linters, handle CI and Docker tasks, and so much more. Instead of relying on the shell file above, or remembering all the CLI commands, we’ll create ourselves a few new tasks to handle all the steps:

Now, all your designer needs to do is update the Sketch document with the latest icons, run make icons , and you’re ready to roll!

What about…

So, we left you with the public/images/icon-sprite.svg , what do you do now? That’s up to you, but at Kolide, we injected the contents of the SVG into our template, and then referenced the icons with <use> elements.

<svg class=”icon”>

<use xlink:href=”#icon-name” />

</svg>

In a follow-up blog post, I will explain the implementation of icon components in our React app.