Generating multi-brand multi-platform icons with Sketch and a Node.js script — Part #1
By: Cristiano Rastelli
Tags: Nodejs, JavaScript, Tools For Design, Design Systems, Sketch

Well, actually it’s not that easy, but it can certainly be done. This post is a detailed explanation of how we did it, and what we discovered along the way.

The problem we were trying to solve

At Badoo we build a dating app. Actually, multiple dating apps. For multiple platforms (iOS, Android, Mobile Web, Desktop Web), across multiple teams.

We use hundreds of icons in our apps. Some of them are the same across different apps, some are very specific to the brands the apps reflect. The icons are continuously evolving, in sync with the evolution of the design. Sometimes completely new icons are added, while others get updated, and still others get dropped (although, they often remain in the codebase).

Our design team designs and maintains the icons, and until now the only way for them to provide the correct assets to the different teams, platforms and apps, was to send them via email, chat or dropbox. This is not only time-consuming, but but it’s almost always prone to error. In fact, we found that errors occurred every time (we’re human!): icons would be updated on one platform but not on another; or icons would be missing or in the wrong format or size. So, there was a constant back and forth between designers and developers; developers would export icons directly from the Sketch file, and the icons would get added to the codebase but with no check to see if similar icons already existed (and were available for reuse). I’m sure you know what I am talking about 😄

At Badoo we have a Design System, called Cosmos, and recently we introduced a library of Design Tokens across multiple platforms (Mobile Web, Android and iOS) both for our main app and its white-labels. Essentially, we are now able to communicate design decisions (like the border of a button, the background colour of a particular feature page, the font size of the Heading 1, or the duration of an animation for a popup) using elementary design values, all processed and delivered automatically to all the apps, and all the platforms consuming these design tokens in their apps.

How we are now able to transform a design idea, like for example a change of a colour, to real code in production, in just a few clicks and hardly any time at all, has really impressed product managers and designers alike.

So, their next question (and request) was: can you do something similar for the assets? And our answer was: yes, we (probably) can!

It was quite a leap of faith at the time, I have to admit. We had some ideas of how to do it, but we were not at all sure if it was technically doable, with all the restrictions we operate under. We agreed to start with an MVP project, but by the end everything had gone so well that it became our final product, with all the necessary features.

The requirements

The requirements for the MVP were very clear: create a pipeline that could take a Sketch file and export all the icons included in the file, in different formats, for different platforms, with each icon suitable for use in AB testing.

This is complicated because, in our products, one icon can have many different colours and/or different shapes for different brands/white-labels (although the codebase of the app is the same, as well as, the name of the icon).

Look at some of the icons used in our applications and you will notice that some are identical, some are very similar to each other but for a few details, while others are quite different, both in their shapes and colours:

A comparison of the icon for the different brands

Now, the colours used in the icons are not just plain colours, but match the exact colours stated in the design tokens for that brand and its specific features:

Comparison of the colours across different brands. The colour values are specified as design tokens.

So, our aim with this new assets pipeline was not only to automate the process of generation and delivery of the icons, for all the different platforms and brands, but rather to be able to “dynamically” colour the icons according to the brand/white-label.

Sketch and sketchtool

Sketch is the main design tool that our design team uses. Even though we did consider other options (Figma, mainly), we knew that Sketch was the format of the source files we were going to be using for this project (simply because it’s the tool our designers are more proficient in, and it’s the format of the files where the existing icons/assets are used, among other reasons).

The fact is that, at the beginning of the project we were not even sure what final formats the platforms were expecting. In our minds, the process was going to be pretty basic like this: export the icons in SVG format from the Sketch file and then consume the SVG files in Mobile Web and Android, and for iOS find a library that can convert SVGs to PDFs. And that’s it. That was the plan when we started, although we had no idea if it would work, or about the unknown unknowns we might encounter (hence, the MVP to see if it was even feasible, and, if so, how much effort it might entail).

I don’t know if you have ever worked with “PDF converters” but in my experience they are generally a pain. They “almost” do the job, but never to the 100% you really need. So, in the back of my mind, I felt we were treading a dangerous path.

Sketch has a way of exporting assets that is pretty much perfect, I’ve never had a problem with it (be it SVG, PDF, or other formats). So I wanted to see if there were any other ways to interact with Sketch, to use its engine to export assets directly via Sketch, possibly in a programmatic way (I was also wondering if a custom plugin could be built, although that would have meant a lot of work for me, not only because I have zero experience in that field!).

I knew that internally Sketch is no more than a zip file (if you rename a .sketch file to .zip, double-click to uncompress it, and open the resulting folder, you see a list of JSON files, and a bitmap used as preview):

Inner structure of a Sketch file, once uncompressed

So, I started to explore the different JSON files, trying to understand the connections and dependencies between them.

I realised that somehow, despite the JSON files being deeply nested (and quite large!), the relations between the different entities inside their objects is not too complicated: you have pages, artboards and layers; inside the layers you have the paths, and you can have shared styles between them; each one of these entities has a unique ID that is used to keep a reference between the different files; and all the “page” objects are saved in JSON files, stored in a sub-folder called pages, with the ID of the page used as name of the file.

