Blog author's portrait
Artur Tagisow
Vue dev from Eastern Poland

Lessons learned after working implementing a design system/component library in web tech

1. Ask your boss to assign a business analyst to help with managing the requirements

There are too many things to keep track of and business analysts are a godsend in cases like these. This is especially true if another company is creating the mockups (Figma etc.) and you’re the developer who creates views/components in accordance with the design system. In in that case - communication is even harder (e-mails, different Teams organizations, official meetings).

Business analysts have a better eye for things like: * “we’re not gonna do that, that’s a change request”

As a developer, the answers you feel comfortable answering sound like:
* “oh that’s not possible technically” * “oh yeah that’s easy to do, I can do that in 2 hours” * “oof if you want me to do that that’ll take like 15 hours and and a re-test of basically the whole app from the QA team”

2. Use a ticket tracking system

This is “obvious”. You may tell the designers, “well, can you add a Figma showing how text overflow looks on this dropdown?” and you may trust them that they’ll add it.

But man, shit like this piles up fast - and when it does, none of the critical shit you ask for gets added.

Create a ticket for this so you can hold people accountable to things.

3. Don’t use Figma

Figma is: * sloow * links to frames for sharing with co-workers don’t work - they redirect you to nowhere, leading to confusion

3.1 … because you can’t track changes

Our Figma workspace looked like this:
* the company implementing the style guide had read and write access to the workspace * we’ve had only read-only access

This is obviously standard.

But the Figma workspace is editable at any time, even if we’re in the middle of implementing something.
It’s a nightmare trying to figure when you get a bug report that eg. you didn’t add a field to some form, because there are two possibilities: * the field didn’t exist during implementation, so you’ve done everything according to the mockups, and adding that field is a change request * the field DID exist on the mockups before implementation, but the developer missed it and it somehow didn’t pop up in code review

You may want to “freeze” the workspace so no changes are made. This is not officially supported. Sure, you can duplicate the workspace, but do you think having 3-4 worksaces representing the component library at different moments in time is feasible? Propagating “which Figma is the most recent” to developers would be a nightmare

Designer rant

I’ve noticed that … the designers I’ve worked with don’t really use Figma components. They know about them, but they don’t want to use them.

Like legit, there’ll be like 60 mockups that have one string. Then some business analyst comes along and says “Hey, Designer, the client said we don’t want to use that one string anymore, please change it to string two”

Then the designer needs to go through 60 mockups and manually change the string.

At one point it became a problem because the designers said “oh we’ll just remove those one strings as we see them, no way we’re going through 120 mockups right now and fixing that” or “ok we’ll just change it in this one mockup and we’ll leave the old one in the 119 others, but refer to the fixed one please :)”. And of course they missed some, which led to developers that used the mockups to implement features asking “hey, why is string one there, and string two in the other mockup there? Which one should I use?”.

But the same thing was with components - it’s not like the 30 instances of text inputs in the Figma workspace have one common Figma Component ancestor. No, they’re all duplicated and free-form.

We told them “Hey, I don’t work with Figma, but can’t you use the Figma Components feature for things like that? You could change it in one place and it’d change verywhere”, to which a designer team lead responded “Ohh, naah please don’t make us work that way”

To me that sounds just like “I don’t want to use components because I don’t want to be forced to be consistent”. Figma has features that mimic CSS flex etc. so you could design a component and imbue it with all the rules of how text wrapping/overflow etc. should work, but you don’t? Because it’s easier to just not use components, duplicate everything and be allowed to slap some text wherever you want, while not being commited to being consistent?

Duplication seems like a problem for designers in general. What if you have 200 mock-ups, and the client suddenly says “Hey, can you remove this field everywhere in the forms?” And you need to look in 200 mock-ups to remove them. It’s not like in programming where you can “Go To References” and manually remove things. I’m wondering if there’s some tagging system/autogeneration system that’d help designers alleviate that.

4. Technical stuff

4.1 Use global classes as foundation for your typography components

TODO: Oh, wait, this is called “styled components” and people actually do this in the industry? https://github.com/styled-components/vue-styled-components

Global classes:

<div class="text-big text-bold text-style-link">
  Helloooo
</div>

Components:

<my-text-component font-weight="bold">
  Hellooo
</my-text-component>

Obviously you could always use classes (and duplicate the div with the same 3 classes several hundred times in your app), but putting those 3 classes into a component is more opinionated, and prevents people from creating class combinations that don’t exist in the Design System.

So with the above “global classes, but components for convenience” your hierarchy looks like this: * global classes * components

But the grave mistake is when your hierarchy looks like this: * components

In other words, “using the <ds-link-text> component is the only way of making some text look like a link”.

This is bad because: 1. When designing the application, designers seem (from my experience) to think about the Design System as a box of puzzle pieces.

Aha, so I have: 4 colors, 3 font weight variants, 7 different fonts and I can mix and match them however I like

This plays nicely if you have a “global classes > components” hierarchy, because the designer’s “puzzle pieces” literally translates into classes.

Aha, so I have: 4 classes for colors, 3 classes for font-weights, 7 classes for fonts and I can mix and match them however I like

But if you use just components, you’re commiting to the notion that

This text will ALWAYS look like this - it’ll only have 3 font weights and 3 colors. Listen, I know we’ve been working with this Figma only for 2 weeks and none of the UX acrobatics in the more complex components have reared its head, but dude trust me, this text will always have those 3 colors lmao

And then it turns out that the color token from the Design System that was never used on text in any Figma file suddenly appears on text in some complex table component because it’s the only way that made sense. The designer didn’t do anything wrong - the Design System allows what he’s done. But now you have a problem.

You created your Text component based on the basic cases the designers have shown you and thought the designers will use only that. But the designers thought in terms of puzzle pieces, not set in stone components. As an anectode: today, I got several issues on JIRA from the accessibility auditor saying “Hi, please change this <h4> tag into a <p> tag, but please keep the size, color and font-weight exactly the same”. It’d be fine if we didn’t use components for headers, but since we do, we have <ds-title level="4"/> that we need to turn into <ds-title level='4' as="p"/> (as is a custom prop that changes the tag - level=4 without ‘as’ displays a <h4> )

When I first saw our Design System Figma workspace, I saw a whole page frame dedicated to just what links should look like. They had a text color that matched a color token from the design system and were underlined. I thought - great! I’ll make a ParagraphLink component, which is just a span with a class (not <router-link>, or <a>) and thought it was it. But then they decided to redesign the footer. The footer had links. They looked nothing like link I used as reference when creating ParagraphLink from the “whole page frame”.
Since the footer was white, the links now were dark grey, and had no underline. Again, I shot myself in the foot because I thought in “commiting to a consistent standard” using the ParagraphLink component. I should’ve thought in terms of puzzle pieces and free classes.

So now instead of just using a text-color-newfancycolor class just for that edge case, you’re forced to modify your Text component to support the new color.

There are many things you can count on, but you can’t count on a designer to be consistent.

To build and manage those global classes, you can use WindiCSS or TailwindCSS.

  1. Components add a wrapping <div> node
    If you use JUST components for eg. setting text style, you’ll do this a lot: html <!-- AccessibleOutlineWrapper.vue --> <!-- Adds orange wrapper on :focus-within --> <div class="scoped_accessible_outline"> <slot/> </div>

    This is OK until you do this:

    <!-- ComponentLibraryIcon.vue -->
    <AccessibleOutlineWrapper>
      <span :class="iconClass" tabindex="0" @click="handleClick">
        {{icon}}
      </div>
    </AccessibleOutlineWrapper>
    

    Now if you eg. put the icon in a flex container, the AccessibleOutlineWrapper’s div will be resized according to rules of the flex container, so the containing span may now be out of alignment.

    If you want to fix that, you’d have to do the obvious stupid thing:

    <ComponentLibraryIcon class="my_fix_for_flex"/>
    
    .my_fix_for_flex > span { // span = icon
      // fix flex misalign here
    }
    

    It’d be much easier to apply some global classes that define what the outline looks like for the icon component, because then you only have one nesting level.

So basically, using just components that have encapsulated styles inside is putting too much of a constraint on yourself. It’s comitting to extreme consistency, where whether that consistency will exist is not under your control. It’s under control of your designer.

Global classes allow you to escape the components you normally use and create something from scratch using the very same puzzle pieces the designer used.