DEV Community

Cover image for How to apply Design Tokens in WebComponents with customProperties
Matias Trujillo
Matias Trujillo

Posted on

How to apply Design Tokens in WebComponents with customProperties

Let's start by imagining that you already have your design system with the design tokens already identified in a tool such as Adobe XD, Figma or another and now you are in your last step developing the code and it looks like this:

class MyAwesomeButton extends AnyLibrarie {
  static styles = css`
    button {
      background: var(--my-awesome-ds-button-background);
      color: var(--my-awesome-ds-button-color);
      border: var(--my-awesome-ds-button-border);
    }
  `;
  render() {
    return html`<button><slot></slot></button>`;
  }
}
Enter fullscreen mode Exit fullscreen mode

is this correct? maybe, can it be better? Of course with properly implemented Design Tokens(Heck if I was wrong correct me in the comments).

From here I will use Atomico a 3kB library to create WebComponents using functions, do you have to know Atomico to continue?, No, since we will write standard JS and use only 3 functions:

  1. c: transform your function into a custom elements.
  2. html: create our HTML template
  3. css: create our CSS.

Let's continue!

How to improve your design tokens?

First create an archive called tokens.js and write the following code

import { css } from "atomico";

export const tokensInput = css`
  :host {
    --background: var(--my-awesome-ds-input--background, black);
    --color: var(--my-awesome-ds-input--color, white);
    --border: var(--my-awesome-ds-input--border, 1px solid tomato);
  }
`;
Enter fullscreen mode Exit fullscreen mode

With the above code you have gained something amazing✨:

  1. You simplified the name of the internal custom properties to be used by your components. Every component that imports tokensInput must not know that ugly prefix --my-awesome-ds-input--background, for your components it will only be --background 😎
  2. Instance-level customization of your component.

Before continuing I want to highlight the name of our tokensInput variable is not called tokensButton and this will depend on your design system, for me the properties between the button and the input are usually generic.

How to use your design tokens?

Step 1, import dependencies

import { c, html, css } from "atomico";
import { tokensInput, tokensColors } from "../tokens";
Enter fullscreen mode Exit fullscreen mode

Step 2, create our component

function button() {
  return html`<host shadowDom>
    <button><slot /></button>
  </host>`;
}
Enter fullscreen mode Exit fullscreen mode

Step 3, associate our tokens and use them

button.styles = [
  tokensInput,
  tokensColor,
  css`
    button {
      background: var(--background);
      color: var(--color);
      border: var(--border);
    }
  `,
];
Enter fullscreen mode Exit fullscreen mode

Now our code looks like this

import { c, html, css } from "atomico";
import { tokensInput, tokensColors } from "../tokens";

function button() {
  return html`<host shadowDom>
    <button><slot /></button>
  </host>`;
}

button.styles = [
  tokensInput,
  tokensColor,
  css`
    button {
      background: var(--background);
      color: var(--color);
      border: var(--border);
    }
  `,
];

customElements.define("my-awesome-button", c(button));
Enter fullscreen mode Exit fullscreen mode

There is something beautiful in using this technique that I share with you and it is that the custom-properties of the component are not leveraged to a prefix of our design system at the component level, thus achieving a more sustainable code, example:

<my-awesome-button style="--background: gold"></my-awesome-button>
Enter fullscreen mode Exit fullscreen mode

The above is only introductory, I invite you to learn more about Atomico and the entire stack of useful tools at the time of developing with webcomponents.

Discussion (0)