Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tokens): add new tokens json files #29525

Merged
merged 41 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4f2a72c
removed old json tokens file
BenOsodrac May 15, 2024
978fbaf
Added new JSON tokens and intial adjustements to script
BenOsodrac May 15, 2024
0648900
Change script to valid outputs
BenOsodrac May 16, 2024
c20d91c
Added new folder for tokens
BenOsodrac May 16, 2024
fe09fa5
replaced old tokens and fixed build issues
BenOsodrac May 16, 2024
eee448b
Improved utility-classes
BenOsodrac May 17, 2024
b717377
updated tokens
BenOsodrac May 17, 2024
d5beb29
fix scss order issue
BenOsodrac May 20, 2024
767cbf7
removed json files
BenOsodrac May 20, 2024
1e9c7e8
re-added correct jsons
BenOsodrac May 20, 2024
c4170fb
added theme json to specific folder
BenOsodrac May 20, 2024
3f543cc
adapted tokens sources paths
BenOsodrac May 20, 2024
964e292
Improved tokens.js code
BenOsodrac May 20, 2024
3b90b7d
Merge branch 'next' into ROU-4870
BenOsodrac May 20, 2024
14692d7
Merge from next
BenOsodrac May 20, 2024
a1752e7
Update chip snapshots to match new tokens
BenOsodrac May 20, 2024
e6877a4
adjusted ReadMe
BenOsodrac May 20, 2024
4734243
Updated top-bat snapshots
BenOsodrac May 20, 2024
2a5a0c4
update card snapshots
BenOsodrac May 20, 2024
f8ff2c5
update link snapshots
BenOsodrac May 20, 2024
9357fe5
update typography snapshots
BenOsodrac May 20, 2024
94d1e77
Added html generated preview for tokens
BenOsodrac May 20, 2024
1c98eea
update lint ignore
BenOsodrac May 20, 2024
757c0d7
fixed lint for generated file
BenOsodrac May 20, 2024
944f7ce
remove html preview file and prevent its generation for now
BenOsodrac May 20, 2024
ebfb134
revert rgb lint
BenOsodrac May 21, 2024
6f62eef
Added comments
BenOsodrac May 21, 2024
854b429
updated lint on utilities
BenOsodrac May 21, 2024
2f7287f
added comment
BenOsodrac May 21, 2024
aa3e432
Added comment on rgb function
BenOsodrac May 21, 2024
88edc18
Added TODO
BenOsodrac May 21, 2024
36a9497
renamed tokens scripts
BenOsodrac May 21, 2024
ae95ee6
removed html tokens code
BenOsodrac May 21, 2024
6fc5b55
Added comments
BenOsodrac May 21, 2024
4a6a517
Added comments
BenOsodrac May 21, 2024
10b3535
Added lowecase
BenOsodrac May 21, 2024
301f598
Fixed comments format
BenOsodrac May 21, 2024
9731193
Update core/scripts/tokens/index.js
BenOsodrac May 22, 2024
38affcf
Add comment
BenOsodrac May 22, 2024
6648113
Added ReadMe
BenOsodrac May 22, 2024
9d6ea49
renamed method
BenOsodrac May 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 156 additions & 76 deletions core/scripts/tokens.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,114 @@
/* 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.
*/
// 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 customVariables = ''; // Empty for example of how we can add some extra variables, not from the tokens JSON
const variablesPrefix = 'ionic';
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved

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;
return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null;
}
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved

function generateShadowValue(shadow) {
return `${shadow.offsetX} ${shadow.offsetY} ${shadow.blur} ${shadow.spread} ${shadow.color}`;
}

function generateFontFamilyValue(prop, variableType = 'css') {
const propName = prop.name.split('-').slice(0, -1).join('-');
return variableType === 'scss'
? `$${variablesPrefix}-${propName}: var(--${variablesPrefix}-${propName}, "${prop.value}", sans-serif);`
: `--${variablesPrefix}-${propName}: "${prop.value}", sans-serif;`;
}

