How we built a themeable multi-brand design system with Vue.js
Tags: designsystem, vue, javascript, sass
Published: 2022-06-21 09:00:43

At Monisnap our goal is to enable users to support their friends and family abroad by providing remittance services where traditional banking services are unavailable.

To this end, we build products under our own brand but we also have partners who  leverage our expertise to enable their users to transfer money without the complexities that arise when dealing with multiple methods of international remittance.

In 2021 the front-end team at Monisnap built the **Monisnap Interface Kit** (**Moniik** for short). In the following article we’ll go over its creation, from the first meetings to the first projects in production.

For the sake of brevity we are focusing more on the process and reasoning rather than deep-diving the technical details but feel free to get in touch if your curiosity is piqued! 

## Why build a design system ?

The main reasons organizations decide to build a design system are [myriad](https://www.invisionapp.com/inside-design/guide-to-design-systems/) [and](https://material.io/design/introduction) [well documented](https://medium.com/@broccolini/design-systems-at-github-c8e5378d2542). For the team at Monisnap our top priorities were:

- Enabling the tech team to deliver new products under the Monisnap brand in a small time frame while preserving the visual consistency of the Monisnap brand.
- Delivering a complete turn-key remittance product to our partners with a short turn-around time. Our back-end services are stable and require only small configuration adjustments for new partners, we wanted an equally fast process for the front-end.

## The kaleidoscope of design possibilities

The elephant in our design-system-room is of course the "themeable multi-brand" part. Our partners want a seamless experience when their users switch between the partner's main application and the Monisnap application. We need to make our product look and behave like our partner's and follow their brand guidelines. 

But doesn't building a design system, without knowing the full extent of customization that will be required of it before-hand seem at odds with the basic idea of finding common patterns in designs, describing and re-using them?

Is Moniik really a bonafide design system? Maybe not, but we don’t get too hung up about it. We're happy with the productivity we've gained since and it's met our objective of cutting down on development time by orders of magnitude.

For example, here is the same recipient form themed for Monisnap and multiple partners. The screenshot below is from Figma, but with a handful of SASS variables we achieved the exact same thing in our production deployments.

![Variations of the same recipient form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1c2cq2gpg6xcy3cvjzgb.png)

## Goyav, our first step towards a design system.
Before Moniik most of our front-end code was in a single git repository and we created a CSS-only toolkit we called Goyav that we applied to project components as needed. 

This was a great first step towards outlining the components that would be most important to us in the future and thinking about how to make them generic enough for re-use. After several product launches and new partnerships however it became apparent that we were limited by a single repository.

We decided to split our front-end code into multiple repositories which lead us to reconsider how we shared common design elements and UI behaviors.

## Design principals

The guiding principle was that of [atomic design](https://bradfrost.com/blog/post/atomic-web-design/) and we built up the different stages in the atomic design method as separate files: atoms, molecules and organisms.

We also decided to include an extra stage before the atoms: variables (also called [Design Tokens](https://bradfrost.com/blog/post/extending-atomic-design/)). Colors, margins, borders, font-sizes are all set as overrideable SASS variables. These our the first step for customization for a partner.

![The Atomic Design pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91f4p1fd4sv188scs3ty.png)

Moniik doesn’t include template or page layout components, only some helpers. We were afraid that we would spend more time fighting against layout components, and reading their documentation than using the handful of helpers and variables you need for most layouts.

## Getting the ball rolling

In the summer of 2021 we started testing several options for our design system. The only technical decision we made beforehand was to implement it with Vue.js (more on this later).

### CSS Framework

We wanted to hit the ground running so opted to go with an existing SASS/CSS framework. The two contenders we tested exhaustively were Tailwind and Bulma. In the end Bulma fit our use case the best giving us base classes and helpers to build our components, but also allowing us to re-export them easily without the extra build step required by Tailwind.

### Choosing Vue.js.

Vue.js SFCs (or Single File Components) and their surrounding ecosystem allow us to extend and override templates, scripts and styles in consumer projects with ease.

Although everyone on the team has more or less experience with the increasingly industry-standard React, we decided to go with Vue.js for the following reasons:

- SFC’s allow us to swap-out or override styles quickly across a whole project
- Vue.js templating system allows us to override components on a name by name basis. This becomes *very* useful when we want to replace a nested component. For example, we can swap out the `<TextInput>` component that is depended on by the `<PhoneInput>` component without reimplementing the whole `<PhoneInput>`
- Nuxt.js is a great framework and does exactly what we need. SSG, SSR and SPA rendering are all supported.
- Monisnap’s existing front-ends are written in Vue using Nuxt and everyone was satisfied with the experience so far.

However, Vue.js also comes with its fair share of challenges:

- When we created Moniik (and arguably still today) Vue 2 is the most used version and until Nuxt 3 is released and stable we’ll be sticking with it, and missing out on many of the innovations of Vue 3.
- Typescript support for Vue 2 and `.vue` files wasn’t stable enough to allow us to build with confidence. Notably absent at the time was a good solution for command line type checking and loading named exports. For this reason, and because of the relative simplicity of UI component logic compared to the rest of our front-end code, we decided to skip Typescript for now.

## Developing Moniik

### Development environment

Once we were set on technical choices, we built the development environment around the project.

- Jest unit tests to avoid breaking changes in the components' public `props` and `events` API
- Jest snapshot tests allow us to detect and avoid unwanted changes in the rendered HTML.
- Storybook component tests & documentation. As static version of the storybook is also hosted on GitLab for easier references.
- A sandbox project to play with multiple components in ways Storybook does not offer.
- GitLab package registry publishing.

### Component development

Over the next 8 weeks we developed the different components for the design system starting at the atomic level and working our way up to the organisms.

The pipeline we created that each component went through is illustrated by this diagram:

![Component pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ou5cvk5d09euqiquoezg.png)

For each component we created a separate `.js`, `.vue` and `.scss` files, Storybook and Jest test files and TypeScript `.d.ts` declaration files.

![The Storybook page for the Button component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l0a35wp2b29oaaxxm4ca.png)

After the intial HTML + SASS integration, we extracted most CSS property values to variables that can optionally be overriden in consumer projects:

```scss
@import '../../scss/variables';

// Control
$text-field-height: 58px !default;
$text-field-padding-top: 0 !default;
$text-field-icon-size: 24px !default;
$text-field-spacing: spacing(3) !default;
$text-field-font-size: 6 !default;

// Control Border
$text-field-border-radius: $radius-small !default;
$text-field-border-color: $grey-lighter !default;
```

### First projects

Overlapping the end of the component development period, we started our first real project with Moniik. This was crucial to uncovering any issues that only arise when building products in the real world. We fixed some issues with tracking variables and some weird Nuxt compatibility bugs but integration was otherwise very smooth.

In December 2021 we started our first partner project and took three days to add their look and feel to Moniik. This was mainly due to adding SASS variables to values we didn't initially expect to have to modify.

In March 2022 we started our third partner project and with the increased maturity of Moniik’s code base, it took us a single afternoon to customize for our partner.

## Moving forward

Moving forward we have the following challenges:

- Better CSS loading: Within the given timeframe to develop the Design System we paid less attention to CSS performance than code clarity. There is a good amount of unused or redundant CSS that we would like to remove eventually.
- The move to Vue 3 & TypeScript. Like many, we are huge fans of TypeScript and would love to have a homogenous TS codebase across all our projects.

We’re really happy with how Moniik has turned out and the projects it has allowed us to implement. We’re looking forward to building many more over the coming years while progressively improving the developer experience. If you would like to help us, [take a look at our careers page](https://www.welcometothejungle.com/en/companies/monisnap/jobs)