mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
feat(tokens): modernize scripts and change prefix to token (#30666)
Issue number: internal --------- ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> This pull request updates the design token build pipeline to support Style Dictionary v5 and modernizes the scripts by migrating them to ES modules. The changes ensure compatibility with the latest dependencies and improve maintainability by refactoring the build scripts and utilities. **Dependency and Build Pipeline Updates:** * Upgraded `outsystems-design-tokens` to version `^1.3.2` and `style-dictionary` to version `^5.0.0` in `package.json` to support the latest features and fixes. * Updated the `build.tokens` npm script to use the new ES module entry point (`index.mjs`) instead of the old CommonJS file (`index.js`). **Migration to ES Modules:** * Migrated the main design token build script from `index.js` to `index.mjs`, refactoring imports/exports and adjusting to Style Dictionary v5 API changes. [[1]](diffhunk://#diff-80accbbc258884565c215c7eb6280722805ddf618d72bf6506c7dd1fb7faaa54L1-L201) [[2]](diffhunk://#diff-0b9870c62ff80af860467e2541bba0b9ba5e7280b12bea6eeb124b1d174efbcfR1-R194) * Renamed and refactored the utility functions file from `utils.js` to `utils.mjs`, converting all utility functions to ES module exports for compatibility. [[1]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL4-R10) [[2]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL22-R22) [[3]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL47-R53) [[4]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL65-R65) [[5]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL76-R76) [[6]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL85-R85) [[7]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL95-R95) [[8]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL118-R118) [[9]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL144-R144) [[10]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL155-R155) [[11]](diffhunk://#diff-8b5c339d9dd13300954577213f84a443321a0d6477acd7553787fbcc00ce8cabL221-R221) **Style Dictionary Configuration and Output Improvements:** * Adjusted the Style Dictionary configuration and format registration to match the new API, including changes to file header handling and output structure for SCSS variables and utility classes. * Changed variable prefix to --token ## Does this introduce a breaking change? - [ ] Yes - [x] 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>
This commit is contained in:
951
core/package-lock.json
generated
951
core/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -66,12 +66,12 @@
|
|||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-cli": "^29.7.0",
|
"jest-cli": "^29.7.0",
|
||||||
"outsystems-design-tokens": "^1.3.0",
|
"outsystems-design-tokens": "^1.3.2",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"rollup": "^2.26.4",
|
"rollup": "^2.26.4",
|
||||||
"sass": "^1.33.0",
|
"sass": "^1.33.0",
|
||||||
"serve": "^14.0.1",
|
"serve": "^14.0.1",
|
||||||
"style-dictionary": "^4.1.3",
|
"style-dictionary": "^5.0.0",
|
||||||
"stylelint": "^13.13.1",
|
"stylelint": "^13.13.1",
|
||||||
"stylelint-order": "^4.1.0"
|
"stylelint-order": "^4.1.0"
|
||||||
},
|
},
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"build.css": "npm run css.sass && npm run css.minify",
|
"build.css": "npm run css.sass && npm run css.minify",
|
||||||
"build.debug": "npm run clean && stencil build --debug",
|
"build.debug": "npm run clean && stencil build --debug",
|
||||||
"build.docs.json": "stencil build --docs-json dist/docs.json",
|
"build.docs.json": "stencil build --docs-json dist/docs.json",
|
||||||
"build.tokens": "npx build.tokens --config scripts/tokens/index.js && npm run prettier.tokens",
|
"build.tokens": "npx build.tokens --config scripts/tokens/index.mjs && npm run prettier.tokens",
|
||||||
"clean": "node scripts/clean.js",
|
"clean": "node scripts/clean.js",
|
||||||
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
|
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
|
||||||
"css.sass": "sass --embed-sources --style compressed src/css:./css",
|
"css.sass": "sass --embed-sources --style compressed src/css:./css",
|
||||||
|
|||||||
@ -1,201 +0,0 @@
|
|||||||
/* 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,
|
|
||||||
generateDefaultSpaceUtilityClasses,
|
|
||||||
generateSpaceUtilityClasses,
|
|
||||||
removeConsecutiveRepeatedWords,
|
|
||||||
setPrefixValue,
|
|
||||||
generateRadiusUtilityClasses,
|
|
||||||
generateBorderUtilityClasses,
|
|
||||||
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...');
|
|
||||||
|
|
||||||
const primitiveProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'primitives');
|
|
||||||
const scaleProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'scale');
|
|
||||||
const borderProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'border');
|
|
||||||
const semanticsProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'semantics');
|
|
||||||
const nonPrimitiveScaleBorderSemanticsProperties = dictionary.allTokens.filter(
|
|
||||||
(prop) => !['primitives', 'scale', 'border', 'semantics'].includes(prop.path[0])
|
|
||||||
);
|
|
||||||
const typographyProperties = nonPrimitiveScaleBorderSemanticsProperties.filter((prop) => prop.$type === 'typography');
|
|
||||||
const otherProperties = nonPrimitiveScaleBorderSemanticsProperties.filter((prop) => prop.$type !== 'typography');
|
|
||||||
|
|
||||||
// Order: primitives → semantics → scale → border → other → typography
|
|
||||||
const sortedProperties = [
|
|
||||||
...primitiveProperties,
|
|
||||||
...semanticsProperties,
|
|
||||||
...scaleProperties,
|
|
||||||
...borderProperties,
|
|
||||||
...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':
|
|
||||||
utilityClass = generateBorderUtilityClasses(prop, 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);
|
|
||||||
});
|
|
||||||
|
|
||||||
const defaultSpaceUtilityClasses = generateDefaultSpaceUtilityClasses();
|
|
||||||
otherUtilityClasses.push(defaultSpaceUtilityClasses);
|
|
||||||
|
|
||||||
// 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`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
186
core/scripts/tokens/index.mjs
Normal file
186
core/scripts/tokens/index.mjs
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/* 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.
|
||||||
|
import * as utils from './utils.mjs';
|
||||||
|
const {
|
||||||
|
generateShadowValue,
|
||||||
|
generateFontSizeValue,
|
||||||
|
generateFontFamilyValue,
|
||||||
|
generateTypographyOutput,
|
||||||
|
generateValue,
|
||||||
|
generateColorUtilityClasses,
|
||||||
|
generateDefaultSpaceUtilityClasses,
|
||||||
|
generateSpaceUtilityClasses,
|
||||||
|
removeConsecutiveRepeatedWords,
|
||||||
|
setVariablePrefixValue,
|
||||||
|
setClassesAndScssPrefixValue: setClassesPrefixValue,
|
||||||
|
generateRadiusUtilityClasses,
|
||||||
|
generateBorderUtilityClasses,
|
||||||
|
generateFontUtilityClasses,
|
||||||
|
generateShadowUtilityClasses,
|
||||||
|
generateUtilityClasses
|
||||||
|
} = utils;
|
||||||
|
import StyleDictionary from 'style-dictionary';
|
||||||
|
|
||||||
|
const customHeader = `// Do not edit directly, this file was auto-generated.`;
|
||||||
|
// Set the prefix for classes
|
||||||
|
setClassesPrefixValue('ion');
|
||||||
|
// Set the prefix for variables
|
||||||
|
setVariablePrefixValue('token');
|
||||||
|
|
||||||
|
// SCSS variables format
|
||||||
|
StyleDictionary.registerFormat({
|
||||||
|
name: 'scssVariablesFormat',
|
||||||
|
format: function ({ dictionary }) { // Use 'format' for Style Dictionary v3
|
||||||
|
console.log('Generating SCSS variables...');
|
||||||
|
|
||||||
|
const primitiveProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'primitives');
|
||||||
|
const scaleProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'scale');
|
||||||
|
const borderProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'border');
|
||||||
|
const semanticsProperties = dictionary.allTokens.filter((prop) => prop.path[0] === 'semantics');
|
||||||
|
const nonPrimitiveScaleBorderSemanticsProperties = dictionary.allTokens.filter(
|
||||||
|
(prop) => !['primitives', 'scale', 'border', 'semantics'].includes(prop.path[0])
|
||||||
|
);
|
||||||
|
const typographyProperties = nonPrimitiveScaleBorderSemanticsProperties.filter((prop) => prop.$type === 'typography');
|
||||||
|
const otherProperties = nonPrimitiveScaleBorderSemanticsProperties.filter((prop) => prop.$type !== 'typography');
|
||||||
|
|
||||||
|
// Order: primitives → semantics → scale → border → other → typography
|
||||||
|
const sortedProperties = [
|
||||||
|
...primitiveProperties,
|
||||||
|
...semanticsProperties,
|
||||||
|
...scaleProperties,
|
||||||
|
...borderProperties,
|
||||||
|
...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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// In v3, the header is added automatically by Style Dictionary.
|
||||||
|
// The format function should only return the file content.
|
||||||
|
return [
|
||||||
|
customHeader + '\n\n',
|
||||||
|
'@use "../themes/functions.sizes" as font;\n',
|
||||||
|
prefixedVariables.join('\n') + '\n',
|
||||||
|
].join('');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create utility-classes
|
||||||
|
StyleDictionary.registerFormat({
|
||||||
|
name: 'cssUtilityClassesFormat',
|
||||||
|
format: function ({ dictionary }) { // Use 'format' for Style Dictionary v3
|
||||||
|
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':
|
||||||
|
utilityClass = generateBorderUtilityClasses(prop, 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultSpaceUtilityClasses = generateDefaultSpaceUtilityClasses();
|
||||||
|
otherUtilityClasses.push(defaultSpaceUtilityClasses);
|
||||||
|
|
||||||
|
// Concatenate typography utility classes at the beginning
|
||||||
|
const finalOutput = typographyUtilityClasses.concat(otherUtilityClasses).join('\n');
|
||||||
|
|
||||||
|
// In v3, the header is added automatically by Style Dictionary.
|
||||||
|
// The format function should only return the file content.
|
||||||
|
return [
|
||||||
|
customHeader + '\n\n',
|
||||||
|
'@import "./ionic.vars";\n@import "../themes/mixins";\n',
|
||||||
|
finalOutput,
|
||||||
|
].join('');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// APPLY THE CONFIGURATION
|
||||||
|
const config = {
|
||||||
|
source: ["node_modules/outsystems-design-tokens/tokens/**/*.json"],
|
||||||
|
platforms: {
|
||||||
|
scss: {
|
||||||
|
transformGroup: "scss",
|
||||||
|
buildPath: './src/foundations/',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
destination: "ionic.vars.scss",
|
||||||
|
format: "scssVariablesFormat",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
destination: "ionic.utility.scss",
|
||||||
|
format: "cssUtilityClassesFormat",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -1,335 +0,0 @@
|
|||||||
let variablesPrefix; // Variable that holds the prefix used on all css and scss variables generated
|
|
||||||
|
|
||||||
// Set the variable prefix value
|
|
||||||
function setPrefixValue(prefix) {
|
|
||||||
variablesPrefix = prefix;
|
|
||||||
return variablesPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a valid rgba() color
|
|
||||||
function getRgbaValue(propValue) {
|
|
||||||
// Check if its rgba color
|
|
||||||
const isRgba = hexToRgba(propValue);
|
|
||||||
// If it is, then compose rgba() color, otherwise use the normal color
|
|
||||||
if (isRgba !== null) {
|
|
||||||
return (propValue = `rgba(${isRgba.r}, ${isRgba.g}, ${isRgba.b},${isRgba.a})`);
|
|
||||||
} else {
|
|
||||||
return propValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translates an hex color value to rgb
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translates an hex color value to rgba
|
|
||||||
function hexToRgba(hex) {
|
|
||||||
const result = /^#?([a-f\d]{2})([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),
|
|
||||||
a: Math.round((parseInt(result[4], 16) * 100) / 255) / 100,
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function to remove consecutive repeated words
|
|
||||||
function removeConsecutiveRepeatedWords(str) {
|
|
||||||
return str.replace(/(\b\w+\b)(-\1)+/g, '$1');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a reference variable for an alias token type
|
|
||||||
// (e.g., $ion-border-default: var(--ion-border-default, #d5d5d5) → $ion-border-default: var(--ion-border-default, $ion-primitives-neutral-400))
|
|
||||||
function getAliasReferenceVariable(prop) {
|
|
||||||
if (typeof prop.$value === 'string' && prop.$value.startsWith('{') && prop.$value.endsWith('}')) {
|
|
||||||
// Remove curly braces and replace dots with dashes
|
|
||||||
let ref = prop.$value.slice(1, -1).replace(/\./g, '-');
|
|
||||||
// Remove consecutive repeated words (e.g., border-border-radius-0 → border-radius-0)
|
|
||||||
ref = removeConsecutiveRepeatedWords(ref);
|
|
||||||
return `$${variablesPrefix}-${ref}`;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a valid box-shadow value from a shadow Design Token structure
|
|
||||||
function generateShadowValue(prop, propName) {
|
|
||||||
const cssShadow = prop.$value.map(shadow => {
|
|
||||||
// Assuming shadow is an object with properties like offsetX, offsetY, blurRadius, spreadRadius, color
|
|
||||||
const color = getRgbaValue(shadow.color);
|
|
||||||
return `${shadow.x}px ${shadow.y}px ${shadow.blur}px ${shadow.spread}px ${color}`;
|
|
||||||
}).join(', ');
|
|
||||||
|
|
||||||
return `$${variablesPrefix}-${propName}: var(--${variablesPrefix}-${propName}, ${cssShadow});`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a valid font-size value from a font-size Design Token structure, while transforming the pixels to rem
|
|
||||||
function generateFontSizeValue(prop, propName, variableType = 'css') {
|
|
||||||
return variableType === 'scss'
|
|
||||||
? `$${variablesPrefix}-${propName}: var(--${variablesPrefix}-${propName}, font.px-to-rem(${parseInt(
|
|
||||||
prop.$value
|
|
||||||
)}));`
|
|
||||||
: `--${propName}: #{font.px-to-rem(${parseInt(prop.$value)})};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a valid font-family value from a font-family Design Token structure
|
|
||||||
function generateFontFamilyValue(prop, propName, variableType = 'css') {
|
|
||||||
// Remove the last word from the token, as it contains the name of the font, which we don't want to be included on the generated variables
|
|
||||||
const _propName = propName.split('-').slice(0, -1).join('-');
|
|
||||||
|
|
||||||
return variableType === 'scss'
|
|
||||||
? `$${variablesPrefix}-${_propName}: var(--${variablesPrefix}-${_propName}, "${prop.$value}", sans-serif);`
|
|
||||||
: `--${variablesPrefix}-${_propName}: "${prop.$value}", sans-serif;`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a final value, based if the Design Token is of type color or not
|
|
||||||
function generateValue(prop, propName) {
|
|
||||||
// Use the original value to detect aliases
|
|
||||||
const aliasVar = getAliasReferenceVariable({ $value: prop.original.$value });
|
|
||||||
|
|
||||||
// Always generate the main variable
|
|
||||||
let mainValue;
|
|
||||||
if (aliasVar) {
|
|
||||||
mainValue = `$${variablesPrefix}-${propName}: var(--${variablesPrefix}-${propName}, ${aliasVar});`;
|
|
||||||
} else {
|
|
||||||
mainValue = `$${variablesPrefix}-${propName}: var(--${variablesPrefix}-${propName}, ${prop.$value});`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always generate the -rgb variable if it's a color
|
|
||||||
const rgb = hexToRgb(prop.$value);
|
|
||||||
let rgbDeclaration = '';
|
|
||||||
if (rgb) {
|
|
||||||
rgbDeclaration = `\n$${variablesPrefix}-${propName}-rgb: var(--${variablesPrefix}-${propName}-rgb, ${rgb.r}, ${rgb.g}, ${rgb.b});`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${mainValue}${rgbDeclaration}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a typography based css utility-class or scss variable from a typography token structure
|
|
||||||
function generateTypographyOutput(prop, propName, isVariable) {
|
|
||||||
const typography = prop.original.$value;
|
|
||||||
|
|
||||||
// Extract the part after the last dot and trim any extraneous characters
|
|
||||||
const extractLastPart = (str) => str.split('.').pop().replace(/[^\w-]/g, '');
|
|
||||||
|
|
||||||
const _initialWrapper = isVariable ? ': (' : ` {`;
|
|
||||||
const _endWrapper = isVariable ? ')' : `}`;
|
|
||||||
const _prefix = isVariable ? '$' : `.`;
|
|
||||||
const _endChar = isVariable ? ',' : ';';
|
|
||||||
|
|
||||||
// This exact format is needed so that it compiles the tokens with the expected lint rules
|
|
||||||
return `
|
|
||||||
${_prefix}${variablesPrefix}-${propName}${_initialWrapper}
|
|
||||||
font-size: $${variablesPrefix}-font-size-${extractLastPart(typography.fontSize)}${_endChar}
|
|
||||||
font-style: ${prop.attributes.item?.toLowerCase() === 'italic' ? 'italic' : 'normal'}${_endChar}
|
|
||||||
font-weight: $${variablesPrefix}-font-weight-${extractLastPart(typography.fontWeight)}${_endChar}
|
|
||||||
letter-spacing: $${variablesPrefix}-font-letter-spacing-${extractLastPart(typography.letterSpacing) || 0}${_endChar}
|
|
||||||
line-height: $${variablesPrefix}-font-line-height-${extractLastPart(typography.lineHeight)}${_endChar}
|
|
||||||
text-transform: ${typography.textCase}${_endChar}
|
|
||||||
text-decoration: ${typography.textDecoration}${_endChar}
|
|
||||||
${_endWrapper};
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a color based css utility-class from a color Design Token structure
|
|
||||||
function generateColorUtilityClasses(prop, className) {
|
|
||||||
const isBg = className.includes('bg');
|
|
||||||
const cssProp = isBg ? 'background-color' : 'color';
|
|
||||||
return `.${variablesPrefix}-${className} {
|
|
||||||
--${cssProp}: $${variablesPrefix}-${prop.name};
|
|
||||||
${cssProp}: $${variablesPrefix}-${prop.name};
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates margin and padding utility classes to match the token-agnostic
|
|
||||||
// utilities provided by the Ionic Framework
|
|
||||||
function generateDefaultSpaceUtilityClasses() {
|
|
||||||
const zeroMarginPaddingToken = 'space-0';
|
|
||||||
const defaultMarginPaddingToken = 'space-400';
|
|
||||||
|
|
||||||
const marginPaddingTemplate = (type) => `
|
|
||||||
.${variablesPrefix}-no-${type} {
|
|
||||||
--${type}-top: #{$${variablesPrefix}-${zeroMarginPaddingToken}};
|
|
||||||
--${type}-end: #{$${variablesPrefix}-${zeroMarginPaddingToken}};
|
|
||||||
--${type}-bottom: #{$${variablesPrefix}-${zeroMarginPaddingToken}};
|
|
||||||
--${type}-start: #{$${variablesPrefix}-${zeroMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}($${variablesPrefix}-${zeroMarginPaddingToken});
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type} {
|
|
||||||
--${type}-top: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
--${type}-end: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
--${type}-bottom: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
--${type}-start: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}($${variablesPrefix}-${defaultMarginPaddingToken});
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-top {
|
|
||||||
--${type}-top: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}($${variablesPrefix}-${defaultMarginPaddingToken}, null, null, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-end {
|
|
||||||
--${type}-end: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}(null, $${variablesPrefix}-${defaultMarginPaddingToken}, null, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-bottom {
|
|
||||||
--${type}-bottom: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}(null, null, $${variablesPrefix}-${defaultMarginPaddingToken}, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-start {
|
|
||||||
--${type}-start: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}(null, null, null, $${variablesPrefix}-${defaultMarginPaddingToken});
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-vertical {
|
|
||||||
--${type}-top: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
--${type}-bottom: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}($${variablesPrefix}-${defaultMarginPaddingToken}, null, $${variablesPrefix}-${defaultMarginPaddingToken}, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-horizontal {
|
|
||||||
--${type}-start: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
--${type}-end: #{$${variablesPrefix}-${defaultMarginPaddingToken}};
|
|
||||||
|
|
||||||
@include ${type}(null, $${variablesPrefix}-${defaultMarginPaddingToken}, null, $${variablesPrefix}-${defaultMarginPaddingToken});
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
|
|
||||||
return `${marginPaddingTemplate('margin')}\n${marginPaddingTemplate('padding')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a margin or padding based css utility-class from a space Design Token structure
|
|
||||||
function generateSpaceUtilityClasses(prop, className) {
|
|
||||||
// This exact format is needed so that it compiles the tokens with the expected lint rules
|
|
||||||
// It will generate classes for margin and padding, for equal sizing on all side and each direction
|
|
||||||
const marginPaddingTemplate = (type) => `
|
|
||||||
.${variablesPrefix}-${type}-${className} {
|
|
||||||
--${type}-top: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
--${type}-end: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
--${type}-bottom: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
--${type}-start: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
|
|
||||||
@include ${type}($${variablesPrefix}-${prop.name});
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-top-${className} {
|
|
||||||
--${type}-top: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
|
|
||||||
@include ${type}($${variablesPrefix}-${prop.name}, null, null, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-end-${className} {
|
|
||||||
--${type}-end: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
|
|
||||||
@include ${type}(null, $${variablesPrefix}-${prop.name}, null, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-bottom-${className} {
|
|
||||||
--${type}-bottom: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
|
|
||||||
@include ${type}(null, null, $${variablesPrefix}-${prop.name}, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
.${variablesPrefix}-${type}-start-${className} {
|
|
||||||
--${type}-start: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
|
|
||||||
@include ${type}(null, null, null, $${variablesPrefix}-${prop.name});
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Add gap utility classes for gap tokens
|
|
||||||
const generateGapUtilityClasses = () =>`
|
|
||||||
.${variablesPrefix}-gap-${prop.name} {
|
|
||||||
gap: #{$${variablesPrefix}-${prop.name}};
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
|
|
||||||
return `${generateGapUtilityClasses()}\n${marginPaddingTemplate('margin')}\n${marginPaddingTemplate('padding')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a valid box-shadow value from a shadow Design Token structure
|
|
||||||
function generateRadiusUtilityClasses(propName) {
|
|
||||||
return `.${variablesPrefix}-${propName} {
|
|
||||||
--border-radius: $${variablesPrefix}-${propName};
|
|
||||||
border-radius: $${variablesPrefix}-${propName};
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a border based css utility-class from a font Design Token structure
|
|
||||||
function generateBorderUtilityClasses(prop, propName) {
|
|
||||||
let attribute;
|
|
||||||
|
|
||||||
switch (prop.attributes.type) {
|
|
||||||
case 'border-radius':
|
|
||||||
case 'border-style':
|
|
||||||
attribute = prop.attributes.type;
|
|
||||||
break;
|
|
||||||
case 'border-size':
|
|
||||||
attribute = 'border-width';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
attribute = 'border-color';
|
|
||||||
}
|
|
||||||
return `.${variablesPrefix}-${propName} {
|
|
||||||
--${attribute}: $${variablesPrefix}-${propName};
|
|
||||||
${attribute}: $${variablesPrefix}-${propName};
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a font based css utility-class from a font Design Token structure
|
|
||||||
function generateFontUtilityClasses(prop, propName) {
|
|
||||||
return `.${variablesPrefix}-${propName} {\n ${prop.attributes.type}: $${variablesPrefix}-${propName};\n}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a valid box-shadow value from a shadow Design Token structure
|
|
||||||
function generateShadowUtilityClasses(propName) {
|
|
||||||
return `.${variablesPrefix}-${propName} {
|
|
||||||
--box-shadow: $${variablesPrefix}-${propName};
|
|
||||||
box-shadow: $${variablesPrefix}-${propName};
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a utility class for a given token category and name
|
|
||||||
function generateUtilityClasses(tokenCategory, propName){
|
|
||||||
return `.${variablesPrefix}-${propName} {\n ${tokenCategory}: $${variablesPrefix}-${propName};\n}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getRgbaValue,
|
|
||||||
hexToRgb,
|
|
||||||
generateShadowValue,
|
|
||||||
generateFontSizeValue,
|
|
||||||
generateFontFamilyValue,
|
|
||||||
generateTypographyOutput,
|
|
||||||
generateValue,
|
|
||||||
getAliasReferenceVariable,
|
|
||||||
setPrefixValue,
|
|
||||||
generateRadiusUtilityClasses,
|
|
||||||
generateColorUtilityClasses,
|
|
||||||
generateDefaultSpaceUtilityClasses,
|
|
||||||
generateSpaceUtilityClasses,
|
|
||||||
removeConsecutiveRepeatedWords,
|
|
||||||
generateBorderUtilityClasses,
|
|
||||||
generateFontUtilityClasses,
|
|
||||||
generateShadowUtilityClasses,
|
|
||||||
generateUtilityClasses
|
|
||||||
};
|
|
||||||
320
core/scripts/tokens/utils.mjs
Normal file
320
core/scripts/tokens/utils.mjs
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
let variablesPrefix; // Variable that holds the prefix used on all css variables generated
|
||||||
|
let classAndScssPrefix; // Variable that holds the prefix used on all css utility-classes and scss variables generated
|
||||||
|
|
||||||
|
// Set the variable prefix value
|
||||||
|
export function setVariablePrefixValue(prefix) {
|
||||||
|
variablesPrefix = prefix;
|
||||||
|
return variablesPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setClassesAndScssPrefixValue(prefix) {
|
||||||
|
classAndScssPrefix = prefix;
|
||||||
|
return classAndScssPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a valid rgba() color
|
||||||
|
export function getRgbaValue(propValue) {
|
||||||
|
// Check if its rgba color
|
||||||
|
const isRgba = hexToRgba(propValue);
|
||||||
|
// If it is, then compose rgba() color, otherwise use the normal color
|
||||||
|
if (isRgba !== null) {
|
||||||
|
return (propValue = `rgba(${isRgba.r}, ${isRgba.g}, ${isRgba.b},${isRgba.a})`);
|
||||||
|
} else {
|
||||||
|
return propValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translates an hex color value to rgb
|
||||||
|
export 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translates an hex color value to rgba
|
||||||
|
function hexToRgba(hex) {
|
||||||
|
const result = /^#?([a-f\d]{2})([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),
|
||||||
|
a: Math.round((parseInt(result[4], 16) * 100) / 255) / 100,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function to remove consecutive repeated words
|
||||||
|
export function removeConsecutiveRepeatedWords(str) {
|
||||||
|
return str.replace(/(\b\w+\b)(-\1)+/g, '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a reference variable for an alias token type
|
||||||
|
// (e.g., $ion-border-default: var(--ion-border-default, #d5d5d5) → $ion-border-default: var(--ion-border-default, $ion-primitives-neutral-400))
|
||||||
|
export function getAliasReferenceVariable(prop) {
|
||||||
|
if (typeof prop.$value === 'string' && prop.$value.startsWith('{') && prop.$value.endsWith('}')) {
|
||||||
|
// Remove curly braces and replace dots with dashes
|
||||||
|
let ref = prop.$value.slice(1, -1).replace(/\./g, '-');
|
||||||
|
// Remove consecutive repeated words (e.g., border-border-radius-0 → border-radius-0)
|
||||||
|
ref = removeConsecutiveRepeatedWords(ref);
|
||||||
|
return `$${classAndScssPrefix}-${ref}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a valid box-shadow value from a shadow Design Token structure
|
||||||
|
export function generateShadowValue(prop, propName) {
|
||||||
|
const cssShadow = prop.$value.map(shadow => {
|
||||||
|
// Assuming shadow is an object with properties like offsetX, offsetY, blurRadius, spreadRadius, color
|
||||||
|
const color = getRgbaValue(shadow.color);
|
||||||
|
return `${shadow.x}px ${shadow.y}px ${shadow.blur}px ${shadow.spread}px ${color}`;
|
||||||
|
}).join(', ');
|
||||||
|
|
||||||
|
return `$${classAndScssPrefix}-${propName}: var(--${variablesPrefix}-${propName}, ${cssShadow});`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a valid font-size value from a font-size Design Token structure, while transforming the pixels to rem
|
||||||
|
export function generateFontSizeValue(prop, propName, variableType = 'css') {
|
||||||
|
return variableType === 'scss'
|
||||||
|
? `$${classAndScssPrefix}-${propName}: var(--${variablesPrefix}-${propName}, font.px-to-rem(${parseInt(
|
||||||
|
prop.$value
|
||||||
|
)}));`
|
||||||
|
: `--${propName}: #{font.px-to-rem(${parseInt(prop.$value)})};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a valid font-family value from a font-family Design Token structure
|
||||||
|
export function generateFontFamilyValue(prop, propName, variableType = 'css') {
|
||||||
|
// Remove the last word from the token, as it contains the name of the font, which we don't want to be included on the generated variables
|
||||||
|
const _propName = propName.split('-').slice(0, -1).join('-');
|
||||||
|
|
||||||
|
return variableType === 'scss'
|
||||||
|
? `$${classAndScssPrefix}-${_propName}: var(--${variablesPrefix}-${_propName}, "${prop.$value}", sans-serif);`
|
||||||
|
: `--${variablesPrefix}-${_propName}: "${prop.$value}", sans-serif;`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a final value, based if the Design Token is of type color or not
|
||||||
|
export function generateValue(prop, propName) {
|
||||||
|
// Use the original value to detect aliases
|
||||||
|
const aliasVar = getAliasReferenceVariable({ $value: prop.original.$value });
|
||||||
|
|
||||||
|
// Always generate the main variable
|
||||||
|
let mainValue;
|
||||||
|
if (aliasVar) {
|
||||||
|
mainValue = `$${classAndScssPrefix}-${propName}: var(--${variablesPrefix}-${propName}, ${aliasVar});`;
|
||||||
|
} else {
|
||||||
|
mainValue = `$${classAndScssPrefix}-${propName}: var(--${variablesPrefix}-${propName}, ${prop.$value});`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always generate the -rgb variable if it's a color
|
||||||
|
const rgb = hexToRgb(prop.$value);
|
||||||
|
let rgbDeclaration = '';
|
||||||
|
if (rgb) {
|
||||||
|
rgbDeclaration = `\n$${classAndScssPrefix}-${propName}-rgb: var(--${variablesPrefix}-${propName}-rgb, ${rgb.r}, ${rgb.g}, ${rgb.b});`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${mainValue}${rgbDeclaration}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a typography based css utility-class or scss variable from a typography token structure
|
||||||
|
export function generateTypographyOutput(prop, propName, isVariable) {
|
||||||
|
const typography = prop.original.$value;
|
||||||
|
|
||||||
|
// Extract the part after the last dot and trim any extraneous characters
|
||||||
|
const extractLastPart = (str) => str.split('.').pop().replace(/[^\w-]/g, '');
|
||||||
|
|
||||||
|
const _initialWrapper = isVariable ? ': (' : ` {`;
|
||||||
|
const _endWrapper = isVariable ? ')' : `}`;
|
||||||
|
const _prefix = isVariable ? '$' : `.`;
|
||||||
|
const _endChar = isVariable ? ',' : ';';
|
||||||
|
|
||||||
|
// This exact format is needed so that it compiles the tokens with the expected lint rules
|
||||||
|
return `
|
||||||
|
${_prefix}${classAndScssPrefix}-${propName}${_initialWrapper}
|
||||||
|
font-size: $${classAndScssPrefix}-font-size-${extractLastPart(typography.fontSize)}${_endChar}
|
||||||
|
font-style: ${prop.attributes.item?.toLowerCase() === 'italic' ? 'italic' : 'normal'}${_endChar}
|
||||||
|
font-weight: $${classAndScssPrefix}-font-weight-${extractLastPart(typography.fontWeight)}${_endChar}
|
||||||
|
letter-spacing: $${classAndScssPrefix}-font-letter-spacing-${extractLastPart(typography.letterSpacing) || 0}${_endChar}
|
||||||
|
line-height: $${classAndScssPrefix}-font-line-height-${extractLastPart(typography.lineHeight)}${_endChar}
|
||||||
|
text-transform: ${typography.textCase}${_endChar}
|
||||||
|
text-decoration: ${typography.textDecoration}${_endChar}
|
||||||
|
${_endWrapper};
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a color based css utility-class from a color Design Token structure
|
||||||
|
export function generateColorUtilityClasses(prop, className) {
|
||||||
|
const isBg = className.includes('bg');
|
||||||
|
const cssProp = isBg ? 'background-color' : 'color';
|
||||||
|
return `.${classAndScssPrefix}-${className} {
|
||||||
|
--${cssProp}: $${classAndScssPrefix}-${prop.name};
|
||||||
|
${cssProp}: $${classAndScssPrefix}-${prop.name};
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates margin and padding utility classes to match the token-agnostic
|
||||||
|
// utilities provided by the Ionic Framework
|
||||||
|
export function generateDefaultSpaceUtilityClasses() {
|
||||||
|
const zeroMarginPaddingToken = 'space-0';
|
||||||
|
const defaultMarginPaddingToken = 'space-400';
|
||||||
|
|
||||||
|
const marginPaddingTemplate = (type) => `
|
||||||
|
.${classAndScssPrefix}-no-${type} {
|
||||||
|
--${type}-top: #{$${classAndScssPrefix}-${zeroMarginPaddingToken}};
|
||||||
|
--${type}-end: #{$${classAndScssPrefix}-${zeroMarginPaddingToken}};
|
||||||
|
--${type}-bottom: #{$${classAndScssPrefix}-${zeroMarginPaddingToken}};
|
||||||
|
--${type}-start: #{$${classAndScssPrefix}-${zeroMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}($${classAndScssPrefix}-${zeroMarginPaddingToken});
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type} {
|
||||||
|
--${type}-top: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
--${type}-end: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
--${type}-bottom: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
--${type}-start: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}($${classAndScssPrefix}-${defaultMarginPaddingToken});
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-top {
|
||||||
|
--${type}-top: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}($${classAndScssPrefix}-${defaultMarginPaddingToken}, null, null, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-end {
|
||||||
|
--${type}-end: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}(null, $${classAndScssPrefix}-${defaultMarginPaddingToken}, null, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-bottom {
|
||||||
|
--${type}-bottom: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}(null, null, $${classAndScssPrefix}-${defaultMarginPaddingToken}, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-start {
|
||||||
|
--${type}-start: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}(null, null, null, $${classAndScssPrefix}-${defaultMarginPaddingToken});
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-vertical {
|
||||||
|
--${type}-top: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
--${type}-bottom: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}($${classAndScssPrefix}-${defaultMarginPaddingToken}, null, $${classAndScssPrefix}-${defaultMarginPaddingToken}, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-horizontal {
|
||||||
|
--${type}-start: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
--${type}-end: #{$${classAndScssPrefix}-${defaultMarginPaddingToken}};
|
||||||
|
|
||||||
|
@include ${type}(null, $${classAndScssPrefix}-${defaultMarginPaddingToken}, null, $${classAndScssPrefix}-${defaultMarginPaddingToken});
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
return `${marginPaddingTemplate('margin')}\n${marginPaddingTemplate('padding')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a margin or padding based css utility-class from a space Design Token structure
|
||||||
|
export function generateSpaceUtilityClasses(prop, className) {
|
||||||
|
// This exact format is needed so that it compiles the tokens with the expected lint rules
|
||||||
|
// It will generate classes for margin and padding, for equal sizing on all side and each direction
|
||||||
|
const marginPaddingTemplate = (type) => `
|
||||||
|
.${classAndScssPrefix}-${type}-${className} {
|
||||||
|
--${type}-top: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
--${type}-end: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
--${type}-bottom: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
--${type}-start: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
|
||||||
|
@include ${type}($${classAndScssPrefix}-${prop.name});
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-top-${className} {
|
||||||
|
--${type}-top: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
|
||||||
|
@include ${type}($${classAndScssPrefix}-${prop.name}, null, null, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-end-${className} {
|
||||||
|
--${type}-end: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
|
||||||
|
@include ${type}(null, $${classAndScssPrefix}-${prop.name}, null, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-bottom-${className} {
|
||||||
|
--${type}-bottom: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
|
||||||
|
@include ${type}(null, null, $${classAndScssPrefix}-${prop.name}, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
.${classAndScssPrefix}-${type}-start-${className} {
|
||||||
|
--${type}-start: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
|
||||||
|
@include ${type}(null, null, null, $${classAndScssPrefix}-${prop.name});
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Add gap utility classes for gap tokens
|
||||||
|
const generateGapUtilityClasses = () =>`
|
||||||
|
.${classAndScssPrefix}-gap-${prop.name} {
|
||||||
|
gap: #{$${classAndScssPrefix}-${prop.name}};
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
return `${generateGapUtilityClasses()}\n${marginPaddingTemplate('margin')}\n${marginPaddingTemplate('padding')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a valid box-shadow value from a shadow Design Token structure
|
||||||
|
export function generateRadiusUtilityClasses(propName) {
|
||||||
|
return `.${classAndScssPrefix}-${propName} {
|
||||||
|
--border-radius: $${classAndScssPrefix}-${propName};
|
||||||
|
border-radius: $${classAndScssPrefix}-${propName};
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a border based css utility-class from a font Design Token structure
|
||||||
|
export function generateBorderUtilityClasses(prop, propName) {
|
||||||
|
let attribute;
|
||||||
|
|
||||||
|
switch (prop.attributes.type) {
|
||||||
|
case 'border-radius':
|
||||||
|
case 'border-style':
|
||||||
|
attribute = prop.attributes.type;
|
||||||
|
break;
|
||||||
|
case 'border-size':
|
||||||
|
attribute = 'border-width';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
attribute = 'border-color';
|
||||||
|
}
|
||||||
|
return `.${classAndScssPrefix}-${propName} {
|
||||||
|
--${attribute}: $${classAndScssPrefix}-${propName};
|
||||||
|
${attribute}: $${classAndScssPrefix}-${propName};
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a font based css utility-class from a font Design Token structure
|
||||||
|
export function generateFontUtilityClasses(prop, propName) {
|
||||||
|
return `.${classAndScssPrefix}-${propName} {\n ${prop.attributes.type}: $${classAndScssPrefix}-${propName};\n}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a valid box-shadow value from a shadow Design Token structure
|
||||||
|
export function generateShadowUtilityClasses(propName) {
|
||||||
|
return `.${classAndScssPrefix}-${propName} {
|
||||||
|
--box-shadow: $${classAndScssPrefix}-${propName};
|
||||||
|
box-shadow: $${classAndScssPrefix}-${propName};
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a utility class for a given token category and name
|
||||||
|
export function generateUtilityClasses(tokenCategory, propName){
|
||||||
|
return `.${classAndScssPrefix}-${propName} {\n ${tokenCategory}: $${classAndScssPrefix}-${propName};\n}`;
|
||||||
|
}
|
||||||
@ -8,9 +8,8 @@
|
|||||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||||
/>
|
/>
|
||||||
<link href="../../../../../css/ionic/bundle.ionic.css" rel="stylesheet" />
|
<link href="../../../../../css/ionic/bundle.ionic.css" rel="stylesheet" />
|
||||||
<!-- TODO(ROU-10833): remove this cdn for the font, when the font loading is solved -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet" />
|
|
||||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||||
|
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 118 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 173 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 175 KiB |
@ -1,6 +1,5 @@
|
|||||||
/*
|
// Do not edit directly, this file was auto-generated.
|
||||||
Do not edit directly, this file was auto-generated.
|
|
||||||
*/
|
|
||||||
@import "./ionic.vars";
|
@import "./ionic.vars";
|
||||||
@import "../themes/mixins";
|
@import "../themes/mixins";
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user