One significant discovery I made, during this exploration, was that the names of the layers, pages, and styles are just labels, and they can be changed at any time, without compromising the inner workings of the Sketch file. The thing that matters is the unique ID assigned to them, and this is never exposed to the end user (although, can be read and referenced inside the JSON files). Here is an example of how the unique ID of a style looks like:

{

"_class": "sharedStyle",

"do_objectID": "49BA4E98-8D63-435C-81D9-E2F6CDB63136",

"name": "name-of/the-style",

"value": {

"_class": "style",

"endMarkerType": 0,

"fills": [

{

"_class": "fill",

"isEnabled": true,

"color": {

"_class": "color",

"alpha": 1,

"blue": 0.7176470588235294,

"green": 0.4627450980392159,

"red": 0

},

"fillType": 0,

"noiseIndex": 0,

"noiseIntensity": 0,

"patternFillType": 1,

"patternTileScale": 1

}

],

"miterLimit": 10,

"startMarkerType": 0,

"windingRule": 1

}

}

This gave me an idea: that maybe we could use specific conventions on the names of the artboards and of the pages, to declare some kind of meta-information about the relations between the different assets, and use them programmatically at build time.

Sketchtool

At this point, by the end of the initial explorations, the plan had changed from “let’s export the icons in SVG and then convert them” to “let’s build a plugin that, within Sketch, allows us to directly export the icons in their final format”. But even then, the plan was still very blurred (and the technical feasibility was still uncertain).

It was while I was looking at existing plugins, specially at their source code, to see if and how they might interact with the Sketch export APIs, that I came across a tool that I’d never heard of before: Sketchtool.

Sketchtool is an official Sketch tool (official as in: developed by Bohemian Coding), which according to the documentation:

… is a command line utility that’s bundled with Sketch, that allows you to perform some operations with Sketch documents, like inspecting them or exporting assets. It also lets you control Sketch from the command line to perform some actions (like running plugins, for example).

Hang on… a command line utility to perform operations like exporting assets? Just, what I was looking for! Also, being an official tool, there wouldn’t be any problems of versions, obsolescence, maintenance, etc.

I started to look into it immediately, and read the documentation, which amounted to just a single page on the Sketch website (I’ve found hardly any other resources or pages about it out there, so it’s understandable that I’d never heard of it :) )

Sketchtool is bundled directly with Sketch, and you can find it inside Sketch at this path: Sketch.app/Contents/Resources/sketchtool/.

When you launch it in your CLI with the command:

$ /Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool

this is the output you see on your terminal (I have simplified it a little):

Usage: sketchtool <command> [<args>]

[--formats=<string>]

[--use-id-for-name{=YES|NO}]

[--export-page-as-fallback{=YES|NO}]

[--serial{=YES|NO}]

[--context=<string>]

[--application=<path>]

[--without-activating{=YES|NO}]

[--item=<string>]

[--items=<string>]

[--safemode{=YES|NO} | --no-safemode | -S {<YES|NO>}]

[--max-size=<float> | -m <float>]

[--background=<string> | -g <string>]

[--compression=<float> | -c <float>]

[--new-instance{=YES|NO}]

[--reveal{=YES|NO}]

[--timeout=<float>]

[--include-symbols{=YES|NO}]

[--bounds=<rectangle>]

[--outputJSON=<path>]

[--filename=<string>]

[--wait-for-exit{=YES|NO}]

[--scales=<path>]

[--overwriting{=YES|NO}]

[--group-contents-only{=YES|NO}]

[--trimmed{=YES|NO}]

[--help]

[--progressive{=YES|NO}]

[--save-for-web{=YES|NO}]

[--output=<path>] Commands:

dump Dump out the structure of a document as JSON. export artboards Export one or more artboards

export layers Export one or more layers

export pages Export an area from one or more pages

export preview Export a preview image for a document

export slices Export one or more slices help Show this help message. list artboards List information on the document's artboards.

list formats List the supported export formats.

list layers List information on all of the document's layers.

list pages List information on the document's pages.

list slices List information on the document's slices. metadata List the metadata for a document.

run Run a command from a plugin, inside Sketch.

show Show the location of the various sketch folders. See ‘sketchtool help <command>’ for more information on a specific command.

As you can see, the tool has four main functions: to read/dump the metadata of the internal JSON files; to list the entities inside a file; to export these entities; and to run a command exposed by a plugin. In addition, each command has a lot of options available. In the case of the export command, almost all the options you find in the export panel are also available via Sketchtool’s command line:

“Export” panel in Sketch, with the available options

This means that Sketch can be used directly as export “engine” via sketchtool, without the need for external converters (from SVG to PNG or PDF, for example). Big deal! 🎉

A quick test using sketchtool and a simple Sketch file with a few icons inside, confirmed all the initial suppositions: just by using this simple tool, we can avoid both using third-party exporters and building our own custom exporters: Sketch does it all!

The Sketch file(s)

Once we knew that Sketch was the tool we were going to use, for both storing and exporting the icons, it was time to actually collect the icons used in our applications into a Sketch file.

Initially, we only planned to work with a limited set of icons, those for the MVP project, but we quickly realised that it would have been better to collected them all at once, to be able to immediately spot duplicates, inconsistencies, any omissions, etc.

Our designers did an incredible job and in just a couple of days a large part of the assets used in their Sketch files has been collected and organised into a single file. At this point, the Sketch file looked like this: