Style-dictionary: Custom value transforms skip values with aliases

Created on 8 Aug 2020  Â·  8Comments  Â·  Source: amzn/style-dictionary

Existing related issues

Maybe #208, although that seems like a larger architectural discussion.

Environment

style-dictionary --version
2.10.1

node -v
v12.16.1

npm -v
6.13.4

Test case

Given the following config.js:

// configure Style Dictionary with default & custom configs 
// https://github.com/amzn/style-dictionary/issues/344#issuecomment-543285393
const StyleDictionary = require('style-dictionary');

// defines a custom transform for CSS variables
// to translate aliases `{vds.custom.value}` into CSS vars `var(--vds-custom)`
// https://amzn.github.io/style-dictionary/#/api?id=registertransform
const alias_syntax = /\{([^\}]+)\}/gm;
StyleDictionary.registerTransform({
  name: 'css/custom_vars',
  type: 'value',
  matcher: function(prop) {
    if (alias_syntax.test(prop.original.value)) {
      console.log(`:) MATCHING ${prop.original.value}`);
      return true;
    } else {
      return false;
    }
  },
  transformer: function(prop) {
    console.log(`~ TRANSFORMING ${prop.original.value}`);
    return prop.original.value;
  }
});

StyleDictionary.registerTransformGroup({
  name: 'css-vds',
  transforms: [
    // custom transforms registered above
    "css/custom_vars",

    /* css default transform group = 
     * attribute/cti name/cti/kebab time/seconds content/icon size/rem color/css 
     * https://amzn.github.io/style-dictionary/#/transform_groups?id=css
     **/
    "attribute/cti",
    "name/cti/kebab",
    "time/seconds",
    "content/icon",
    "size/rem",
    "color/css",
  ]
});

module.exports = {
  "source": ["lib/styles/style_dict/tokens/**/*.json"],
  "platforms": {
    "css": {
      "transformGroup": "css-vds", 
      "buildPath": "lib/styles/build/css/",
      "files": [{
        "destination": "design_tokens.css",
        "format": "css/variables"
      }]
    }
  }
}

And the following tokens.json:

{
  "vds": {
    "font": {
      "scale": {
        "base": { 
          "value": "1.6rem"
        },
        "multiplier": {
          "value": "1.25"
        }
      },

      "size": {
        "md": {
          "value": "{vds.font.scale.base.value}"
        },
        "sm": {
          "value": "calc({vds.font.size.md.value} / {vds.font.scale.multiplier.value})"
        }
      }
    }
  }
}

Actual behavior

I see the following output:

style-dictionary build --config=./lib/styles/style_dict/config.js


css
:) MATCHING {vds.font.scale.base.value}
:) MATCHING calc({vds.font.size.md.value} / {vds.font.scale.multiplier.value})
✔︎  lib/styles/build/css/design_tokens.css

Note how only the MATCHING lines are logged…

Expected behavior

I _expect_ to see the following:

style-dictionary build --config=./lib/styles/style_dict/config.js


css
:) MATCHING {vds.font.scale.base.value}
~ TRANSFORMING {vds.font.scale.base.value}
:) MATCHING calc({vds.font.size.md.value} / {vds.font.scale.multiplier.value})
~ TRANSFORMING calc({vds.font.size.md.value} / {vds.font.scale.multiplier.value})
✔︎  lib/styles/build/css/design_tokens.css

Note how both MATCHING and TRANSFORMING lines are logged…

Changing the registerTransform option from type: 'value' to type: 'name' or type: 'attribute' gives the expected behavior.

Is this intended?

Most helpful comment

This is "working as expected" right now, but something we plan to fix in the next major version. Right now the "build" happens in a few distinct steps. The relevant steps to this are: transforming the tokens and then resolving aliases/references which are done in 2 passes of the entire style dictionary object as seen in this build diagram. Notably the transform step skips doing any value transforms on values that reference other values. Here is the line of code that does that: https://github.com/amzn/style-dictionary/blob/main/lib/transform/property.js#L36 The original intent was that the aliased token should already be properly transformed for the given format.

Doing it this way does make it impossible to do certain things like output a variable reference in an output file, --color-a: var(--color-b); for example or modify a value like darken a referenced color. It also doesn't fit the mental model people have about how the build process works. So we are fixing that in the next major version. We have a proposed solution already, we just need to do some more testing and branch clean up and other work for the next major release.

All 8 comments

I came here to report this too—I was trying to do the same as #452 (piece together a token from an opaque color and an alpha value), but found that if I pulled the rgb from the opaque token via aliases, the color transforms weren't being applied. After logging all the transforms and scratching my head, it seems that aliased values just _aren't_ transformed, even though they match the matcher given.

This is "working as expected" right now, but something we plan to fix in the next major version. Right now the "build" happens in a few distinct steps. The relevant steps to this are: transforming the tokens and then resolving aliases/references which are done in 2 passes of the entire style dictionary object as seen in this build diagram. Notably the transform step skips doing any value transforms on values that reference other values. Here is the line of code that does that: https://github.com/amzn/style-dictionary/blob/main/lib/transform/property.js#L36 The original intent was that the aliased token should already be properly transformed for the given format.

Doing it this way does make it impossible to do certain things like output a variable reference in an output file, --color-a: var(--color-b); for example or modify a value like darken a referenced color. It also doesn't fit the mental model people have about how the build process works. So we are fixing that in the next major version. We have a proposed solution already, we just need to do some more testing and branch clean up and other work for the next major release.

Cool. For now since we're just doing web, I can use my workaround and I'll eagerly await v3 😎 Thanks @dbanksdesign !

I'm still using type: 'name' in my local transform to get what I need.

+1.

@dbanksdesign thanks for explanation.

It would be really handy to transform resolved value. So I'm looking forward to this feature.

This should work with the coming release of Style Dictionary 3.0. I will leave the issue open until the release occurs, but wanted to give you all a heads up that a solution was merged into the codebase with #371

For those curious and want to get access to this right now, you can install the upcoming release of style dictionary with the next tag: npm install style-dictionary@next.

Here is an example using transitive transforms in the next version of Style Dictionary: https://github.com/amzn/style-dictionary/tree/3.0/examples/advanced/transitive-transforms

And this documentation page has a more detailed explanation of upcoming changes in 3.0: https://amzn.github.io/style-dictionary/#/version_3

This is now released as part of 3.0 via #636

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nhoizey picture nhoizey  Â·  4Comments

chazzmoney picture chazzmoney  Â·  4Comments

gael-boyenval picture gael-boyenval  Â·  3Comments

custa1200 picture custa1200  Â·  5Comments

chazzmoney picture chazzmoney  Â·  4Comments