Style-dictionary: Creating utility classes from tokens

Created on 26 Jun 2019  路  4Comments  路  Source: amzn/style-dictionary

I'm trying to automate the creation of some utility classes from my design tokens. Below is a small example of what I'm trying to achieve.

{
  "color": {
    "white"   : { "value": "#FFFFFF" },
    "black"   : { "value": "#000000" }
  }
}

Outputs

.color-white {
  color: #FFFFFF;
}
.color-black {
  color: #000000;
}
.background-color-white {
  color: #FFFFFF;
}
.background-color-black {
  color: #000000;
}

I plan to have a list of definitions which help facilitate the creation of these utility class. Below is an example.

var utilities = [
  {
      "name": "color",
      "token": "color",
      "props": {
          "color": "$value"
      }
  },
  {
      "name": "background-color",
      "token": "color",
      "props": {
          "background-color": "$value"
      }
  }
]

My thought was that I would iterate through the definitions matching them against the tokens.

However, the problem I'm having is that when I create a custom format I can only seem to do "one thing" on each token.

StyleDictionary.registerFormat({
  name: 'utilityClass',
  formatter: function(dictionary, platform) {
    console.log(dictionary);
    _.each(dictionary.allProperties, function(prop) {

      var duplicatePath = prop.path.slice(0)
      path.pop();
      var newPath = duplicatePath.join("-")

      _.each(utilities, function(utility) {
        if (utility.matcher == newPath) {
          var utilityClass = utility.name + "-" + prop.path[1]);
        }
      });
    });
  }
});

I can create a utility class for each token but I can only return as many results as there are tokens. I don't know how to iterate through the utility definitions and return the appropriate output.

I then thought it would be easier if I could dynamically create new tokens using a custom transform and then use a custom format to create the utility classes, however, I don't know how I can create dynamic tokens with Style Dictionary.

Does anyone have any suggestions?

question

Most helpful comment

This is an interesting one. This works for me:

const StyleDictionary = require('style-dictionary');

var utilities = [
  {
      "name": "color",
      "tokenType": "color",
      "CSSprop": "color"
  },
  {
      "name": "background-color",
      "tokenType": "color",
      "CSSprop": "background-color"
  }
];


StyleDictionary.registerFormat({
  name: 'utilityClass',
  formatter: function(dictionary, platform) {
    let output = '';
    dictionary.allProperties.forEach(function(prop) {
      var tokenType = prop.path.slice(0,1)[0];

      utilities.forEach(function(utility) {
        if (tokenType === utility.tokenType) {
          var utilityClass = utility.name + "-" + prop.path[1];
          output += `.${utilityClass} {
  ${utility.CSSprop}: ${prop.value};
}\n\n`
        }
      });
    });
    return output;
  }
});

StyleDictionary.extend({
  properties: {
    "color": {
      "white"   : { "value": "#FFFFFF" },
      "black"   : { "value": "#000000" }
    }
  },
  platforms: {
    css: {
      buildPath: 'build/',
      transformGroup: 'css',
      files: [{
        destination: 'utility.css',
        format: 'utilityClass'
      }]
    }
  }
}).buildAllPlatforms();

The downside is you will only ever be able to include 1 css property in each utility class, if you want more css properties we'd have to do something a bit different. Let me know if this answers your question or not. Thanks!

All 4 comments

This is an interesting one. This works for me:

const StyleDictionary = require('style-dictionary');

var utilities = [
  {
      "name": "color",
      "tokenType": "color",
      "CSSprop": "color"
  },
  {
      "name": "background-color",
      "tokenType": "color",
      "CSSprop": "background-color"
  }
];


StyleDictionary.registerFormat({
  name: 'utilityClass',
  formatter: function(dictionary, platform) {
    let output = '';
    dictionary.allProperties.forEach(function(prop) {
      var tokenType = prop.path.slice(0,1)[0];

      utilities.forEach(function(utility) {
        if (tokenType === utility.tokenType) {
          var utilityClass = utility.name + "-" + prop.path[1];
          output += `.${utilityClass} {
  ${utility.CSSprop}: ${prop.value};
}\n\n`
        }
      });
    });
    return output;
  }
});

StyleDictionary.extend({
  properties: {
    "color": {
      "white"   : { "value": "#FFFFFF" },
      "black"   : { "value": "#000000" }
    }
  },
  platforms: {
    css: {
      buildPath: 'build/',
      transformGroup: 'css',
      files: [{
        destination: 'utility.css',
        format: 'utilityClass'
      }]
    }
  }
}).buildAllPlatforms();

The downside is you will only ever be able to include 1 css property in each utility class, if you want more css properties we'd have to do something a bit different. Let me know if this answers your question or not. Thanks!

Thanks this has helped a lot.

I really appreciate you spending your own time to help me.

It's only just made sense to me that formatter wants the whole string for the output, so this makes a lot more sense now.

I think I have some ideas for how to include more than one CSS property in a utility class. I will iterate through the CSS props and use a regex to replace the value of the token where it's denoted by an identifier of some kind.

Just checking in @limitlessloop to see if you have any more questions on this or if we can close the issue. Thanks!

Yep, no problem. No more questions for now. Thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dbanksdesign picture dbanksdesign  路  3Comments

tonyjwalt picture tonyjwalt  路  5Comments

nhoizey picture nhoizey  路  4Comments

dbanksdesign picture dbanksdesign  路  5Comments

johnny353535 picture johnny353535  路  3Comments