function generateTypographyValue(prop, dictionary) {
const typography = prop.value;
const fontSizeMap = createTypeMap(dictionary, 'font-size');
const fontWeightMap = createTypeMap(dictionary, 'font-weight');
const lineHeightMap = createTypeMap(dictionary, 'line-height');
const letterSpacingMap = createTypeMap(dictionary, 'letter-spacing');

BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
return `
$${variablesPrefix}-${prop.name}: (
font-family: $ionic-font-family,
font-size: $ionic-font-size-${fontSizeMap[typography.fontSize]},
font-weight: $ionic-font-weight-${fontWeightMap[typography.fontWeight]},
letter-spacing: $ionic-letter-spacing-${letterSpacingMap[typography.letterSpacing] || 0},
line-height: $ionic-line-height-${lineHeightMap[typography.lineHeight]},
text-transform: ${typography.textTransform},
text-decoration: ${typography.textDecoration}
);
`;
}
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved

function createTypeMap(dictionary, type) {
return Object.fromEntries(
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
Object.entries(dictionary.properties[type]).map(([key, token]) => [token.value, token.attributes.type])
);
}

function generateRgbValue(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});`
: ``
}`;
}
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved

// 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};` : ``
}`;
});
const prefixedVariables = dictionary.allProperties
.filter((prop) => prop['$type'] !== 'typography')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide a comment of how the prefixedVariables was created. It seems that it's based on the category but a clear explanation would make it easier to debug in the future.

.map((prop) => {
if (prop.attributes.category.startsWith('Elevation')) {
const cssShadow = prop.value.map(generateShadowValue).join(', ');
return `--${variablesPrefix}-${prop.name}: ${cssShadow};`;
} else if (prop.attributes.category.match('font-family')) {
return generateFontFamilyValue(prop);
} else {
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'
);
return fileHeader({ file }) + ':root {\n' + prefixedVariables.join('\n') + customVariables + '\n}\n';
},
});

// scss variables format
// 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});`
: ``
}`;
const typographyProperties = dictionary.allProperties.filter((prop) => prop['$type'] === 'typography');
const otherProperties = dictionary.allProperties.filter((prop) => prop['$type'] !== 'typography');

const sortedProperties = [...otherProperties, ...typographyProperties];

BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
const prefixedVariables = sortedProperties.map((prop) => {
if (prop.attributes.category.startsWith('Elevation')) {
const cssShadow = prop.value.map(generateShadowValue).join(', ');
return `$${variablesPrefix}-${prop.name}: var(--${variablesPrefix}-${prop.name}, ${cssShadow});`;
} else if (prop.attributes.category.match('font-family')) {
return generateFontFamilyValue(prop, 'scss');
} else if (prop['$type'] === 'typography') {
return generateTypographyValue(prop, dictionary);
} else {
return generateRgbValue(prop);
}
});

return (
fileHeader({ file }) +
prefixedVariables.join('\n') + // Join all prefixed variables with a newline
customVariables +
'\n'
);
return fileHeader({ file }) + prefixedVariables.join('\n') + customVariables + '\n';
},
});

Expand All @@ -80,41 +121,31 @@ StyleDictionary.registerFormat({
const className = `${prop.name}`;
let utilityClass = '';

if (tokenType.startsWith('Elevation')) {
return `.${variablesPrefix}-${className} {\n box-shadow: $ionic-${prop.name};\n}`;
} else if (prop['$type'] === 'typography') {
return generateTypographyUtilityClass(prop, dictionary);
} else if (prop.attributes.category.match('font-family') || tokenType === 'scale') {
return;
}
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved

switch (tokenType) {
case 'color':
utilityClass = `.${variablesPrefix}-${className} {\n color: $ionic-${prop.name};\n}
.${variablesPrefix}-background-${className} {\n background-color: $ionic-${prop.name};\n}`;
case 'state':
case 'Guidelines':
case 'Disabled':
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
case 'Hover':
case 'Pressed':
utilityClass = generateColorUtilityClasses(prop, className);
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
break;
case 'border':
const borderAttribute = prop.attributes.type === 'radius' ? 'border-radius' : 'border-width';
utilityClass = `.${variablesPrefix}-${className} {\n ${borderAttribute}: $ionic-${prop.name};\n}`;
case 'border-size':
utilityClass = `.${variablesPrefix}-${className} {\n border-width: $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}`;
utilityClass = generateFontUtilityClass(prop, className);
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`;
utilityClass = generateSpaceUtilityClasses(prop, className);
break;
default:
utilityClass = `.${variablesPrefix}-${className} {\n ${tokenType}: $ionic-${prop.name};\n}`;
Expand All @@ -131,6 +162,55 @@ StyleDictionary.registerFormat({
},
});

