Sketching in the Browser

Ask any team that works with a design system, and you’ll find the benefits are clear—designers and developers are more productive, products are more consistent, communication between disciplines is clearer.

However, most design systems still have a fundamental flaw. Designers and developers continue to work in entirely different mediums. As a result, without constant, manual effort to keep them in sync, our code and design assets are constantly drifting further and further apart.

For companies working with design systems, it seems our industry is stuck with design tools that are essentially built for the wrong medium—completely unable to feed our development work back into the next round of design.

Luckily, this is all about to change.

SEEK Style Guide home page

Our design system journey

At SEEK, we’ve been working on our living style guide for over a year now, with an ever-growing suite of React components. As you might expect, this has introduced a radical shift in how we think about visual design.

Suddenly, we had a single source of truth, in code, easily installable, defining how our brand appears across dozens of disparate projects.

Naturally, our design system work didn’t begin in the browser. We’d already spent a lot of time trying to formalise our design rules in a more static form—long before our living style guide ever existed.

What began as a static PDF eventually gave way to a Sketch starter kit. Standardised symbols, colours and text styles could be easily leveraged as the starting point for any new design work.

Our original Sketch starter kit

Some time later, we experimented with Craft, a suite of Sketch plugins from InVision, the most notable being their Library plugin.

This allowed us to share Sketch symbols across both document and team boundaries, building a shared symbol library for the entire organisation.

Craft’s Library plugin

What became immediately apparent was the sheer amount of work required to keep this library up to date, particularly when new and existing patterns are constantly evolving across our products.

Developers, often pairing with designers, would make changes to the code that could have a dramatic impact on the visual design, but our static design library would remain completely untouched—unless, of course, someone remembered to manually keep it to date, which usually didn’t happen.

Meanwhile, we were experiencing exactly the same consistency problems on the other side of the fence, with developers lacking a clear source of truth for design in their code.


From React to react-sketchapp

It was around this time that we began work on our first React project—server-rendered, powered by webpack, and CSS Modules (which we helped co-create along the way)—eventually leading to the creation of our living style guide.

React’s singular focus on components made this transition almost inevitable. It’s no surprise that, since its release, we’ve seen similar stories play out at countless other companies around the world.

Once we’d built up a sizeable collection of components, other teams working on new projects were quick to capitalise on our work—but since our style guide consisted of React components and Less styles, it was of little use to our designers. However, this didn’t stand out as an immediate issue to us. The technical disconnect between designers and developers is an age-old problem—one that’s been with our industry for so long that we’re largely trained to ignore it.

That was, of course, until we saw react-sketchapp.

React-sketchapp
“In Sketch, we use symbols and overrides, in React we use components and properties. The concepts are so similar that it seemed silly not to unify them.”
Jon Gold, Airbnb

We couldn’t believe our eyes. Here we had real React code, rendering directly into Sketch. It seemed that both developers and designers would finally be able to leverage a design system’s single source of truth.

With our design rules defined centrally in code, we’d not only be able to power our production applications—we’d also be able to feed our work back into the tools that our designers were already using. As our design rules continued to evolve, we’d automatically be able to keep our designers up to date, without any manual translation back to Sketch.

Of course, once we dug in a bit further, we discovered that react-sketchapp came with a few requirements.

  1. Components needed to be built with React (obviously). Luckily, we were already using React, so this wasn’t a problem.
  2. Styles needed to be defined in JavaScript. In our case, since our design system was built with CSS Modules, we’d already hit a bit of a hurdle. We’re big fans of CSS-in-JS, but we weren’t about to overhaul our styling across the entire ecosystem—not in any hurry, at least.
  3. Components needed to use generic primitives (View, Text, StyleSheet) rather than browser primitives, using something like react-primitives. Essentially, react-sketchapp was closer to React Native than vanilla React. Again, this is something that we could feasibly migrate to, but it would require a lot of work—and possibly a few tradeoffs along the way.

So, while react-sketchapp is an amazing project that we wholeheartedly recommend, its technical requirements unfortunately meant that we wouldn’t be able to make use of it in the short-to-medium term.

Even if we decided to migrate our component library, we needed another answer in the meantime.


Bringing design to version control

As you may already know, there are tools available now that allow you to use version control inside of your design tools—but this is completely backwards.

We need to bring our design tools to version control, not the other way around. We need design assets to sit side-by-side with any other asset type—not stored in a design-centric silo, hosted in a proprietary walled garden.

So we tried something different.

Using Kactus and some custom Node scripts, we experimented with committing Sketch files into our style guide repository.

Kactus, showing a git diff for a Sketch file

While we were able to achieve this technically, we were disappointed to find that the workflow never worked out the way we’d hoped—for our use case, at least. Keeping two radically different formats in sync was extremely tedious, error prone, and difficult to review.

Maintaining code and Sketch files in the same location may have helped make the problem clearer, but it didn’t help much in actually solving the problem. To make matters worse, it introduced a lot of extra friction for style guide contributors for seemingly little gain. The Sketch files were quickly neglected. For us, it was a failed experiment.

But then—just when we felt like we’d lost hope in bringing designers and developers together in the same project—html-sketchapp came along and changed everything.


The emergence of html-sketchapp

It turned out that we weren’t the only ones having trouble integrating react-sketchapp into their existing tech stack.

“We were unable to quickly work around these limitations, so we created html-sketchapp.”
Konrad Dzwinel, Brainly

With html-sketchapp, they took a radically different approach.

As the name would suggest, html-sketchapp lets you generate Sketch files from regular HTML content—but unlike react-sketchapp, there are no restrictions on the technology choices in your application.

You could build your app with Preact, or Vue, or Angular, or Backbone, or jQuery—or even Ruby or PHP.

You could still use React, of course, but now you could style it however you like, using whatever primitives made sense for your project.

The contract was incredibly straightforward—as long as you generated HTML, you could import it into Sketch.


Generating Sketch files

At first glance, this seemed like magic, but when we had a look under the hood, we quickly found that it’s actually not that complicated.

To understand how html-sketchapp works, you first need to understand Sketch’s file format. Surprisingly enough, Sketch files are really just Zip files.

Once upzipped, Sketch files are mostly made of JSON files which, of course, can be opened in any regular text editor.

If you have a look at the contents of these files, you’ll see that it’s a relatively simple format, essentially made from a small handful of nested classes.

At its lowest level, html-sketchapp allows you to programmatically generate instances of these classes and convert them to JSON—but it goes much further than that.

The most powerful function in html-sketchapp is nodeToSketchLayers’, which gives you the ability to convert a single browser element into an array of Sketch layers. This is where most of the magic happens, since it contains all of the logic for extracting browser styles and converting them into their Sketch equivalents.

What really brings this all together is the ‘SymbolMaster’ class, which allows you to dynamically generate Sketch symbols. Since symbols form the basis of any Sketch library, this allowed us to selectively expose a set of components to our designers, based directly on the underlying code.

Unfortunately, due to some limitations in the current Sketch format with how text styles are encoded, the generated documents aren’t quite valid Sketch files—html-sketchapp refers to them as “Almost Sketch” files, or ‘asketch’ for short. As a result, they need to be manually imported via html-sketchapp’s Sketch plugin. Thankfully, this process isn’t too difficult.

Wiring all of these steps together sounded somewhat daunting at first, but it turned out that there was already an example project on GitHub showing how to convert an existing style guide into a Sketch document.

As soon as we saw this, it didn’t take us long to start experimenting, and it took a surprisingly small amount of time before we started seeing truly startling results.


Experimenting with html-sketchapp

First, to get a feel for what was possible, we pointed it at the home page of our style guide’s website.

Next, we started generating our first symbols from our ‘Button’ component, rendering a few different variants.

To achieve this, we came up with a convention of adding a special JavaScript file within each component folder (e.g. Button.sketch.js), defining the symbols that we wanted to export.

Each file would export an object defining the symbol names and their corresponding React elements.

import React from 'react';
import Button from './Button';
export const symbols = {
'Button/Pink': <Button color="pink">Button</Button>,
'Button/Blue': <Button color="blue">Button</Button>,
'Button/Transparent': <Button color="transparent">Button</Button>,
};

We then created a special hidden route on our style guide site which imported any file ending in ‘.sketch.js’ and rendered the supplied React elements to the screen. Taking this approach, we were able to greatly simplify the conversion process by exposing all Sketch content on a single page.

Each symbol instance was wrapped in a div element with its name defined in a data attribute, which allowed us to easily select and name every symbol on the page.

<div data-sketch-symbol="Button/Pink">
...
</div>

This pattern proved to be very effective, and we soon expanded it to include text styles and document colours.

Since our style guide is responsive, we then automated the process of resizing the browser window and taking symbol snapshots at different screen sizes.

We could now add, remove and rename viewport sizes in a single location, and every single symbol would be regenerated to reflect these new values. In one fell swoop, it seemed that we’d solved one of the most tedious issues with maintaining a responsive Sketch library.

While everything went surprisingly smoothly, we did have to add a few workarounds specifically to support Sketch—in the same way that you might support buggy browser implementations—but we managed to localise them to a single file.


From experiment to production

What started as a small-scale experiment had quickly snowballed into something of a mini-framework. At this point, it didn’t take much work for us to get it properly integrated into our style guide, running as part of our standard build process.

If you look at the pull request, however, you’ll see that it required us to include a lot of extra wiring code and dependencies, even though—at a high level—we were trying to achieve a single, conceptually simple task.

To generate our Sketch library, we had to perform the following steps:

  • Compile a browser script with webpack, containing html-sketchapp and accompanying logic for selecting and converting elements.
  • Start a static web server on any available port.
  • Open Puppeteer (a headless version of Chromium).
  • Navigate to the correct URL.
  • Inject the compiled script into the running Puppeteer instance.
  • Resize the browser window multiple times, taking snapshots at each configured screen size by calling the functions defined in our compiled script.
  • Write the resulting JSON files to disk.
  • Shut down the static web server.
  • Shut down the headless browser.

It seemed obvious to us that this could easily be streamlined in a single command—one which would allow us to simply point to a URL and start taking snapshots.

So that’s what we did.


Unveiling html-sketchapp-cli

Less than a month after we first integrated html-sketchapp into our style guide, we open sourced html-sketchapp-cli, a small command-line utility to take all of this plumbing code off your hands.

Now, we could achieve the same thing with a single dependency and a simple, declarative config file.

module.exports = {
serve: 'docs/dist',
url: '/sketch-exports',
outDir: 'dist/asketch',
viewports: {
'Desktop': '1024x768',
'Mobile Plus': '414x736',
'Mobile': '320x568'
}
};

Not surprisingly, when we used html-sketchapp-cli in our style guide, we were able to delete a lot of code.


Continuous design pipeline

All of this tooling is now part of our standard continuous delivery pipeline, allowing us to expand the reach of our code—moving beyond the developer community and supporting our designers in their daily work.

On every successful build of the style guide—not only do we automatically deploy our site to GitHub pages (using gh-pages) and publish the component library to npm (using semantic-release)—we now automatically generate ‘Almost Sketch’ files, ready to be imported and converted into our official Sketch library.

This Sketch library then gets distributed via our design team’s shared drive, which means that our designers always have an up-to-date copy of the library, syncing in real-time, even while Sketch is open.

Thanks to Sketch’s new built-in library support, designers are immediately able to open up the “SEEK Style Guide Library” menu and start selecting components, knowing that the naming conventions and visual styling match the expectations of the developers on their teams.

Ever since this was introduced, we’ve seen changes to our code continuously flowing through into Sketch — sometimes when those making the changes don’t even have Sketch installed on their machines. Since the style guide is connected to our production applications, it’s constantly being improved upon from people all over the organisation, and we can now ensure that all of those improvements continue to keep our Sketch library fresh and up to date.

Even if they’re technically still working in different mediums, we’re working hard to create the illusion of a single unified language.


So, where to from here?

As great as this development has been for us, we’re still only viewing this as an interim solution. Rendering web content into Sketch is incredibly powerful, and a necessary step forward on our journey, but our industry needs to take this even further.

The line between our mediums may be getting blurry, but the design tools of the future need to remove this line altogether. For us to truly deliver on this potential, we need design tools that don’t merely imitate the target medium—we need them to be actually built on it.

Fortunately, there’s no shortage of people working on this problem right now. Tools like Compositor, Interplay, Alva, Haiku, Webflow and UXPin are looking to break down the technical walls between design tools and the underlying code, and there are more tools like this on the way.

Who knows—we may even start to see more traditional design tools adopt this approach in order to stay relevant, particularly as design systems continue to become a standard part of the modern design toolkit.

In the meantime, while we wait for new design tools that truly embrace the principles driving the design systems era, projects like react-sketchapp and html-sketchapp are doing an astounding job of getting us ready for this new way of thinking today.

Frankly, there’s never been a better time to start.