Skip to content

Commit 3fd1d5c

Browse files
feat(themes): add ionic theme tokens (#30708)
- Updates the token interface to accept a numeric scale. - Updates the base tokens to use the new interface. - Adds tokens for the `ionic` theme based on the existing design tokens. - Excludes certain keys (`enabled`, `rippleEffect`, `formHighlight`) from the generated CSS variables. - Removes the `--background` and `--ion-background-color` from the global `ionic` theme overrides, allowing it to use the dark palette. - Removes the different Ionic colors from the `ionic` theme, ensuring all of the themes use the same named (primary, secondary, etc.) colors. - Updates the testing script to pull in the tokens based on the theme & palette passed to the URL. - Adds a new npm script, `build.themes`, to generate the theme files. --------- Co-authored-by: Brandy Smith <[email protected]>
1 parent 41953b0 commit 3fd1d5c

File tree

196 files changed

+614
-496
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+614
-496
lines changed

.github/workflows/actions/build-core-stencil-prerelease/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ runs:
2929
with:
3030
name: ionic-core
3131
output: core/CoreBuild.zip
32-
paths: core/dist core/components core/src/foundations core/css core/hydrate core/loader core/src/components.d.ts
32+
paths: core/dist core/components core/src/foundations core/css core/themes core/hydrate core/loader core/src/components.d.ts

.github/workflows/actions/build-core/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ runs:
3333
output: core/CoreBuild.zip
3434
# Include generated proxy files from Stencil output targets so
3535
# framework builds can detect when they need to be updated
36-
paths: core/dist core/components core/src/foundations core/css core/hydrate core/loader core/src/components.d.ts core/api.txt packages/angular/src/directives/proxies.ts packages/angular/src/directives/proxies-list.ts packages/angular/standalone/src/directives/proxies.ts packages/vue/src/proxies.ts packages/react/src/components/proxies.ts packages/react/src/components/inner-proxies.ts packages/react/src/components/routing-proxies.ts
36+
paths: core/dist core/components core/src/foundations core/css core/themes core/hydrate core/loader core/src/components.d.ts core/api.txt packages/angular/src/directives/proxies.ts packages/angular/src/directives/proxies-list.ts packages/angular/standalone/src/directives/proxies.ts packages/vue/src/proxies.ts packages/react/src/components/proxies.ts packages/react/src/components/inner-proxies.ts packages/react/src/components/routing-proxies.ts

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ temp/
2323
core/theme-builder/
2424
core/test-components/
2525
core/css/
26+
core/themes/
2627
$RECYCLE.BIN/
2728

2829
.DS_Store

core/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@
7676
"stylelint-order": "^4.1.0"
7777
},
7878
"scripts": {
79-
"build": "npm run clean && npm run build.css && stencil build --es5 --docs-json dist/docs.json",
79+
"build": "npm run clean && npm run build.css && npm run build.themes && stencil build --es5 --docs-json dist/docs.json",
8080
"build.css": "npm run css.sass && npm run css.minify",
8181
"build.debug": "npm run clean && stencil build --debug",
8282
"build.docs.json": "stencil build --docs-json dist/docs.json",
8383
"build.tokens": "npx build.tokens --config scripts/tokens/index.mjs && npm run prettier.tokens",
84+
"build.themes": "esbuild src/themes/*/*.tokens.ts --bundle --format=esm --platform=browser --target=es2017 --outdir=themes --outbase=src/themes && esbuild src/utils/theme.ts --bundle --format=esm --platform=browser --target=es2017 --outfile=themes/utils/theme.js",
8485
"clean": "node scripts/clean.js",
8586
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
8687
"css.sass": "sass --embed-sources --style compressed src/css:./css",
@@ -94,7 +95,7 @@
9495
"prerender.e2e": "node scripts/testing/prerender.js",
9596
"prettier": "prettier \"./src/**/*.{html,ts,tsx,js,jsx,scss}\"",
9697
"prettier.tokens": "prettier \"./src/foundations/*.{scss, html}\" --write --cache",
97-
"start": "npm run build.css && stencil build --dev --watch --serve",
98+
"start": "npm run build.css && npm run build.themes && stencil build --dev --watch --serve",
9899
"test": "npm run test.spec && npm run test.e2e",
99100
"test.spec": "stencil test --spec --max-workers=2",
100101
"test.e2e": "npx playwright test",

core/scripts/clean.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ const path = require('path');
44

55
const cleanDirs = [
66
'dist',
7-
'css'
7+
'css',
8+
'themes'
89
];
910

1011
cleanDirs.forEach(dir => {

core/scripts/testing/scripts.js

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const DEFAULT_THEME = 'md';
12

23
(function() {
34

@@ -15,28 +16,15 @@
1516
}
1617

1718
/**
18-
* The term `palette` is used to as a param to match the
19-
* Ionic docs, plus here is already a `ionic:theme` query being
20-
* used for `md`, `ios`, and `ionic` themes.
21-
*/
22-
const palette = window.location.search.match(/palette=([a-z]+)/);
23-
if (palette && palette[1] !== 'light') {
24-
const linkTag = document.createElement('link');
25-
linkTag.setAttribute('rel', 'stylesheet');
26-
linkTag.setAttribute('type', 'text/css');
27-
linkTag.setAttribute('href', `/css/palettes/${palette[1]}.always.css`);
28-
document.head.appendChild(linkTag);
29-
}
30-
31-
/**
32-
* The `ionic` theme uses a different stylesheet than the `iOS` and `md` themes.
33-
* This is to ensure that the `ionic` theme is loaded when the `ionic:theme=ionic`
34-
* or when the HTML tag has the `theme="ionic"` attribute. This is useful for
35-
* the snapshot tests, where the `ionic` theme is not loaded by default.
19+
* The `theme` query param is used to load a specific theme.
20+
* This can be `ionic`, `ios`, or `md`. Default to `md` for tests.
3621
*/
37-
const themeQuery = window.location.search.match(/ionic:theme=([a-z]+)/);
22+
const themeQuery = window.location.search.match(/ionic:theme=([a-z0-9]+)/i);
3823
const themeAttr = document.documentElement.getAttribute('theme');
24+
const themeName = themeQuery?.[1] || themeAttr || DEFAULT_THEME;
3925

26+
// TODO(): Remove this when the tokens are working for all components
27+
// and the themes all use the same bundle
4028
if ((themeQuery && themeQuery[1] === 'ionic') || themeAttr === 'ionic') {
4129
const ionicThemeLinkTag = document.querySelector('link[href*="css/ionic/bundle.ionic.css"]');
4230

@@ -54,6 +42,51 @@
5442
}
5543
}
5644

45+
/**
46+
* The `palette` query param is used to load a specific palette
47+
* for the theme. This can be `light`, `dark`, `high-contrast`,
48+
* or `high-contrast-dark`. Default to `light` for tests.
49+
*/
50+
const paletteQuery = window.location.search.match(/palette=([a-z]+)/);
51+
const paletteName = paletteQuery?.[1] || 'light';
52+
53+
// Load theme tokens if the theme is valid
54+
const validThemes = ['ionic', 'ios', 'md'];
55+
if (themeName && validThemes.includes(themeName)) {
56+
loadThemeTokens(themeName, paletteName);
57+
} else if(themeName) {
58+
console.warn(
59+
`Unsupported theme "${themeName}". Supported themes are: ${validThemes.join(', ')}. Defaulting to ${DEFAULT_THEME}.`
60+
);
61+
}
62+
63+
async function loadThemeTokens(themeName, paletteName) {
64+
try {
65+
// Load the default tokens for the theme
66+
const defaultTokens = await import(`/themes/${themeName}/default.tokens.js`);
67+
const theme = defaultTokens.defaultTheme;
68+
69+
// If a specific palette is requested, modify the palette structure
70+
// to set the enabled property to 'always'
71+
if (paletteName === 'dark' && theme.palette?.dark) {
72+
theme.palette.dark.enabled = 'always';
73+
}
74+
75+
// Apply the theme tokens to Ionic config
76+
window.Ionic = window.Ionic || {};
77+
window.Ionic.config = window.Ionic.config || {};
78+
window.Ionic.config.customTheme = theme;
79+
80+
// Re-apply the global theme
81+
if (window.Ionic.config.get && window.Ionic.config.set) {
82+
const themeModule = await import('/themes/utils/theme.js');
83+
themeModule.applyGlobalTheme(theme);
84+
}
85+
} catch (error) {
86+
console.error(`Failed to load theme tokens for ${themeName}:`, error);
87+
}
88+
}
89+
5790
window.Ionic = window.Ionic || {};
5891
window.Ionic.config = window.Ionic.config || {};
5992

core/scripts/testing/styles.css

Lines changed: 3 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/src/components/accordion-group/test/expand/accordion-group.e2e.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
99
`
1010
<style>
1111
/* Background styles to show the border radius */
12-
:root {
13-
--background: #ccc7c7;
12+
.ionic {
13+
--ion-background-color: #ccc7c7;
1414
}
1515
</style>
1616
<ion-accordion-group>
@@ -47,8 +47,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
4747
`
4848
<style>
4949
/* Background styles to show the border radius */
50-
:root {
51-
--background: #ccc7c7;
50+
.ionic {
51+
--ion-background-color: #ccc7c7;
5252
}
5353
</style>
5454
<ion-accordion-group value="first">
@@ -87,8 +87,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
8787
`
8888
<style>
8989
/* Background styles to show the border radius */
90-
:root {
91-
--background: #ccc7c7;
90+
.ionic {
91+
--ion-background-color: #ccc7c7;
9292
}
9393
</style>
9494
<ion-accordion-group expand="inset">
@@ -125,8 +125,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
125125
`
126126
<style>
127127
/* Background styles to show the border radius */
128-
:root {
129-
--background: #ccc7c7;
128+
.ionic {
129+
--ion-background-color: #ccc7c7;
130130
}
131131
</style>
132132
<ion-accordion-group value="first" expand="inset">

core/src/components/accordion-group/test/shape/accordion-group.e2e.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
99
`
1010
<style>
1111
/* Background styles to show the border radius */
12-
:root {
13-
--background: #222;
12+
.ionic {
13+
--ion-background-color: #222;
1414
}
1515
</style>
1616

core/src/components/accordion/test/shape/accordion.e2e.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
99
`
1010
<style>
1111
/* Background styles to show the border radius */
12-
:root {
13-
--background: #222;
12+
.ionic {
13+
--ion-background-color: #222;
1414
}
1515
</style>
1616

0 commit comments

Comments
 (0)