How to start Coding up your Design System in Vue

Maximilien Monteil
7 min readJan 31, 2020

--

Imagine this, you create your web app for this new epic idea and you implement the core functionality. The design can come later you think.

Well later is now, you’re gaining traction and getting feedback, the time has come to overhaul or completely recreate your design!

In this article I’ll go over the tools and methods you can use to achieve just that.

I personally fell into this situation with my web app Mylo, a workout management application. As it grew and came into contact with users, issues arose with color contrast, inconsistencies, and more.

Fixing these issues can be described in two pillars:

  • Design System (how everything will look)
  • Implementation (how to apply the look)

(I focus on look here but understand that design is only about 20% look, the rest must be considerations about the user experience)

Going through this has taught me a lot but also made obvious how much more there is to learn. If you find any mistakes or have some tips please let me know :D

I’ll be using TailwindCSS in this article but everything applies just as well without.

Pillar 1: Design System

The first step, which is all the rage these days, is having a design system. A design system is essentially a visual codebase of your software, and just like a codebase, it is a complete and specific description of what the application should look like under almost any circumstance.

And so, the more you look into what makes up a design system, the more it feels like an impossible task. A complete design system involves the colors, spacing rules, text styles, buttons, containers, branding, accessibility, and so much more.

The best and most exhaustive resource I’ve found is the Design System Checklist.

Therefore I feel more comfortable refering to what I have as a design library with loose guidelines. It works out because I can just refer to myself for any design questons ;)

So we’ll be going over how to implement elements like buttons, icons, colors and a few input types.

Pillar 2: Implementation

Directory Structure

Coding up design elements is amazing for reusability and consistency but it isn’t very useful if components are all across the app in random, hard to access locations. We want them organized.

I recommend putting the smallest/atomic elements into the src/components folder and then into their own subfolders (icons, inputs, buttons, etc.)
Compound components, built out of the smaller ones, can be placed into the src/layouts folder, again with their own subfolders.

Colors

Defining and enforcing colors is a good first step.

You generally have a clear discrete set of colors that your app allows, to bring them in you can either modify the TailwindCSS config file or add them as CSS variables.

Here is how it looks in TailwindCSS, I overwrote the default colors to enforce the use of the system colors but you can also extend the default theme and add your own colors.

TailwindCSS modification to add your own theme colors

TailwindCSS Docs — Theme Configuration

If you’re not using tailwind this can also be achieved using css variables like so:

Standard CSS to add your own colors as variables

Icons

First off I recommend using SVG icons because of how configurable they are. You can change their size without any quality loss, dynamically change their color, and their file size is generally smaller than an equivalent png/jpg.

Getting the actual SVGs can be done through the export options of design tools like Figma, Sketch, or Illustrator.

Once you have the files you can further optimize them with SVGO, there is a command line tool and a web based one.

Both work automatically by pointing them to the file though the web version makes the available options more accessible. Make sure to have a look at the final result to make sure your icons still look fine.

Then we bring the icons into our Vue app, I used a method recommended in the Vue Cookbook. It’s a system made by Sarah Drasner, the SVG queen, and you can find the link for it here.

To make it work with TailwindCSS, you’ll need to make a couple of changes:

Since SVGs themselves are rather light, it felt like a lot of overhead to use full components, so I made some further changes to make use of functional components, you can check out my fork here:

Buttons

Initially I wanted to bring in buttons the same way as with icons, using Vue components but that ended up being deceptively complicated. The component had to work with buttons, links, or a router-link (using vue-router).

Supporting links was important for accessibility and semantics as links are meant to take you to another page whereas buttons should not.

As a solution I extracted the common classes into their own utilities in TailwindCSS, which in pure css is just a normal class rule.

Some examples:

Modifying TailwindCSS utilities

Text Inputs

For inputs we can use Vue components but there are a few things to take into consideration.

Our wrapper components need to be lightweight and transparent, we can do that by using functional components and attaching all attributes and event listeners.

I also took the chance to include the label into the component. It fits the design, is more accessible, and ensures I never forget them.

Start off with a BaseInput.vue component:

And here is an example use of BaseInput.vue:

Notice how we call the BaseInput.vue component. Surprisingly, imported components are not exposed in functional components when using the template format. So instead we place the imported components into injections. They could also placed into props if you prefer.

This method was brought up in this github issue:

Radio Buttons

After all the elements we’ve done so far, Radio Buttons are not too different. The difference is that styling them can be more involved. While it is possible to use standard buttons instead, I wanted to use the default radio buttons, again for semantics and accessibility.

The trick I found was to use the behavior of labels wrapping radio buttons.
The buttons by themselves are small and hard to touch/click but if you wrap them in a label, clicking anywhere on the label box will also select the radio button.
Using this, I styled radio buttons by actually making the label look as I wanted and hiding the radio buttons inside the label.

Be careful when hiding the radio button as it still needs to be visible to screen readers, tailwind offers a class for this, in standard css that looks like this:

As for the v-slot and all the ORs (||), I explain those in the next section about select inputs.

A really helpful resource that also goes over checkboxes:

Smashing Magazine — Creating Custom Inputs in VueJS

Select Input

The select component is a fun one to wrap both in terms of design and functionality.

Design wise it was surprising to discover how “hacky” it is to change the default downward arrow. There are a few ways to do it but the trick I went with is to remove the default style by setting appearance: none; and then bringing in my SVG of choice with the URL function of CSS.

To do something similar you will need to encode your SVG tag into a URL compatible string, I found this site to do just that:

Then there are a few more positioning and spacing styles to place the icon where you want.

For functionality, the end user should retain control over how the drop-down values are displayed, the go to solution is to use scoped slots. With this method our component can support any array of values.

This is because the official Vue doc shows examples using a String Array and an Object Array to populate the select.

These wrapper components also offer the chance to enforce best practices like a label for an input and an empty disabled first option for better iOS support.

Conclusion

Well you’ve reached the end, this covers all the components I brought in from my design library. It really just scratches the surface of what is possible and I couldn’t hope to be fully exhaustive. Nonetheless, I hope this helped you out and encouraged you to tackle that redesign or even just start to design!

The next step after wrapping all these input fields would be to compose them into a form or other layout for reuse, these are some of the components you’d place into the src/layouts folder. You can expect a follow up article once I finish it up myself :D

Feel free to leave any comments below and if you have questions or want to follow up, you can find me on twitter @MaxMonteil

Other helpful links

Originally published at https://dev.to on January 31, 2020.

--

--

Maximilien Monteil

My goal is to solve problems people face with beautiful, practical, and useful solutions. I’m driven to apply and share what I learn.