How to apply Design Tokens in WebComponents with customProperties
Matias Trujillo
Posted on September 5, 2021
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>`;
}
}
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:
-
c
: transform your function into a custom elements. -
html
: create our HTML template -
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);
}
`;
With the above code you have gained something amazing✨:
- 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
😎 - 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";
Step 2, create our component
function button() {
return html`<host shadowDom>
<button><slot /></button>
</host>`;
}
Step 3, associate our tokens and use them
button.styles = [
tokensInput,
tokensColor,
css`
button {
background: var(--background);
color: var(--color);
border: var(--border);
}
`,
];
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));
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>
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.
Posted on September 5, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.