/* For generating Design Tokens, we use Style Dictionary for several reasons: - It's prepared to easily generate tokens for multiple types of outputs (CSS, SCSS, iOS, Android, documentation, etc.). - It also works very well out of the box with any kind of Design Tokens formats, like Figma Tokens, as well as APIs to adjust to more custom ones. - It is probably the most well-known and widely used Design Tokens tool. It has also been regularly maintained for a long time. - It can easily scale to different necessities we might have in the future. */ // eslint-disable-next-line @typescript-eslint/no-var-requires const StyleDictionary = require('style-dictionary'); const { fileHeader } = StyleDictionary.formatHelpers; // Empty for as an example of how we can add some extra variables, not from the tokens JSON const customVariables = ``; // Prefix for all generated variables const variablesPrefix = 'ionic'; function hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16), } : null; } // CSS vanilla :root format StyleDictionary.registerFormat({ name: 'rootFormat', formatter({ dictionary, file }) { // Add a prefix to all variable names const prefixedVariables = dictionary.allProperties.map((prop) => { const rgb = hexToRgb(prop.value); return ` --${variablesPrefix}-${prop.name}: ${prop.value};${ rgb ? `\n --${variablesPrefix}-${prop.name}-rgb: ${rgb.r}, ${rgb.g}, ${rgb.b};` : `` }`; }); return ( fileHeader({ file }) + ':root {\n' + prefixedVariables.join('\n') + // Join all prefixed variables with a newline customVariables + '\n}\n' ); }, }); // scss variables format StyleDictionary.registerFormat({ name: 'scssVariablesFormat', formatter({ dictionary, file }) { // Add a prefix to all variable names const prefixedVariables = dictionary.allProperties.map((prop) => { const rgb = hexToRgb(prop.value); return `$${variablesPrefix}-${prop.name}: var(--${variablesPrefix}-${prop.name}, ${prop.value});${ rgb ? `\n$${variablesPrefix}-${prop.name}-rgb: var(--${variablesPrefix}-${prop.name}-rgb, ${rgb.r}, ${rgb.g}, ${rgb.b});` : `` }`; }); return ( fileHeader({ file }) + prefixedVariables.join('\n') + // Join all prefixed variables with a newline customVariables + '\n' ); }, }); // Create utility-classes StyleDictionary.registerFormat({ name: 'cssUtilityClassesFormat', formatter({ dictionary, file }) { const utilityClasses = dictionary.allProperties.map((prop) => { let tokenType = prop.attributes.category; const className = `${prop.name}`; let utilityClass = ''; switch (tokenType) { case 'color': utilityClass = `.${variablesPrefix}-${className} {\n color: $ionic-${prop.name};\n} .${variablesPrefix}-background-${className} {\n background-color: $ionic-${prop.name};\n}`; break; case 'border': const borderAttribute = prop.attributes.type === 'radius' ? 'border-radius' : 'border-width'; utilityClass = `.${variablesPrefix}-${className} {\n ${borderAttribute}: $ionic-${prop.name};\n}`; break; case 'font': let fontAttribute; switch (prop.attributes.type) { case 'size': fontAttribute = 'font-size'; break; case 'weight': fontAttribute = 'font-weight'; break; case 'line-height': fontAttribute = 'line-height'; break; case 'letter-spacing': fontAttribute = 'letter-spacing'; break; case 'family': return; } utilityClass = `.${variablesPrefix}-${className} {\n ${fontAttribute}: $ionic-${prop.name};\n}`; break; case 'elevation': utilityClass = `.${variablesPrefix}-${className} {\n box-shadow: $ionic-${prop.name};\n}`; break; case 'space': utilityClass = `.${variablesPrefix}-margin-${className} {\n --margin-start: #{$ionic-${prop.name}};\n --margin-end: #{$ionic-${prop.name}};\n --margin-top: #{$ionic-${prop.name}};\n --margin-bottom: #{$ionic-${prop.name}};\n\n @include margin(${prop.value});\n};\n .${variablesPrefix}-padding-${className} {\n --padding-start: #{$ionic-${prop.name}};\n --padding-end: #{$ionic-${prop.name}};\n --padding-top: #{$ionic-${prop.name}};\n --padding-bottom: #{$ionic-${prop.name}};\n\n @include padding(${prop.value});\n};\n`; break; default: utilityClass = `.${variablesPrefix}-${className} {\n ${tokenType}: $ionic-${prop.name};\n}`; } return utilityClass; }); return [ fileHeader({ file }), '@import "./ionic.vars";\n@import "../themes/mixins";\n', utilityClasses.join('\n'), ].join('\n'); }, }); // Make Style Dictionary comply with the $ format on properties from W3C Guidelines const w3cTokenJsonParser = { pattern: /\.json|\.tokens\.json|\.tokens$/, parse({ contents }) { // replace $value with value so that style dictionary recognizes it var preparedContent = (contents || '{}') .replace(/"\$?value":/g, '"value":') // convert $description to comment .replace(/"\$?description":/g, '"comment":'); // return JSON.parse(preparedContent); }, }; StyleDictionary.registerParser(w3cTokenJsonParser); // Generate Tokens StyleDictionary.extend({ source: ['./src/foundations/*.json'], platforms: { css: { buildPath: './src/foundations/', transformGroup: 'css', files: [ { destination: 'ionic.root.scss', format: 'rootFormat', options: { outputReferences: true, fileHeader: `myFileHeader`, }, }, ], }, scss: { buildPath: './src/foundations/', transformGroup: 'scss', files: [ { destination: 'ionic.vars.scss', format: 'scssVariablesFormat', options: { outputReferences: true, fileHeader: `myFileHeader`, }, }, { destination: 'ionic.utility.scss', format: 'cssUtilityClassesFormat', options: { outputReferences: true, fileHeader: `myFileHeader`, }, }, ], }, }, fileHeader: { myFileHeader: () => { return [`This is an auto-generated file, please do not change it directly.`, `Ionic Design System`]; }, }, }).buildAllPlatforms();