Accelerates the implementation of design tokens in web components in a sustainable and scalable way
With @atomico/design-tokens you can:
Design systems are complex to develop, due to the number of configurations that are shared between all our components, with @atomico/design-tokens we will solve:
Naming problems of the custom properties of your design tokens.
Legibility of your CSS.
For this example we will use Atomico, by the way you can use @atomico/design-tokens with any library.
import { css } from "atomico";
import { compose, tokens } from "@atomico/design-tokens";
const designTokens = compose(
tokens(
{
size: {
xl: "40px",
l: "32px",
m: "28px",
s: "20px",
},
},
"ds"
)
);
export const tokens = designTokens(css``);The result of the CSS will be the following:
:host {
--size-xl: var(--ds--size-xl, 40px);
--size-l: var(--ds--size-l, 32px);
--size-m: var(--ds--size-m, 28px);
--size-s: var(--ds--size-s, 20px);
}This is a technique that I have created to improve the scalability of design tokens, with it you can:
edit token globally using custom properties:
:root {
--my-ds-size-xl: 50px;
}This is also applicable within a selector.
Simplify maintenance, since your components will use the custom properties without a prefix:
import { c, css } from "atomico";
import tokens from "./tokens";
function button() {
return (
<host shadowDom>
<slot />
</host>
);
}
button.styles = [
tokens,
css`
:host {
height: var(--size-xl);
background: var(--color-primary-60);
padding: var(--size-xxs) var(--size-xs);
}
`,
];I am personally a fan of custom properties, but their use would become repetitive, to avoid this and improve maintenance @atomico/design-tokens introduces classes, a generator of utility classes based on the proposed design tokens, example:
import { css } from "atomico";
import { compose, tokens, classes } from "@atomico/design-tokens";
const designTokens = compose(
classes({
size: {
xl: "40px",
l: "32px",
m: "28px",
s: "20px",
},
})
);
export const tokensSize = designTokens(
css`
.gap.--size {
gap: var(--size);
}
`
);The classes middleware will parse the CSSStyleSheet to relate the custom propeprtiy --size as a class of .gap, internally the css will be as follows:
.gap\.xl {
gap: var(--size-xl);
}
.gap\.l {
gap: var(--size-l);
}
.gap\.m {
gap: var(--size-m);
}
.gap\.s {
gap: var(--size-s);
}This makes it really simple to reuse the tokens, example:
import { c } from "atomico";
import { tokensSize } from "./tokens";
function button() {
return (
<host shadowDom>
<button class="gap.xl">
<slot />
</button>
</host>
);
}
button.styles = tokensSize;
customElements.define("my-button", c(button));import {
compose,
tokens,
classes
} from "@atomico/design-tokens";create a pipeline of functions that share the same CSSStyleSheet.
compose(
...middleware: ((sheet: CSSStyleSheet, lastParam:any)=> any)[]
): (firstParam:any)=>sheet: CSSStyleSheet;transform an object to custom properties.
tokens(theme: Tokens, prefix: string);compose(
tokens(
{
size: {
xl: "32px",
l: "28px",
m: "24px",
}
},
"ds"
)
);:host{
--size-xl: var( --ds--size-xl, 32px);
--size-l: var( --ds--size-xl, 28px);
--size-m: var( --ds--size-xl, 24px);
}compose(
tokens(
{
size: {
xl: "32px",
l: "28px",
m: "24px",
},
variation: {
small: {
size: {
xl: "28px",
l: "24px",
m: "20px",
},
}
}
},
"ds"
)
);:host{
--size-xl: var( --ds--size-xl, 32px);
--size-l: var( --ds--size-xl, 28px);
--size-m: var( --ds--size-xl, 24px);
}
:host([small]){
--size-xl: var( --ds-small--size-xl, 28px);
--size-l: var( --ds-small--size-xl, 24px);
--size-m: var( --ds-small--size-xl, 20px);
}It facilitates the reuse of tokens, through the generation of dynamic classes.
classes(tokens:Tokens);import {css} from "atomico";
import { compose, classes } from "@atomico/design-tokens";
const designTokens = compose(
classes(
{
size: {
xl: "32px",
l: "28px",
m: "24px",
}
}
)
);
export const classUtils = designTokens(
css`
.gap.--size{
gap: var(--size);
}
`
);.gap\.xl{
gap: var(--size-xl);
}
.gap\.l{
gap: var(--size-l);
}
.gap\.m{
gap: var(--size-m);
}