nuxt-modules / design-tokens

Design tokens module for Nuxt
https://design-tokens.nuxtjs.org
MIT License
50 stars 3 forks source link

Tokens binding optimisation #10

Closed Tahul closed 2 years ago

Tahul commented 2 years ago

Aliases resolving

We currently use native {aliases.keys} resolving in Style Dictionary.

This resolving leads to replacing the {alias} by its value in the built output.

Leading to this:

colors: {
   primary: 'blue'
},
border: {
  sm: '1px solid {colors.primary}'
}

Generating this:

:root {
  --colors-primary: blue;
  --border-sm: 1px solid blue;
}

As the project grows, the general expectations from what we can/should build with tokens is growing and so these patterns can be optimized to our use-cases.

I think the output from the example configuration above should be this:

:root {
  --colors-primary: blue;
  --border-sm: 1px solid var(--colors-primary);
}

Shorthands

Currently, we are creating a CSS variable PER token (objects with value) in the whole tokens.config object tree.

Which means this:

{
   sizes: {
      sm: '16px'
   },
   components: {
      block: compose({
         borderRadius: '{sizes.sm}'
      }),
      icon: compose({
         borderRadius: '{sizes.sm}'
      })
   }
}

Will lead to this:

:root {
  --sizes-sm: 16px;
  --components-block-border-radius: 16px;
  --components-icon-border-radius: 16px;
}

As we are also trying on creating an API to easily create live-editable components (compose()), it seems obvious we'll need to have a better approach to this.

There is currently 2 entry points to access your tokens in your app.

$dt() helper, and @component('component.key');

These helpers both has in their resolving, access to full tokens object, processed by Style Dictionary.

That means we should easily be able to change these built outputs into generating only that:

defineTokens({
   // Globals
   colors: {
      primary: 'red'
   },
   sizes: {
      sm: '16px'
   },
   borders: {
      base: '1px solid {colors.primary}'
   },
   components: {
      block: compose({
         // Aliased token
         borderRadius: '{sizes.sm}',
         // Aliased token
         border: '{borders.base}'
      }),
      icon: compose({
         // Aliased token
         borderRadius: '{sizes.sm}',
         // Specific token
         border: '1px solid blue'
      })
   }
})

// generated tokens.css
:root {
  --sizes-sm: 16px;
  --colors-primary: red;
  --borders-base: 1px solid var(--color-primary);
  --components-icon-border: 1px solid blue;
}

// generated tokens.ts
export const aliasedTokens = {
  'components.block.border-radius': 'var(--sizes-sm)';
  'components.block.border': 'var(--borders-base)';
  'components.icon.border-radius: 'var(--sizes-sm)';
}

// Usage
$dt('borders.base'); // 1px solid var(--color-primary);
$dt('components.icon.border'); // 1px solid blue;
$dt('components.block.border-radius'); // var(--sizes-sm);

@component('button');
border-radius: var(--sizes-sm);
border: var(--border-base);

@component('icon');
border-radius: var(--sizes-sm);
border: var(--components-icon-border);

By having this aliased tokens map, but preserving the tokens definition "complete", we can eliminate the overhead of having a lot of variables declared, while preserving the capacity to be very specific at what we can live-edit from our editor.