How we build an almost perfect design system at Affise

8 min readJun 23, 2022


Ten years ago, interface design boiled down to sprucing up the pages. Now, everyone seems to understand the value of product design. To a greater extent, this broad concept relates to a product’s ecosystem and users’ vision and understanding of it. Modern design is about being “convenient” and “quick” rather than “good-looking.”

In this article, I’ll show how we at Affise build a perfect and easy-to-understand design system, enabling us to get things done quickly and prevent mistakes. Also, I’ll walk you through the Affise design changes by demonstrating a few before and after samples. To make things more transparent, I’ll use Lego as an example.

What do design and Lego have in common?

As a kid, I could spend hours building Lego castles and spaceships. My parents used to give me a new Lego kit for almost every holiday. Thus, I’ve got a considerable collection that included both mini builds and projects requiring several days to assemble.

All these sets ended up being mixed in a huge box, from which I took details to create my own designs. I used a specific set of components from a wide variety of details. The more kits and details I got, the more cunning models I could build. For the past ten years, I have been doing the same things in product design.

The most important component of each Lego box is building instruction. Of course, you can build something small and easy without it, but you definitely won’t cope with a big project. You have too many parts; how do you know when to use each?

Like a huge Lego kit, the design includes many elements; each works at the right time and place. Without a well-developed design system or set of rules and principles, everything will start to crumble and fall apart.

Building large Lego projects requires sticking to specific principles. You start with small items and use them to create larger parts; then, you combine them to get the whole object.

Designers rely on a similar methodology called atomic design. The smallest components, such as icons, work as atoms. When connected, they form bigger elements, e.g., buttons, cards, or table cells. These parts, in turn, can build molecules: tables, groups of cards, or pop-ups. But why use this nesting?

Imagine you are a designer who just got the task to enlarge the buttons in 50 pop-ups. Nesting will let you alter only one button and apply changes to all pop-ups instead of adjusting each manually. Sounds good, isn’t it? Sure, until you encounter the limitations of a seemingly logical atomic design system. The biggest roadblock is the number of nested elements.

What makes Affise macro design better than atomic design?

We encounter products with very rich functionality when designing complex reports for Enterprise clients. Our main task is to simplify visualization and highlight specific parts. Often, there’s no way to get by with two or three nestings of elements when working with complex interface projects.

Why use more nestings? It’s all about the variety of options and the willingness to minimize design errors. Many nestings allow us to automate component types and add more rules. Let’s see why the atomic design system doesn’t suit us. Here, you can see a diagram of a classic atomic model:

The classic model is limited to five levels of nesting, whereas only pop-ups in our system include that much. But what if each level could have more nestings? Thus, we came up with this scheme:

We have three primary levels: macro-, micro-, and component levels. The macro-level acts as a plug-in library with shortcuts to our design system; it includes text styles, colors, and effects. Also, here we have macro-elements or single-cell components like avatars, badges, combinations of icons and device names, and mini-tumblers. In Lego, these would be bricks.

Next comes the micro-level, which stores all kinds of more complex details. They are combinations of macro-elements, texts, and styles, as well as intersections between them.

What does it mean? For example, on the first level, we have a button micro-component that includes an icon, text, and color taken from the macro-level. Each of these button components can be present in many variations and conditions.

On a second level, any button can become a part of another micro-component, e.g., the bottom menu:

This micro-component, in its turn, can become a part of a pop-up micro-component:

After filling in the details, the pop-up becomes a component — a part of the third level of our structure. Eventually, sets of components form sections, pages, or final layouts.

Let’s go back to the micro-level. Inside the micro-components, you can see the inscription “Nests.” It means that the micro-component has its own nesting level, maybe even more than one. To understand it better, we need another example — filters.

Let’s explore nesting levels

The picture shows the final variation of the pop-up filter menu that appears after clicking on any filter inside the table. This is a component that we use in layouts. Let’s look at what we have under its hood.

Boy, what a scheme! Our final component is on the left side. The blue area in the middle is a micro-level, and the right side is a macro-level. You can see how the filter window is getting divided into parts, and these parts split into smaller ones, and so on, just like in a Lego castle. The numbers next to the components’ names indicate the nesting level.

In the corners, you can see the attributes of each nesting level. Thanks to so many nestings, we can dive into the main component one level lower and make swift adjustments at any time. For example, checkboxes can be changed to radio buttons, the search bar deleted, or the filter menu hidden with a single click.

Here it is, the wide range of just one filter’s variations. But variety is even wider if we dive into the component and start changing attributes.

Let’s look at how it works from the interface design perspective. By changing only one macro-element, I get 277 out of 500 elements altered at once. I have to wait a while until the changes are applied everywhere, but the chances of making a mistake come to zero. To eliminate errors at all, we came up with a few more rules.

Our rules for avoiding mistakes

The first rule is to inscribe names in the components. For instance, we determine all possible statuses and use them to avoid mistakes when choosing the right color and status name combination. Also, we store all actions or filters and assign an icon for each. Thus, the cancel button corresponds only to the cross mark and not any other icon.

Another safety measure is using nests to unify components and minimize errors. We already talked about it, but what does it mean? Let’s get back to our pop-ups:

We made a rule to use only four typical pop-up sizes, as the fewer entities you have, the easier it is to interact with an end-user. To help developers and designers eliminate mistakes with the pop-up component sizes, we assigned these sizes to one of the nesting levels. It means we start with choosing a suitable size and then inserting the component’s filling. As in Lego, we first build the castle walls and then insert tiny houses and add little men.

We refused old-school modular grids and use Autolayout, Fill container, and Left & Right frames instead. Each of our elements stretches to any size without distortion. It lets us check how much information can fit the different screen resolutions.

How does all of this affect Affise?

We have built a design system that saves us time and money; it’s intuitive for managers, developers, and designers. Apart from it, we have also reapproached many user mechanics in the product itself.

We’ve tested this design system on our new project Affise Reach and now working on extending it to our classic product, Affise Performance. Also, we’re updating current functionality to improve the visualization and shift the focus to crucial parts of the interface. To that date, we have reworked more than half of the sections of our service.

Our main task is to identify patterns presented in all sections of the service and alleviate users’ interaction with them. Let me share an example. This is what the offers section looked like before the redesign:

This is how the section looks now:

In the future, we want to make this section similar to what we have in Affise Reach.

We focus on improving the table’s readability and alleviating the user’s work with data filtering. To achieve it, we implement additional functionality that affects the display of the table and the possibility of customizing the visualization. Users can now zoom in or out and edit column width.


Building design systems is not just a joy of solving engaging logical tasks and accepting new challenges. It is a way of optimizing designers’ and front-end developers’ work. When your design system is almost perfect, it becomes easy to build a layout from ready-made parts according to the instructions.

But as we know, there’s always room for improvement. Figma is not fully capable of meeting our needs yet, so we look forward to changes and tweak our system with every update. Each change makes our life easier, and layouts are getting faster to create.

By the way, I still have a lot of fun playing with Lego!

by Ilya Garifulin, Lead Product Designed at Affise.