Do you know where your icons are?

Managing icons on the Dropbox Paper team

“Hey, do you have the source file for that ‘add members’ icon?”

This can be a scary question for designers. Is it on your laptop? Your old laptop? Is it in “icons_latest_jw_jan17.sketch” or “icons_final_FINAL.sketch”? Does Angela know? Does Angela know someone who knows? When will the person Angela knows get back to Angela, so Angela can get back to you, so you can get back to the person who asked you in the first place?

The Dropbox Paper team didn’t have a great system for managing icons in the early days. If designers wanted to use an icon in a mock, they could ask around, download from the web inspector, or, if all else failed, grab a screenshot. Developers didn’t have it easy, either. To add or edit icons, they had to run ad-hoc scripts to optimize SVGs and then paste them into a file. It was time-consuming and error-prone.

Thankfully, the Paper team does a lot of things right when it comes to icons. On the engineering side, we use inline SVGs. These have many advantages. One advantage is that SVG is a well-structured format that we can manipulate with code. Paper is also using React and has a component for inserting icons. We also had some experience with sketchtool, a command line tool for exporting slices and artboards from Sketch files.

That made us wonder. Could we use code to connect the dots? Could we give designers a source of truth for all the latest icons, and give developers an easy way to do updates?

👋 Hello, Papercons

Developer Justin Hileman and I came up with a solution we call Papercons. It works like this:

There’s a single Sketch file checked in to our codebase that contains all the icon shapes.

Our custom build script uses gulp-sketch to export all the slices as SVGs.

The build script takes the exported SVGs and optimizes them. Then it builds a TypeScript file that has the properties for every icon — properties like path, height, width, and viewBox. A companion LESS file is also generated.

Developers can use a React component, “SvgIcon,” that takes that data and inlines an SVG in the app.

Ta-da! This has many advantages for designers and developers. Designers have a place to download all the latest production icons. They can use the icons in their mocks, natively in Sketch. The handoff with developers is also smoother. When designers make a new icon, they download the latest “icons.sketch,” add the shapes, and send the file back to the developer. The developer then replaces the icon file, the build script does its magic, and they commit the change. All the busywork is automated away.

Now, whenever someone asks for an icon, we can just share a link to all the latest production icons. No more hunting, context switching, and long conversation threads. We saved ourselves, Angela, and the whole team a lot of time.

Preparing icons for export

Sketch gives us a lot of tools to make great icons: masking, borders, groups, shadows. Unfortunately, those don’t translate well to SVGs, which we’re using in our app. We need to simplify our icon down to a single, flat shape. Sketch makes that easy, too, but it requires some finesse.

Here’s a step-by-step process for flattening an icon.

Draw the shape. We’re making a person-y shape using two modified ovals with borders. Those borders won’t export well, so we need to convert them to shapes. We can use the outline tool to do that. Select the shape and select Layer > Convert to Outlines in the header toolbar. We want to convert the two shapes into a single shape. To do so, select both shapes and choose Layer > Combine > Union in the header toolbar. In the Layer list (the left sidebar), move the inner shapes forward and backward to get back to the original shape. For inner shapes, you can change the Join type (Union, Subtract, etc.) using the “two square” icon to the right of the name. Note: the fill-rule determines how overlapping shapes are interpreted. This SitePoint article explains the two fill-rule types: “nonzero” and “evenodd.” To change the fill-rule, select the shape and click the gear next to the fill section in the right sidebar, like so. Another trick: Android requires the “nonzero” rule, but Sketch defaults to “evenodd.” Luckily, as long as you use a type like “Union” and “Subtract,” and avoid “None,” it’ll work for either fill-rule type. Flattening will merge any overlapping shapes into one. In the example, you can see the two background shapes are merged into one. To flatten, select Layer > Paths > Flatten. It’s a handy replacement for masking, which doesn’t export well. Warning: flattening is also destructive. That means you can’t get back to the original shape and might end up having to redraw your icon. Someday, we’ll add a “scratch” artboard with the original shapes that the script ignores.

Anthony Collurafici goes into more detail about preparing your icons for export in this great post. Give it a read.

Rough edges

This has been great for our workflows, but there are still a few hiccups.

Debugging can take time.

Flattening icons takes a lot of finesse, as we’ve seen. If we don’t do it exactly right, the developer and designer end up spending a lot of time debugging. The designer has to tweak the icon, send the file to the developer, then it has to be tested. If it fails, we have to do it again. Then again. Then again.

There are a few potential fixes for this. More descriptive errors would help us pinpoint what path is causing the issue. It would also be nice to have a test script that designers can run locally to be sure their icons are ready for handoff. It might also be nice to have visual diffs so we can compare them side-by-side.

The format is unforgiving.

There are a lot of particular steps involved in adding and editing icons, besides flattening. You have to know how to position it, create groups, and title things in just the right ways. Otherwise, it won’t build. We have a “how to” doc in Paper with documentation for designers and developers, but it’s still a lot of steps. I’m not sure how to best get around this, but thankfully folks only need to learn the process once.

What about versioning and conflicts?

Committing a binary Sketch file can lead to a lot of messy conflicts when you want to merge. Fortunately, the team uses feature flags, small conditionals in the code that allow different sets of users to see new features. Instead of using many, separate branches, everything is pushed to the main branch. This keeps things in sync and reduces the chance of conflict.

As the team grows, I imagine it’ll be harder to keep in sync. Luckily, if there is a conflict, it’s not hard to pull the Sketch files from different commits and merge visually. The two commits will tell you which icons are different, making it even easier.