style-dictionary: I can't generate variables css to a specific class

Hello!

We are use the Style Dictionary to generate our tokens, but we have a problem here.

The tokens are building to different products (themes) and we like use variables css, but in the generated files, we got:

// theme-x.css
:root {
   // variables
}

// theme-y.css
:root {
   // variables
}

How change the destiny of variables css to custom class, like a:

// theme-x.css
.theme-x {
   // variables
}

// theme-y.css
.theme-y {
   // variables
}

🍻

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 15 (4 by maintainers)

Most upvoted comments

Unfortunately the built-in css/variables format does not allow this. But that would be a good addition! Here is the code: https://github.com/amzn/style-dictionary/blob/master/lib/common/formats.js#L106

You could write a custom format that does this too if you can’t wait for that change to be made into the core library:

StyleDictionary.registerFormat({
  name: 'css/variables',
  formatter: function(dictionary, config) {
    return `${this.selector} {
${dictionary.allProperties.map(prop => `  --${prop.name}: ${prop.value};`)}
    }`
  }
});

StyleDictionary.extend({
  source: [],
  platforms: {
    css: {
      files: [{
        destination: 'theme-x.css',
        format: 'css/variables',
        selector: '.theme-x'
      }]
    }
  }
})

Note: this is untested code, but should probably work and doesn’t include full working code. Also it won’t exactly match the functionality of ‘css/variables’ format, but if you want to you could copy the functions in https://github.com/amzn/style-dictionary/blob/main/lib/common/formats.js to make it work exactly the same.

Let me know if this helps!

Hi!

I think is worth pointing out that you can also use Custom format helpers. To make use of the functions already defined in the library without having to re-write the formatter, if you only need to change/add a selector/class!

const { fileHeader, formattedVariables } = StyleDictionary.formatHelpers;

StyleDictionary.registerFormat({
  name: 'css/variables-themed',
  formatter: function({dictionary, file, options}) {
    const { outputReferences, theme } = options;
    return fileHeader({file}) +
      `:root .${theme} {\n` +
      formattedVariables({format: 'css', dictionary, outputReferences}) +
      '\n}\n';
  }
});

StyleDictionary.extend({
  source: ['src/**/*base.json', 'src/**/*dark.json'],
  platforms: {
    scss: {
      transformGroup: 'css',
      buildPath: 'build/css/',
      files: [{
        destination: 'dark.css',
        format: 'css/variables-themed',
        options: {
          outputReferences: true,
          theme: "dark"
        }
      }]
    }
  }
}).buildAllPlatforms();

@dbanksdesign Thank you so mutch for your help! ❤️

We understand about the registerFormat method and got a great result. Just a observation to your script. The method .map return a new array with ,. We use the join() method to solve this and get a right css on finish.

Script:

StyleDictionaryPackage.registerFormat({
  name: 'css/variables',
  formatter: function(dictionary, config) {
    return `${this.selector} {
${dictionary.allProperties.map(prop => `  --${prop.name}: ${prop.value};`).join('\n')}
    }`
  }
});

@sarah-martinellibenedetti Sorry, us project don’t is open source 😢

But, I go try explain to you:

folders

/properties
    /brands
        /brand1
            brand1.json
        /brand2
            brand2.json
        /brand3
            brand3.json

    /globals
        border.json
        font.json
        height.json
        z-index.json

    /platforms
        /android
            button.json
            font.json
        /ios
            button.json
            font.json
        /web
            breakpoint.json
            font.json

brands/brand1.json

{
	"color": {
		"primary": {
			"base": { "value": "#27bd6e", "type": "brand" },
			"accessible": { "value": "#036c36", "type": "brand" }
		},
		"secondary": {
			"base": { "value": "#128dc4", "type": "brand" }
		}
	}
}

plataforms/web/font.json

{
	"font": {
		"family": { "value": "'Open Sans', sans-serif", "type": "global" },
		"weight": {
			"regular": { "value": "400", "type": "global" },
			"semibold": { "value": "600", "type": "global" },
			"bold": { "value": "700", "type": "global" }
		}
	}
}

@uptonking you could actually use outputReferences in a custom format. Take a look at this example:

https://github.com/amzn/style-dictionary/blob/3.0/examples/advanced/variables-in-outputs/sd.config.js

The custom format doesn’t use the outputReferences option, which is my bad, it will always output references. But you could change the formatter code to this to check if the outputReferences option is true:

function({dictionary, options}) {
      return dictionary.allProperties.map(token => {
        let value = JSON.stringify(token.value);
        // the `dictionary` object now has `usesReference()` and
        // `getReference()` methods. `usesReference()` will return true if
        // the value has a reference in it. `getReference()` will return
        // the reference to the whole token so that you can access its
        // name or any other attributes.
        if (options.outputReferences) {
          if (dictionary.usesReference(token.original.value)) {
            const reference = dictionary.getReference(token.original.value);
            value = reference.name;
          }
        }
        return `export const ${token.name} = ${value};`
      }).join(`\n`)
    }

Man, you helped us so much. The .join() is a detail 😉

Thank you @dbanksdesign! You’ve saved the day! 😁👏👏👏👏👏