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
}
๐ป
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!
Thank you @dbanksdesign! You've saved the day! ๐๐๐๐๐๐
@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')}
}`
}
});
@gabrielmedina great catch on the .join() my bad! ๐คฆโโ๏ธ That is why I need to test my code before sending it to people.
Man, you helped us so much. The .join() is a detail ;)
Hi @gabrielmedina sorry to ask but how did you setup the style-dictionary to generate the 2 separate themes? do you mind sharing?
Hi @sarah-martinellibenedetti
This is my build.js
const StyleDictionaryPackage = require('style-dictionary');
const path = 'src/tokens'
function getStyleDictionaryConfig(brand, platform) {
return {
source: [
`${path}/properties/brands/${brand}/*.json`,
`${path}/properties/globals/**/*.json`,
`${path}/properties/platforms/${platform}/**/*.json`
],
platforms: {
web: {
transformGroup: 'web',
buildPath: `${path}/build/web/`,
files: [
{
destination: '_tokens-globais.scss',
format: 'css/variables',
selector: ':root',
filter: { type: 'global' }
},
{
destination: `_tokens-${brand}.scss`,
format: 'css/variables',
selector: `.tokens-${brand}`,
filter: { type: 'brand' }
}
]
},
android: {
transformGroup: 'android',
buildPath: `${path}/build/android/`,
files: [
{
destination: `tokens-${brand}.colors.xml`,
format: 'android/colors'
},
{
destination: `tokens-${brand}.dimens.xml`,
format: 'android/dimens'
},
{
destination: `tokens-${brand}.font_dimens.xml`,
format: 'android/fontDimens'
}
]
},
ios: {
transformGroup: 'ios',
buildPath: `${path}/build/ios/`,
files: [
{
destination: `tokens-${brand}.h`,
format: 'ios/macros'
},
]
}
}
};
}
StyleDictionaryPackage.registerFormat({
name: 'css/variables',
formatter: function (dictionary, config) {
return `${this.selector} {
${dictionary.allProperties.map(prop => ` --${prop.name}: ${prop.value};`).join('\n')}
}`
}
});
console.log('Build started...');
const brands = ['brand1', 'brand2', 'brand3']
const plataforms = ['web', 'ios', 'android']
brands.map(function (brand) {
plataforms.map(function (platform) {
console.log('\n==============================================');
console.log(`\nProcessing: [${platform}] [${brand}]`);
const StyleDictionary = StyleDictionaryPackage.extend(getStyleDictionaryConfig(brand, platform);
StyleDictionary.buildPlatform(platform);
console.log('\nEnd processing');
});
});
console.log('\n==============================================');
console.log('\nBuild completed!');
@gabrielmedina understood! Do you have your project in some repository? Can I please take a look? I just want to understand better how did you organize the tokens inside each brand/platform. Thank you for your patience!
@sarah-martinellibenedetti Sorry, us project don't is open source ๐ข
But, I go try explain to you:
/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
{
"color": {
"primary": {
"base": { "value": "#27bd6e", "type": "brand" },
"accessible": { "value": "#036c36", "type": "brand" }
},
"secondary": {
"base": { "value": "#128dc4", "type": "brand" }
}
}
}
{
"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" }
}
}
}
@sarah-martinellibenedetti It looks like @gabrielmedina followed this example: https://github.com/amzn/style-dictionary/tree/main/examples/advanced/multi-brand-multi-platform hopefully that helps!
@dbanksdesign @gabrielmedina
in v3,if you use registerFormat, you lose the benefits of outputReferences.
Is there any good idea for custom top level selector name rather than the default :root?
what I can come up with is to create custom action scripts to modify the .css file after tokens are built.
@uptonking you could actually use outputReferences in a custom format. Take a look at this example:
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`)
}
@dbanksdesign
I think it's unnecessary to use JSON.stringify(token.value);, it will output extra quotes.
I found a way to support css variables fallback values.
StyleDictionary.registerFormat({
name: 'css/variables',
formatter: function ({ dictionary, options }) {
return `${this.selectorName} {
${dictionary.allProperties
.map((prop) => {
// let value = JSON.stringify(prop.value);
let value = prop.value;
if (options.outputReferences) {
if (dictionary.usesReference(prop.original.value)) {
const reference = dictionary.getReference(prop.original.value);
value = reference.name;
return ` --${prop.name}: var(--${value}, ${prop.value});`;
}
}
return ` --${prop.name}: ${prop.value};`;
})
.join('\n')}
}`;
},
});
A flaw is that the indentation of the first two lines is unfriendly.
Most helpful comment
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:
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!