function generateTypographyUtilityClass(prop, dictionary) {
const typography = prop.value;
const fontSizeMap = createTypeMap(dictionary, 'font-size');
const fontWeightMap = createTypeMap(dictionary, 'font-weight');
const lineHeightMap = createTypeMap(dictionary, 'line-height');
const letterSpacingMap = createTypeMap(dictionary, 'letter-spacing');

BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
return `
.${variablesPrefix}-${prop.name} {
font-family: $ionic-font-family;
font-size: $ionic-font-size-${fontSizeMap[typography.fontSize]};
font-weight: $ionic-font-weight-${fontWeightMap[typography.fontWeight]};
letter-spacing: $ionic-letter-spacing-${letterSpacingMap[typography.letterSpacing] || 0};
line-height: $ionic-line-height-${lineHeightMap[typography.lineHeight]};
text-transform: ${typography.textTransform};
text-decoration: ${typography.textDecoration};
};
`;
}
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved

function generateColorUtilityClasses(prop, className) {
return `.${variablesPrefix}-${className} {\n color: $ionic-${prop.name};\n}
.${variablesPrefix}-background-${className} {\n background-color: $ionic-${prop.name};\n}`;
}

function generateFontUtilityClass(prop, className) {
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;
}
return `.${variablesPrefix}-${className} {\n ${fontAttribute}: $ionic-${prop.name};\n}`;
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
}

function generateSpaceUtilityClasses(prop, className) {
return `.${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($ionic-${prop.name});\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($ionic-${prop.name});\n};\n`;
}

BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved
// Make Style Dictionary comply with the $ format on properties from W3C Guidelines
const w3cTokenJsonParser = {
pattern: /\.json|\.tokens\.json|\.tokens$/,
Expand All @@ -150,7 +230,7 @@ StyleDictionary.registerParser(w3cTokenJsonParser);

// Generate Tokens
StyleDictionary.extend({
source: ['./src/foundations/*.json'],
source: ['./src/foundations/tokens/*.json', './src/foundations/tokens/theme/*.json'],
platforms: {
css: {
buildPath: './src/foundations/',
Expand Down
8 changes: 4 additions & 4 deletions core/src/components/button/button.ionic.scss
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,20 @@
// -------------------------------------------------------------------------------

:host(.button-soft) {
--border-radius: #{globals.$ionic-border-radius-rounded-medium};
--border-radius: #{globals.$ionic-border-radius-200};
}

:host(.button-soft.button-xsmall),
:host(.button-soft.button-small) {
--border-radius: #{globals.$ionic-border-radius-rounded-small};
--border-radius: #{globals.$ionic-border-radius-100};
}

:host(.button-round) {
--border-radius: #{globals.$ionic-border-radius-rounded-full};
--border-radius: #{globals.$ionic-border-radius-full};
}

:host(.button-rectangular) {
--border-radius: #{globals.$ionic-border-radius-square};
--border-radius: #{globals.$ionic-border-radius-0};
}

// Button Focused
Expand Down
12 changes: 6 additions & 6 deletions core/src/components/card/card.ionic.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
// --------------------------------------------------

:host {
--background: #{globals.$ionic-color-neutral-0};
--border-radius: #{globals.$ionic-border-radius-rounded-small};
--background: #{globals.$ionic-color-base-white};
--border-radius: #{globals.$ionic-border-radius-100};

@include globals.padding(globals.$ionic-space-base);
@include globals.padding(globals.$ionic-space-400);
@include globals.border-radius(var(--border-radius));

display: block;

border: #{globals.$ionic-border-size-small} solid #{globals.$ionic-color-neutral-50};
border: #{globals.$ionic-border-size-025} solid #{globals.$ionic-color-neutral-500};
BenOsodrac marked this conversation as resolved.
Show resolved Hide resolved

background: var(--background);
color: var(--color);
Expand All @@ -22,9 +22,9 @@
// ---------------------------------------------

:host(.card-round) {
--border-radius: #{globals.$ionic-border-radius-rounded-large};
--border-radius: #{globals.$ionic-border-radius-400};
}

:host(.card-rectangular) {
--border-radius: #{globals.$ionic-border-radius-square};
--border-radius: #{globals.$ionic-border-radius-0};
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading