Files
Bernardo Cardoso a5a7bee25c feat(tokens): update tokens architecture and usage (#29950)
Issue number: internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->


## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Removed old tokens stored on foundations folder. Now the jsons with
the tokens come from the new dependency:
https://github.com/OutSystems/outsystems-design-tokens.
- The tokens from UX Team completely changed the structure and in some
cases the values. Everything was replaced on the project to use the new
tokens.
- Snapshots updated and differences reviewed with UX Team. A fix was
done on the testing css, to make sure the correct _Inter_ font-family
was actually used. This resulted in some differences on the text for
some componentes, that had snapshots with the native fonts.
- Removed generation of scss file on tokens script, that contained the
:root selector with the custom CSS properties, as they were not used on
the Ionic context.
- Removed generation of html file with tokens preview, as there wasn't a
great value on this (we have storybook on other contexts) and it allowed
to reduce a lot of code and complexity from the tokens script.
- The token command was adapted to use the command available from the
https://github.com/OutSystems/outsystems-design-tokens package, using
the `--config` prop, where we pass the path to our Ionic token script,
where we generate the needed scss variables and utility-classes (by
default the tokens repo tries to be as agnostic as possible, and only
generates the css variables, without the prefix and added details we
need on the Ionic side).
- Removed the token command from the npm run build, as it unnecessarily
added time on that command to run. Besides, it should not be common that
we need to run this command in the future.
- Updated reference to latest version of [Style Dictionary
4.1.3](https://v4.styledictionary.com/version-4/statement/). Version 4
comes with a lot of improvements, but also breaking-changes, so it was
needed to adapt the tokens script. The code on the script is also now
cleaner.
- Changed prefix from ionic to ion. The term ionic was used initially,
to help differentiate from the old ios/md stuff, but I feel with the
current scss architecture, that is no longer needed, and we can use the
same prefix across themes.

This requires to run `npm install ` again.

## Does this introduce a breaking change?

- [x] Yes
- [ ] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->

---------

Co-authored-by: ionitron <hi@ionicframework.com>
Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
2024-10-28 18:17:48 +00:00

187 lines
6.9 KiB
JavaScript

/* eslint-disable @typescript-eslint/no-var-requires */
// 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.
(async () => {
const {
generateShadowValue,
generateFontSizeValue,
generateFontFamilyValue,
generateTypographyOutput,
generateValue,
generateColorUtilityClasses,
generateSpaceUtilityClasses,
removeConsecutiveRepeatedWords,
setPrefixValue,
generateRadiusUtilityClasses,
generateBorderSizeUtilityClasses,
generateFontUtilityClasses,
generateShadowUtilityClasses,
generateUtilityClasses
} = require('./utils.js');
const StyleDictionary = (await import('style-dictionary')).default;
// Set the prefix for variables and classes
setPrefixValue('ion');
// Register a custom file header
StyleDictionary.registerFileHeader({
name: 'custom-header',
fileHeader: async (defaultMessages = []) => {
return [...defaultMessages, 'Do not edit directly, this file was auto-generated.'];
},
});
// SCSS variables format
StyleDictionary.registerFormat({
name: 'scssVariablesFormat',
format: async function({ dictionary, file}) {
console.log('Generating SCSS variables...');
// Separate typography tokens from the rest
const typographyProperties = dictionary.allTokens.filter((prop) => prop.$type === 'typography');
const otherProperties = dictionary.allTokens.filter((prop) => prop.$type !== 'typography');
// Make sure the reused scss variables are defined first, to avoid compilation errors
const sortedProperties = [...otherProperties, ...typographyProperties];
const prefixedVariables = sortedProperties.map((prop) => {
// Remove consecutive repeated words from the token name, like border-border-color
const propName = removeConsecutiveRepeatedWords(prop.name);
switch (prop.$type) {
case 'boxShadow':
return generateShadowValue(prop, propName);
case 'fontFamilies':
return generateFontFamilyValue(prop, propName, 'scss');
case 'fontSizes':
return generateFontSizeValue(prop, propName, 'scss');
case 'typography':
return generateTypographyOutput(prop, propName, true);
default:
return generateValue(prop, propName);
}
});
const fileHeader = await file.options.fileHeader();
return [
`/*\n${fileHeader.join('\n')}\n*/`,
'@use "../themes/functions.sizes" as font;\n',
prefixedVariables.join('\n') + '\n',
].join('\n');
},
});
// Create utility-classes
StyleDictionary.registerFormat({
name: 'cssUtilityClassesFormat',
format: async function({ dictionary, file}) {
console.log('Generating Utility-Classes...');
// Arrays to store specific utility classes
const typographyUtilityClasses = [];
const otherUtilityClasses = [];
// Generate utility classes for each token
dictionary.allTokens.map((prop) => {
const tokenCategory = prop.attributes.category;
if (prop.$type === 'fontFamilies' || tokenCategory === 'scale' || tokenCategory === 'backdrop') {
// Not creating for the tokens below, as they make no sense to exist as utility-classes.
return;
}
// Remove consecutive repeated words from the token name, like border-border-color
const propName = removeConsecutiveRepeatedWords(prop.name);
if (prop.$type === 'typography') {
// Typography tokens are handled differently, as each might have a different tokenType
return typographyUtilityClasses.push(generateTypographyOutput(prop, propName, false));
} else if(tokenCategory.startsWith('round') || tokenCategory.startsWith('rectangular') || tokenCategory.startsWith('soft')) {
// Generate utility classes for border-radius shape tokens, as they have their own token json file, based on primitive tokens
return otherUtilityClasses.push(generateRadiusUtilityClasses(propName));
}
let utilityClass = '';
switch (tokenCategory) {
case 'color':
case 'primitives':
case 'semantics':
case 'text':
case 'bg':
case 'icon':
case 'state':
utilityClass = generateColorUtilityClasses(prop, propName);
break;
case 'border-size':
utilityClass = generateBorderSizeUtilityClasses(propName);
break;
case 'font':
utilityClass = generateFontUtilityClasses(prop, propName);
break;
case 'space':
utilityClass = generateSpaceUtilityClasses(prop, propName);
break;
case 'shadow':
case 'elevation':
utilityClass = generateShadowUtilityClasses(propName);
break;
default:
utilityClass = generateUtilityClasses(tokenCategory, propName);
}
return otherUtilityClasses.push(utilityClass);
});
// Concatenate typography utility classes at the beginning
const finalOutput = typographyUtilityClasses.concat(otherUtilityClasses).join('\n');
const fileHeader = await file.options.fileHeader();
return [
`/*\n${fileHeader.join('\n')}\n*/`,
'@import "./ionic.vars";\n@import "../themes/mixins";\n',
finalOutput,
].join('\n');
},
});
})();
// APPLY THE CONFIGURATION
module.exports = {
source: ["node_modules/outsystems-design-tokens/tokens/**/*.json"],
platforms: {
scss: {
transformGroup: "scss",
buildPath: './src/foundations/',
files: [
{
destination: "ionic.vars.scss",
format: "scssVariablesFormat",
options: {
fileHeader: `custom-header`,
},
},
{
destination: "ionic.utility.scss",
format: "cssUtilityClassesFormat",
options: {
fileHeader: `custom-header`
}
}
]
}
}
};