Helio DS — Glovo design system on steroids
Why?
As Glovo was experiencing rapid growth, we started to expand to new verticals and release more features, which led us to have to develop a lot more new user interfaces. It involved many engineers and different teams. At that time we didn’t have a design system, and all of this caused a lot of frustration for the engineers: they had to reinvent the wheel across different apps, it was a big blocker for moving towards micro frontends, and developing new UI was taking a lot of time.
These were some things that made us focus on developing a design system. The first goals were to list what we have at the moment and align the design team to work in the same framework as we started to develop. We quickly realized that we already had some UI libraries and incomplete design systems, which made us think not only about developing yet another set of components but about delivering a system that would help us to not have this fragmentation in the future.
How did we solve this?
Some of the things we realized are:
- The UI we have in iOS and Android apps is almost identical, as well as the mobile web in most cases
- The same component (for instance button) may have a lot of different variations, which can be updated at any time
- We want all of our apps to eventually support light and dark modes, our components should be able to change their appearance in runtime.
- There still might be differences between platforms, for instance, only the web needs to support a hover state (when an element is hovered by the user’s cursor)
Design stage
With these things in mind, we decided to have a way to define this information about components as well as design tokens, on top of which the components are built. One of the first tokens defined by the design team was colors and spacings.
First, we had primitive colors, such as yellow500 or green700. Using these base colors we defined semantic colors, which have 2 values — a primitive color for light mode and a primitive color for dark mode. The spacings were just numeric values in pixels or DP (density-independent pixel), depending on the platform, for instance for web spacingXL was defined as 32px.
Connecting design with the code
All these definitions are done by the design team in their favorite tool, Figma. This is a great tool for designing UI and sharing it with people, but it doesn’t translate easily into what we need — the code. Since we need this data in our code, and as we expect this data to be updated frequently — we need some kind of code-friendly way of storing all of this data. We turned all this (as well as more other) information into YAML files with definitions. This way we could have a consistent data source for all the platforms and even designers can tweak them (in the future we can still do some automation to update those YAMLs directly from Figma).
What do these YAML files look like? Primitive colors just have a hex color as a value. Similarly, spacings have the same structure, with pixel/dp values instead of colors.
Later, using these primitive colors as variables we defined our “universal” colors, which are using primitive colors as values (e.g. variables).
Using all of these, we were able to define our components and their variants, here’s an example of the button variants.
Using all this data, we organized a monorepo that would allow us to generate and publish components for each platform we have: iOS, Android, and Web.
First, we organized a monorepo where we stored those definitions and developed “generators” that would turn those definitions into components for each corresponding platform. We decided to go for TypeScript as the common language of this monorepo, which was proven to be a good idea — web engineers already were using it in other projects and for mobile engineers the onboarding was very easy. On top of that TypeScript gives us flexibility in terms of how strict we want to be with the types, optionals and so on.
We developed a common codebase that would parse those YAML definitions and run corresponding generators for each platform. After this, all the work was done by the engineers of each platform in their own folders.
For the web we are generating VueJS components, the monorepo takes care of unit testing the output of generators, running UI tests to make sure that the components are looking the way they should, generating pages of the design systems website with all the documentation (more about that later) and publishing the components to a private github package registry. The process is similar for mobile platforms, where we are generating Maven artifacts for Android and CocoaPods Pod for iOS.
With the architecture, any UI change or a new variant of something like a button can be performed in one commit with the size of just a few lines. It will trigger the CI/CD for each platform that would run different tests, request the UI changes approval, update the documentation on the website and publish all the packages for every platform at once.
Documentation is King
As I mentioned earlier, we are generating a whole website with the documentation. At the moment it is hosted in an internal domain, so it’s only available to Glovo employees. It is the central place for the documentation for all the platforms, it includes the list of the components we have with detailed instructions on how to use them and their APIs. On top of that, it has the documentation for the design tokens we have — so the developers can see and use the colors and spacings we have, as well as icons. It also includes some pages about our design principles, naming conventions, and accessibility best practices. Currently, we are in the transition phase, so you can see 2 other design systems next to Helio on this website. Our current goal is to provide all the tools for building and documenting what we have to be able to unify everything in the future.
Some of the things we’ve learned
Working on such a project brought a good amount of knowledge to our team. I wanted to share some of the insights.
Invest in a design system if you didn’t do it yet
Although it might sound like an unnecessary effort for smaller companies (or less tech-y ones), this effort pays off. If you don’t do this soon enough — you will end up with either developer wasting their and the designer’s time on developing the same UI elements or having several non-general-purpose design systems or UI libraries across different projects. The sooner you do it — the easier will the transition be: the less code to refactor, fewer projects/pages to align, and fewer isolated component libraries to unify.
TypeScript is a great tool for a multiplatform monorepo
Even though there are a lot of great tools for the same purpose, we chose to go for TypeScript and so far we had no regrets. It’s not only about the programming language itself (which is again, super easy to work with for people with experience with any general-purpose object-oriented programming language), but it also relies on the javascript ecosystem, which has a great number of tools like npm with over 1.3 million packages, jest (unit-testing framework), storybooks (it’s a frontend workshop for building UI components and pages in isolation, can be used for showing the components and/or UI tests) and many more things like linters and libraries. Working with this ecosystem we feel confident that for whatever integration we need we’d always find an official package that would work.
Make it ready for contributions
A design system with a components library is an ever-changing thing, it will always have more things to be added to it. As you spread the knowledge about the unified design system, more and more people would want to make use of it, but, even if you’ve spent huge amounts of time on each and every component there will always be something that is needed now and there will be people willing to do it. To be most efficient, these systems need to be collaborative. It is very important to make sure in the early stage that the system is ready for contributions from outside of the team/workgroup responsible for it. What we did was make sure every step of the process (of creating a new component) is easy and well-documented. For instance, for making it easy to contribute, we introduced code generators, with which a developer can create a new package with all the boilerplate like package.json, entry file, linting config, and so on by executing just a single command.
Provide the best documentation you can
When it comes to library consumers, make sure to provide them with the best documentation you can. If people struggle to use the library, it will impact the adoption and make people less keen to contribute to the library. The website we created for Helio easily becomes a part of the development workflow: linting and runtime errors point to the website pages, places where people have css variables have links to the website pages where people can see in action colors, spacings, typography, and other tokens.
Always use the latest version
While you should provide tools that work with the existing ecosystem (for instance we decided to start with Vue2 support as we didn’t have any projects migrated to Vue3 yet), try to be on the cutting edge where you can. The migration from vue-cli’s webpack builder to the latest version of Vite is a good example of this, which resulted in build times 7.5 times faster (from 4:21 minutes down to 35 seconds). This is especially important when you take into consideration that a project like this usually requires being rebuilt very often.
Next steps
The design system and its component library already became the de-facto standard for the design team as well as for engineers developing anything new for our apps. One of our next goals is to unify those isolated design systems so that we have to provide a common design language and tooling for the whole company. For sure we will keep developing new components and improving the existing ones: it is very important for the adoption, but we should not fall into a trap of creating something that is intended to be only used in 1 place — these elements should not be part of the design system. On top of that, we always keep exploring tools for improving the design system consumer’s experience, currently, we are thinking about things like Figma plugin to keep the definitions always in-sync as well as some IDE extensions, to make Developer Experience (DX) even better. We are also considering making the core of Helio open-source. Everything that is strictly related to Glovo branding is isolated in 1 theme, so if we’d like to open-source it — we can easily do it. It will provide anyone wanting to develop/integrate a design system with the tooling we already have set up, since it’s a monorepo it will include everything from parsers, generators, linting, unit- and UI-testing to things like the auto-generated website with the documentation and design principles. Let us know in the comments if you’d like to see it open-sourced and what else would you like to have there.