-
Notifications
You must be signed in to change notification settings - Fork 13.4k
feat(theme): add ionic colors #30920
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,169 @@ | ||
| import * as colorTokens from 'outsystems-design-tokens/tokens/color scheme.json'; | ||
| import * as primitiveTokens from 'outsystems-design-tokens/tokens/primitives.json'; | ||
| import * as lightTokens from 'outsystems-design-tokens/tokens/theme/light.json'; | ||
| import * as typographyTokens from 'outsystems-design-tokens/tokens/typography.json'; | ||
|
|
||
| import { cachedResolveOsToken } from '../../utils/theme'; | ||
| import type { LightTheme } from '../themes.interfaces'; | ||
|
|
||
| const tokenMap = { | ||
| colorTokens, | ||
| lightTokens, | ||
| primitiveTokens, | ||
| typographyTokens, | ||
| }; | ||
|
|
||
| export const lightTheme: LightTheme = { | ||
| backgroundColor: '#ffffff', | ||
| textColor: '#000000', | ||
|
|
||
| color: { | ||
| primary: { | ||
| bold: { | ||
| base: cachedResolveOsToken(colorTokens.bg.primary.base.default, tokenMap), | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the export request is approved, then we can simply replace this with the correct tokens. |
||
| contrast: cachedResolveOsToken(colorTokens.text.inverse, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.primary, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.primary.base.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.primary['600'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.primary.subtle.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.primary, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.primary, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.primary.subtle.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.primary['200'], tokenMap), | ||
| }, | ||
| }, | ||
| secondary: { | ||
| bold: { | ||
| base: cachedResolveOsToken(colorTokens.bg.info.base.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.inverse, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.primary, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.info.base.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.info['700'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.info.subtle.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.info, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.info, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.info.subtle.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.info['200'], tokenMap), | ||
| }, | ||
| }, | ||
| tertiary: { | ||
| bold: { | ||
| base: cachedResolveOsToken(lightTokens.primitives.violet['700'], tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.inverse, tokenMap), | ||
| foreground: cachedResolveOsToken(lightTokens.primitives.violet['700'], tokenMap), | ||
| shade: cachedResolveOsToken(lightTokens.primitives.violet['800'], tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.primary['600'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(lightTokens.primitives.violet['100'], tokenMap), | ||
| contrast: cachedResolveOsToken(lightTokens.primitives.violet['700'], tokenMap), | ||
| foreground: cachedResolveOsToken(lightTokens.primitives.violet['700'], tokenMap), | ||
| shade: cachedResolveOsToken(lightTokens.primitives.violet['300'], tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.violet['200'], tokenMap), | ||
| }, | ||
| }, | ||
| success: { | ||
| bold: { | ||
| base: cachedResolveOsToken(colorTokens.bg.success.base.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.inverse, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.success, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.success.base.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.success['800'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.success.subtle.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.success, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.success, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.success.subtle.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.success['200'], tokenMap), | ||
| }, | ||
| }, | ||
| warning: { | ||
| bold: { | ||
| base: cachedResolveOsToken(colorTokens.bg.warning.base.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.warning, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.warning.base.press, tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.yellow['300'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.warning.subtle.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.warning, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.warning, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.warning.subtle.press, tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.yellow['100'], tokenMap), | ||
| }, | ||
| }, | ||
| danger: { | ||
| bold: { | ||
| base: cachedResolveOsToken(colorTokens.bg.danger.base.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.inverse, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.danger, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.danger.base.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.danger['700'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.danger.subtle.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.danger, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.danger, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.danger.subtle.press, tokenMap), | ||
| tint: cachedResolveOsToken(colorTokens.semantics.danger['200'], tokenMap), | ||
| }, | ||
| }, | ||
| light: { | ||
| bold: { | ||
| base: cachedResolveOsToken(lightTokens.primitives.base.white, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| shade: cachedResolveOsToken(lightTokens.primitives.neutral['600'], tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.neutral['400'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.neutral.subtlest.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.neutral.subtlest.press, tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.neutral['100'], tokenMap), | ||
| }, | ||
| }, | ||
| medium: { | ||
| bold: { | ||
| base: cachedResolveOsToken(colorTokens.bg.neutral.bold.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.inverse, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.neutral.bold.press, tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.neutral['900'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.neutral.subtle.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.subtlest, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.neutral.subtle.press, tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.neutral['100'], tokenMap), | ||
| }, | ||
| }, | ||
| dark: { | ||
| bold: { | ||
| base: cachedResolveOsToken(colorTokens.bg.neutral.boldest.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.inverse, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.neutral.boldest.press, tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.neutral['1100'], tokenMap), | ||
| }, | ||
| subtle: { | ||
| base: cachedResolveOsToken(colorTokens.bg.neutral.subtle.default, tokenMap), | ||
| contrast: cachedResolveOsToken(colorTokens.text.subtle, tokenMap), | ||
| foreground: cachedResolveOsToken(colorTokens.text.default, tokenMap), | ||
| shade: cachedResolveOsToken(colorTokens.bg.neutral.subtle.press, tokenMap), | ||
| tint: cachedResolveOsToken(lightTokens.primitives.neutral['100'], tokenMap), | ||
| }, | ||
| }, | ||
| }, | ||
|
|
||
| components: { | ||
| IonCard: { | ||
| background: '#ffffff', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -471,3 +471,86 @@ export const mix = (baseColor: string, mixColor: string, weight: string): string | |
| const toHex = (n: number) => n.toString(16).padStart(2, '0'); | ||
| return `#${toHex(r)}${toHex(g)}${toHex(b)}`; | ||
| }; | ||
|
|
||
| // Create a cache to store results | ||
| const cache = new Map<any, any>(); | ||
|
|
||
| export const cachedResolveOsToken = (tokenPath: any, tokenMap: Record<string, any>): any => { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in my previous comment, we only have the JSON files to access the |
||
| // Use the path/object as the key | ||
| // (Note: For objects, this caches by reference) | ||
| if (cache.has(tokenPath)) { | ||
| return cache.get(tokenPath); | ||
| } | ||
|
|
||
| // Use your existing resolveOsToken function with the global tokenMap | ||
| const result = resolveOsToken(tokenPath, tokenMap); | ||
|
|
||
| cache.set(tokenPath, result); | ||
| return result; | ||
| }; | ||
|
|
||
| export const resolveOsToken = (tokenPath: any, tokenMap: Record<string, any>): any => { | ||
| // 1. Handle Objects (like Typography maps) | ||
| if (typeof tokenPath === 'object' && tokenPath !== null) { | ||
| // If it's a leaf-node token object, unwrap the $value immediately | ||
| if ('$value' in tokenPath) { | ||
| return resolveOsToken(tokenPath.$value, tokenMap); | ||
| } | ||
|
|
||
| // Otherwise, it's a map of multiple tokens, resolve each key | ||
| const resolvedObject: Record<string, any> = {}; | ||
| for (const [key, val] of Object.entries(tokenPath)) { | ||
| resolvedObject[key] = resolveOsToken(val, tokenMap); | ||
| } | ||
| return resolvedObject; | ||
| } | ||
|
|
||
| // 2. Handle Reference Strings: "{category.path.item}" | ||
| let lookupPath = tokenPath; | ||
| let isPath = false; | ||
|
|
||
| if (typeof tokenPath === 'string' && tokenPath.startsWith('{') && tokenPath.endsWith('}')) { | ||
| const reference = tokenPath.slice(1, -1).trim(); | ||
| const [refCategory, ...refPath] = reference.split('.'); | ||
|
|
||
| let rootKey: string | null = null; | ||
| switch (refCategory) { | ||
| case 'semantics': | ||
| rootKey = 'colorTokens'; | ||
| break; | ||
| case 'font': | ||
| rootKey = 'primitiveTokens'; | ||
| break; | ||
| case 'primitives': | ||
| rootKey = 'lightTokens'; | ||
| break; | ||
| case 'typography': | ||
| rootKey = 'typographyTokens'; | ||
| break; | ||
| case 'scale': | ||
| rootKey = 'primitiveTokens'; | ||
| break; // Added 'scale' based on your example | ||
| } | ||
|
|
||
| if (!rootKey) return tokenPath; | ||
|
|
||
| lookupPath = `${rootKey}.${refCategory}.${refPath.join('.')}`; | ||
| isPath = true; | ||
| } | ||
|
|
||
| // 3. ONLY run the reduce if we have confirmed this is a path to be searched | ||
| if (isPath) { | ||
| const value = lookupPath.split('.').reduce((acc: any, key: string) => { | ||
| if (acc && typeof acc === 'object' && key in acc) { | ||
| return acc[key]; | ||
| } | ||
| return undefined; | ||
| }, tokenMap); | ||
|
|
||
| // Recursively resolve the result of the lookup | ||
| return resolveOsToken(value, tokenMap); | ||
| } | ||
|
|
||
| // 4. If it's not a path or a reference, it's a Literal Value (Hex, Font-stack, etc.) | ||
| return tokenPath; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,7 +34,8 @@ | |
| "@utils/*": ["src/utils/*"], | ||
| "@utils/test": ["src/utils/test/utils"], | ||
| "@global/*": ["src/global/*"] | ||
| } | ||
| }, | ||
| "resolveJsonModule": true, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in my previous comment, we only have the JSON files to access the |
||
| }, | ||
| "include": [ | ||
| "src", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The OS package does not export these tokens to be consumed in a JS/TS file. The package only provides Sass files to be consumed. As a workaround, I was able to access the original JSON files. I'll be reaching out to Bernardo requesting JS/TS exports once he comes back from vacation. In the mean time, consider this as a temporary solution in order to not be blocked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The plan was to remove this package from ionic-framework and have them set the token file on mobile UI. Is exporting these tokens part of the plan?