Désactiver le thème sombre

Article | Back to articles

Supernova: use design tokens in code, with Figma as a source of truth

02/04/2022 by Benoit Rajalu

A character sitting at their desk being flumoxed

Design tokens are a powerful way of ensuring your design language actually gets used. They are a versatile way to distribute its vocabulary, its colors, font styles etc... Using them makes obvious sense, but how do we create and distribute them? Without a strong pipeline, they'll remain a good idea that never actually gets used.

Even when they do get used, another issue arises. How do we maintain them? How do we keep them in sync for all consumers? The pipeline cannot be a passive one, it must be reactive. If that sounds daunting, fear not! There's a simple solution ahead.

The perfect pipeline

What are we trying to achieve with strong design tokens? We want them to be absolute. They need to dictate how we design as much as how we code. They must bind the designers' and the developers' worlds.

The designers remain responsible for defining them. They are design tokens. It's sensible to have their source be the designers' production tool... The design team then commands ownership over those tokens.

This has consequences. The designers are autonomous in curating the tokens. They are free to edit, remove or add anything. How do we ensure developers follow? Do we expect designers to deliver the tokens to the dev team? Should the developers stalk every move designers do?

That's unrealistic and unscalable.

The perfect pipeline observes the designer's source. It formats the created tokens into consumables for the developers. It pushes those tokens right into the codebase.

Once automated, the pipeline would trigger on every change with minimal human oversight.

Provided your team uses Figma, there's an effortless way to get there: Supernova.

Tokens in Figma

Let's first build our token library in Figma, where they are called “styles”. It supports colors, font styles and effects such as shadows or layer blurs. But Figma disappoints when it comes to handling the rest. It doesn't support spacings, radiuses, breakpoints and many other design decisions. Let's put a pin on that though and create our style library:

Screenshot: a collection of colors and text styles on Figma
Semantic tokens: these are all styles within a shared Figma library.

Here's a realistic little library. Note that the names of the styles are not random. Even without semantics, naming is a bridge between designers and developers. We share tokens and thus must share our understanding of their names.

We expect our pipeline glue to keep this naming.

Once our library is ready, we can publish it to our Figma organization. The other designers can now consume tokens, but the developers can't.

Schema: design tokens in the middle, linked bidirectionnaly to designers on the left, but a crossed out arrow toward developers on the right

Let's fix this.

Supernova exporters: format tokens for code

Our next move is to connect our Figma library file to https://cloud.supernova.io/. This is easy, but it will only list everything we've done on Figma. Sure, they have tools to help us build documentation around those styles, but it's not why we're here.

Screenshot: a collection of colors and text styles but this time on Supernova
Supernova lists everything we published on Figma.

We're here for the “Code Integration” section. This will let us export everything we've taken from Figma into our codebase. Supernova provides many default exporters in its (free) storefront: CSS, Swift, Tailwind... They all come with a preview feature, helping us figure out if the premade stuff is good enough for our use case:

Screenshot: The preview of a Supernova exporter running on our tokens
This exporter translates our tokens into CSS custom properties.

We're free to install more than one exporter too. Developers don't all share the same consumption habits. While web devs may need CSS, our mobile teams won't care for that at all.

If the premade exporters don't fit our needs, Supernova's killer feature then saves the day. We can create our own exporters, tailored to fit our needs. Fork any of theirs, and we're off to the races.

Here's an example of mine: https://github.com/Benrajalu/exporter-custom. The goal was to produce SASS aliases to CSS Custom Properties with a single exporter.

We can see this in output.json, where the exporter gets its in/out orders:

json


{
  "blueprints": [
    {
      "invoke": "tokens.pr",
      "write_to": "_tokens.scss"
    },
    {
      "invoke": "theme.pr",
      "write_to": "_theme.scss"
    }
  ]
}

Supernova uses a language called Pulsar to write those “tokens.pr” and “theme.pr” files. This has the disadvantage of being custom, but it is simple enough to learn. Slightly editing what the premade exporters provide is also enough for most cases.

Here's an example of my renderer for “color” tokens:

pulsar


{[ inject "reference-wrapper" context context ]}
{[ let hslaOutput = rgbaToHsla(context.r, context.g, context.b, context.a) /]}
{{ hslaOutput }}
{[/]}

The only edit here is not Pulsar, but the Javascript `rgbaToHsla` function. Yup! In the above example, `rgbaToHsla` is defined as plain Javascript. It is then accessible natively in Pulsar. This makes custom exporters easy to code and overwhelmingly powerful.

Schema: design tokens in the middle, linked bidirectionnaly to designers on the left.  It is now linked to Supernova on the right, but a crossed out arrow toward developers on the far right remains.

We now have our tokens in Supernova and exporters to format them. Let's get them to our developers.

Supernova builds and hooks

Pushing our formatted tokens into our codebase is very simple. Supernova provides what it calls “Builds” to do that. Pick the collection of tokens you want to use, the exporter, and your method of delivery of choice. Here I'm running my custom exporter and exporting the output into a Github pull request:

Screenshot: Supernova offers to open a pull request to output the result of an exporter run
It's very easy set-up for the delivery of an exporter run.

This is good already. Designers have created the tokens first. Supernova has then let us format them for our codebase. It has now handled delivery to the developers.

Schema: design tokens in the middle, linked bidirectionnaly to designers on the left.  It is now linked to Supernova on the right, which is now linked to developers on the far right.

But that's not enough, we had to trigger that pull request ourselves. That's where Hooks come in. With them, Supernova is able to open that pull request whenever we update the source library (on Figma).

Screenshot: a Supernova modal to configure running an exporter each time the Figma file is updated
We don't rely purely on human goodwill and communication anymore.

Now there's no escaping the tokens. Every time the designers update the library for “their” use, developers will be in the loop.

The extra mile: doing what Figma doesn't

Remember when we listed the styles Figma acknowledges as tokens? There weren't that many. It sucks, but there too Supernova can help. It lets us build tokens on top of those imported on Figma.

There are two sides to that coin, of course. On one hand, we can define tokens for spacing, border-radius, transition styles even... We can document every little part of our design language and distribute it all.

On the other hand, those important design paradigms are no longer coming from Figma.

Should that stop us? I don't think so. Supernova is best used as an extension of Figma. Designers and developers share the ownership of the tool. The former refine the grammar of their language. The latter ensure those decisions are exported in formats that fit their needs. Win win.

But what if we want more? What about getting those extra tokens back into Figma?

Schema: design tokens in the middle, linked bidirectionnaly to designers on the left.  It is now linked bidirectionnaly to Supernova on the right, which remains linked to developers on the far right.

When I'm writing this, Figma does not support this process, but there is Jan Six's Figma Tokens plugin. It extends Figma's basic token support, including for those Supernova lets us create. This plugin could consume the automatic output of a dedicated custom exporter. With this, designers can enjoy the same depth of tokenization as the developers.

We've looped the loop!

Extra mile: linting the tokens in Figma

We can even go a little bit further. We can fork the Design Lint plugin from Daniel Destefanis and start to tinker. With this plugin, we're free to create custom linting rules that are based on our tokens.

We can warn designers that a color token meant for text is being used for a background, or vice versa. We can suggest text tokens based on the currently selected font and size. We can bring designers the level of guidance developers are used to.

It is a little out of scope for this article, but the extra mile is always nice. Here's a rough implementation of this idea if it made you curious!

Caveats and conclusion

Nothing good comes for free. In Supernova's case, it's not free for everything. The free plan is generous, but automating many exporters or sources requires a paid subscription.

The developer documentation is also very out of date. The team says they are working on it though. Their Discord server is very active and friendly, in the meantime.

Their Pulsar language isn't recognized by our IDEs. Supernova provides a VSCode plugin that helps, but if you're not into VScode... You're out of luck.

This workflow also isn't perfect. If designers remove a token on Figma, the change is not breaking there. Figma fallbacks to that style's value. But we've tied our code to Figma now, so the token also gets removed from the libraries. What then? Do we trust our human review to spot that breaking change?

These issues are minor and most can be solved. A strong CI would spot the breaking change. Time will help with the doc. Money solves most limitations, as always. Even with those minor hindrances, Supernova remains compelling.

It's a simple yet powerful connector between design and code. It's so easy that it doesn't simply build that bridge: it makes it impossible not to build it. There would be no excuse.