From 06f42b01b0a406eb070a40af0eaf7024ca36b81a Mon Sep 17 00:00:00 2001
From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Date: Tue, 23 Sep 2025 09:59:52 -0400
Subject: [PATCH 01/22] test(utils): move spec tests into test
---
 core/src/utils/{ => test}/helpers.spec.ts |   2 +-
 core/src/utils/test/theme.spec.ts         | 474 +++++++++++++++++++++-
 core/src/utils/theme.spec.ts              | 466 ---------------------
 3 files changed, 474 insertions(+), 468 deletions(-)
 rename core/src/utils/{ => test}/helpers.spec.ts (96%)
 delete mode 100644 core/src/utils/theme.spec.ts
diff --git a/core/src/utils/helpers.spec.ts b/core/src/utils/test/helpers.spec.ts
similarity index 96%
rename from core/src/utils/helpers.spec.ts
rename to core/src/utils/test/helpers.spec.ts
index 40b4f44a9c8..42dcb616b11 100644
--- a/core/src/utils/helpers.spec.ts
+++ b/core/src/utils/test/helpers.spec.ts
@@ -1,4 +1,4 @@
-import { deepMerge, inheritAriaAttributes } from './helpers';
+import { deepMerge, inheritAriaAttributes } from '../helpers';
 
 describe('inheritAriaAttributes', () => {
   it('should inherit aria attributes', () => {
diff --git a/core/src/utils/test/theme.spec.ts b/core/src/utils/test/theme.spec.ts
index 8b28eba220a..f59c8641b6d 100644
--- a/core/src/utils/test/theme.spec.ts
+++ b/core/src/utils/test/theme.spec.ts
@@ -1,4 +1,16 @@
-import { getClassList, getClassMap } from '../theme';
+import { newSpecPage } from '@stencil/core/testing';
+
+import { CardContent } from '../../components/card-content/card-content';
+import { Chip } from '../../components/chip/chip';
+import {
+  generateComponentThemeCSS,
+  generateCSSVars,
+  generateGlobalThemeCSS,
+  getClassList,
+  getClassMap,
+  getCustomTheme,
+  injectCSS,
+} from '../theme';
 
 describe('getClassList()', () => {
   it('should parse string', () => {
@@ -62,3 +74,463 @@ describe('getClassMap()', () => {
     });
   });
 });
+
+describe('getCustomTheme', () => {
+  const baseCustomTheme = {
+    radii: {
+      sm: '14px',
+      md: '18px',
+      lg: '22px',
+    },
+    components: {
+      IonChip: {
+        hue: {
+          subtle: {
+            bg: 'red',
+            color: 'white',
+          },
+        },
+      },
+    },
+  };
+
+  const iosOverride = {
+    components: {
+      IonChip: {
+        hue: {
+          subtle: {
+            bg: 'blue',
+          },
+        },
+      },
+    },
+  };
+
+  const mdOverride = {
+    components: {
+      IonChip: {
+        hue: {
+          subtle: {
+            bg: 'green',
+          },
+        },
+      },
+    },
+  };
+
+  it('should return the custom theme if no mode overrides exist', () => {
+    const customTheme = { ...baseCustomTheme };
+
+    const result = getCustomTheme(customTheme, 'ios');
+
+    expect(result).toEqual(customTheme);
+  });
+
+  it('should combine only with ios overrides if mode is ios', () => {
+    const customTheme = {
+      ...baseCustomTheme,
+      ios: iosOverride,
+      md: mdOverride,
+    };
+
+    const result = getCustomTheme(customTheme, 'ios');
+
+    const expected = {
+      ...baseCustomTheme,
+      components: {
+        IonChip: {
+          hue: {
+            subtle: {
+              bg: 'blue',
+              color: 'white',
+            },
+          },
+        },
+      },
+    };
+
+    expect(result).toEqual(expected);
+  });
+
+  it('should combine only with md overrides if mode is md', () => {
+    const customTheme = {
+      ...baseCustomTheme,
+      ios: iosOverride,
+      md: mdOverride,
+    };
+
+    const result = getCustomTheme(customTheme, 'md');
+
+    const expected = {
+      ...baseCustomTheme,
+      components: {
+        IonChip: {
+          hue: {
+            subtle: {
+              bg: 'green',
+              color: 'white',
+            },
+          },
+        },
+      },
+    };
+
+    expect(result).toEqual(expected);
+  });
+});
+
+describe('generateCSSVars', () => {
+  it('should not generate CSS variables for an empty theme', () => {
+    const theme = {
+      palette: {
+        light: {},
+        dark: {},
+      },
+    };
+
+    const css = generateCSSVars(theme);
+    expect(css).toBe('');
+  });
+
+  it('should generate CSS variables for a given theme', () => {
+    const theme = {
+      palette: {
+        light: {},
+        dark: {
+          enabled: 'system',
+        },
+      },
+      borderWidth: {
+        sm: '4px',
+      },
+      spacing: {
+        md: '12px',
+      },
+      scaling: {
+        0: '0',
+      },
+      radii: {
+        lg: '8px',
+      },
+      dynamicFont: '-apple-system-body',
+      fontFamily: 'Roboto, "Helvetica Neue", sans-serif',
+      fontWeights: {
+        semiBold: '600',
+      },
+      fontSizes: {
+        sm: '14px',
+        md: '16px',
+      },
+      lineHeights: {
+        sm: '1.2',
+      },
+      components: {},
+    };
+
+    const css = generateCSSVars(theme);
+
+    expect(css).toContain('--ion-palette-dark-enabled: system;');
+    expect(css).toContain('--ion-border-width-sm: 4px;');
+    expect(css).toContain('--ion-spacing-md: 12px;');
+    expect(css).toContain('--ion-scaling-0: 0;');
+    expect(css).toContain('--ion-radii-lg: 8px;');
+    expect(css).toContain('--ion-dynamic-font: -apple-system-body;');
+    expect(css).toContain('--ion-font-family: Roboto, "Helvetica Neue", sans-serif;');
+    expect(css).toContain('--ion-font-weights-semi-bold: 600;');
+    expect(css).toContain('--ion-font-sizes-sm: 14px;');
+    expect(css).toContain('--ion-font-sizes-sm-rem: 0.875rem;');
+    expect(css).toContain('--ion-font-sizes-md: 16px;');
+    expect(css).toContain('--ion-font-sizes-md-rem: 1rem;');
+    expect(css).toContain('--ion-line-heights-sm: 1.2;');
+  });
+});
+
+describe('injectCSS', () => {
+  it('should inject CSS into the head', () => {
+    const css = 'body { background-color: red; }';
+    injectCSS(css);
+    expect(document.head.innerHTML).toContain(``);
+  });
+
+  it('should inject CSS into an element', async () => {
+    const page = await newSpecPage({
+      components: [CardContent],
+      html: '',
+    });
+
+    const target = page.body.querySelector('ion-card-content')!;
+
+    const css = ':host { background-color: red; }';
+    injectCSS(css, target);
+
+    expect(target.innerHTML).toContain(``);
+  });
+
+  it('should inject CSS into an element with a shadow root', async () => {
+    const page = await newSpecPage({
+      components: [Chip],
+      html: '',
+    });
+
+    const target = page.body.querySelector('ion-chip')!;
+    const shadowRoot = target.shadowRoot;
+    expect(shadowRoot).toBeTruthy();
+
+    const css = ':host { background-color: red; }';
+    injectCSS(css, shadowRoot!);
+
+    expect(shadowRoot!.innerHTML).toContain(``);
+  });
+});
+
+describe('generateGlobalThemeCSS', () => {
+  it('should generate global CSS for a given theme', () => {
+    const theme = {
+      palette: {
+        light: {},
+        dark: {
+          enabled: 'never',
+        },
+      },
+      borderWidth: {
+        sm: '4px',
+      },
+      spacing: {
+        md: '12px',
+      },
+      dynamicFont: '-apple-system-body',
+    };
+
+    const css = generateGlobalThemeCSS(theme).replace(/\s/g, '');
+
+    const expectedCSS = `
+      :root {
+        --ion-border-width-sm: 4px;
+        --ion-spacing-md: 12px;
+        --ion-dynamic-font: -apple-system-body;
+      }
+    `.replace(/\s/g, '');
+
+    expect(css).toBe(expectedCSS);
+  });
+
+  it('should generate global CSS for a given theme with light palette', () => {
+    const theme = {
+      palette: {
+        light: {
+          color: {
+            primary: {
+              bold: {
+                base: '#0054e9',
+                contrast: '#ffffff',
+                shade: '#0041c4',
+                tint: '#0065ff',
+              },
+              subtle: {
+                base: '#0054e9',
+                contrast: '#ffffff',
+                shade: '#0041c4',
+                tint: '#0065ff',
+              },
+            },
+            red: {
+              50: '#ffebee',
+              100: '#ffcdd2',
+              200: '#ef9a9a',
+            },
+          },
+        },
+        dark: {
+          enabled: 'never',
+        },
+      },
+      borderWidth: {
+        sm: '4px',
+      },
+      spacing: {
+        md: '12px',
+      },
+      dynamicFont: '-apple-system-body',
+    };
+
+    const css = generateGlobalThemeCSS(theme).replace(/\s/g, '');
+
+    const expectedCSS = `
+      :root {
+        --ion-border-width-sm: 4px;
+        --ion-spacing-md: 12px;
+        --ion-dynamic-font: -apple-system-body;
+
+        --ion-color-primary-bold: #0054e9;
+        --ion-color-primary-bold-contrast: #ffffff;
+        --ion-color-primary-bold-shade: #0041c4;
+        --ion-color-primary-bold-tint: #0065ff;
+        --ion-color-primary-subtle: #0054e9;
+        --ion-color-primary-subtle-contrast: #ffffff;
+        --ion-color-primary-subtle-shade: #0041c4;
+        --ion-color-primary-subtle-tint: #0065ff;
+        --ion-color-red-50: #ffebee;
+        --ion-color-red-100: #ffcdd2;
+        --ion-color-red-200: #ef9a9a;
+      }
+    `.replace(/\s/g, '');
+
+    expect(css).toBe(expectedCSS);
+  });
+
+  it('should not include component or palette variables in global CSS', () => {
+    const theme = {
+      palette: {
+        light: {},
+        dark: {
+          enabled: 'never',
+        },
+      },
+      borderWidth: {
+        sm: '4px',
+      },
+      spacing: {
+        md: '12px',
+      },
+      components: {
+        IonChip: {
+          hue: {
+            subtle: {
+              bg: 'red',
+            },
+          },
+          shape: {
+            round: {
+              borderRadius: '4px',
+            },
+          },
+        },
+        IonButton: {
+          color: {
+            primary: {
+              bg: 'blue',
+            },
+          },
+        },
+      },
+    };
+
+    const css = generateGlobalThemeCSS(theme);
+
+    // Should include global design tokens
+    expect(css).toContain('--ion-border-width-sm: 4px');
+    expect(css).toContain('--ion-spacing-md: 12px');
+
+    // Should NOT include component variables
+    expect(css).not.toContain('--ion-components-ion-chip-hue-subtle-bg');
+    expect(css).not.toContain('--ion-components-ion-chip-shape-round-border-radius');
+    expect(css).not.toContain('--ion-components-ion-button-color-primary-bg');
+    expect(css).not.toContain('components');
+
+    // Should NOT include palette variables
+    expect(css).not.toContain('--ion-color-palette-dark-enabled-never');
+    expect(css).not.toContain('palette');
+  });
+
+  it('should generate global CSS for a given theme with dark palette enabled for system preference', () => {
+    const theme = {
+      palette: {
+        light: {},
+        dark: {
+          enabled: 'system',
+          color: {
+            primary: {
+              bold: {
+                base: '#0054e9',
+                contrast: '#ffffff',
+                shade: '#0041c4',
+                tint: '#0065ff',
+              },
+              subtle: {
+                base: '#0054e9',
+                contrast: '#ffffff',
+                shade: '#0041c4',
+                tint: '#0065ff',
+              },
+            },
+            red: {
+              50: '#ffebee',
+              100: '#ffcdd2',
+              200: '#ef9a9a',
+            },
+          },
+        },
+      },
+      borderWidth: {
+        sm: '4px',
+      },
+      spacing: {
+        md: '12px',
+      },
+      dynamicFont: '-apple-system-body',
+    };
+
+    const css = generateGlobalThemeCSS(theme).replace(/\s/g, '');
+
+    const expectedCSS = `
+      :root {
+        --ion-border-width-sm: 4px;
+        --ion-spacing-md: 12px;
+        --ion-dynamic-font: -apple-system-body;
+      }
+
+      @media(prefers-color-scheme: dark) {
+        :root {
+          --ion-enabled: system;
+          --ion-color-primary-bold: #0054e9;
+          --ion-color-primary-bold-contrast: #ffffff;
+          --ion-color-primary-bold-shade: #0041c4;
+          --ion-color-primary-bold-tint: #0065ff;
+          --ion-color-primary-subtle: #0054e9;
+          --ion-color-primary-subtle-contrast: #ffffff;
+          --ion-color-primary-subtle-shade: #0041c4;
+          --ion-color-primary-subtle-tint: #0065ff;
+          --ion-color-red-50: #ffebee;
+          --ion-color-red-100: #ffcdd2;
+          --ion-color-red-200: #ef9a9a;
+        }
+      }
+    `.replace(/\s/g, '');
+
+    expect(css).toBe(expectedCSS);
+  });
+});
+
+describe('generateComponentThemeCSS', () => {
+  it('should generate component theme CSS for a given theme', () => {
+    const IonChip = {
+      hue: {
+        subtle: {
+          bg: 'red',
+          color: 'white',
+          borderColor: 'black',
+        },
+        bold: {
+          bg: 'blue',
+          color: 'white',
+          borderColor: 'black',
+        },
+      },
+    };
+
+    const css = generateComponentThemeCSS(IonChip, 'chip').replace(/\s/g, '');
+
+    const expectedCSS = `
+      :host(.chip-themed) {
+        --ion-chip-hue-subtle-bg: red;
+        --ion-chip-hue-subtle-color: white;
+        --ion-chip-hue-subtle-border-color: black;
+        --ion-chip-hue-bold-bg: blue;
+        --ion-chip-hue-bold-color: white;
+        --ion-chip-hue-bold-border-color: black;
+      }
+    `.replace(/\s/g, '');
+
+    expect(css).toBe(expectedCSS);
+  });
+});
diff --git a/core/src/utils/theme.spec.ts b/core/src/utils/theme.spec.ts
deleted file mode 100644
index 9a5fb6d38f3..00000000000
--- a/core/src/utils/theme.spec.ts
+++ /dev/null
@@ -1,466 +0,0 @@
-import { newSpecPage } from '@stencil/core/testing';
-
-import { CardContent } from '../components/card-content/card-content';
-import { Chip } from '../components/chip/chip';
-
-import { generateComponentThemeCSS, generateCSSVars, generateGlobalThemeCSS, getCustomTheme, injectCSS } from './theme';
-
-describe('getCustomTheme', () => {
-  const baseCustomTheme = {
-    radii: {
-      sm: '14px',
-      md: '18px',
-      lg: '22px',
-    },
-    components: {
-      IonChip: {
-        hue: {
-          subtle: {
-            bg: 'red',
-            color: 'white',
-          },
-        },
-      },
-    },
-  };
-
-  const iosOverride = {
-    components: {
-      IonChip: {
-        hue: {
-          subtle: {
-            bg: 'blue',
-          },
-        },
-      },
-    },
-  };
-
-  const mdOverride = {
-    components: {
-      IonChip: {
-        hue: {
-          subtle: {
-            bg: 'green',
-          },
-        },
-      },
-    },
-  };
-
-  it('should return the custom theme if no mode overrides exist', () => {
-    const customTheme = { ...baseCustomTheme };
-
-    const result = getCustomTheme(customTheme, 'ios');
-
-    expect(result).toEqual(customTheme);
-  });
-
-  it('should combine only with ios overrides if mode is ios', () => {
-    const customTheme = {
-      ...baseCustomTheme,
-      ios: iosOverride,
-      md: mdOverride,
-    };
-
-    const result = getCustomTheme(customTheme, 'ios');
-
-    const expected = {
-      ...baseCustomTheme,
-      components: {
-        IonChip: {
-          hue: {
-            subtle: {
-              bg: 'blue',
-              color: 'white',
-            },
-          },
-        },
-      },
-    };
-
-    expect(result).toEqual(expected);
-  });
-
-  it('should combine only with md overrides if mode is md', () => {
-    const customTheme = {
-      ...baseCustomTheme,
-      ios: iosOverride,
-      md: mdOverride,
-    };
-
-    const result = getCustomTheme(customTheme, 'md');
-
-    const expected = {
-      ...baseCustomTheme,
-      components: {
-        IonChip: {
-          hue: {
-            subtle: {
-              bg: 'green',
-              color: 'white',
-            },
-          },
-        },
-      },
-    };
-
-    expect(result).toEqual(expected);
-  });
-});
-
-describe('generateCSSVars', () => {
-  it('should not generate CSS variables for an empty theme', () => {
-    const theme = {
-      palette: {
-        light: {},
-        dark: {},
-      },
-    };
-
-    const css = generateCSSVars(theme);
-    expect(css).toBe('');
-  });
-
-  it('should generate CSS variables for a given theme', () => {
-    const theme = {
-      palette: {
-        light: {},
-        dark: {
-          enabled: 'system',
-        },
-      },
-      borderWidth: {
-        sm: '4px',
-      },
-      spacing: {
-        md: '12px',
-      },
-      scaling: {
-        0: '0',
-      },
-      radii: {
-        lg: '8px',
-      },
-      dynamicFont: '-apple-system-body',
-      fontFamily: 'Roboto, "Helvetica Neue", sans-serif',
-      fontWeights: {
-        semiBold: '600',
-      },
-      fontSizes: {
-        sm: '14px',
-        md: '16px',
-      },
-      lineHeights: {
-        sm: '1.2',
-      },
-      components: {},
-    };
-
-    const css = generateCSSVars(theme);
-
-    expect(css).toContain('--ion-palette-dark-enabled: system;');
-    expect(css).toContain('--ion-border-width-sm: 4px;');
-    expect(css).toContain('--ion-spacing-md: 12px;');
-    expect(css).toContain('--ion-scaling-0: 0;');
-    expect(css).toContain('--ion-radii-lg: 8px;');
-    expect(css).toContain('--ion-dynamic-font: -apple-system-body;');
-    expect(css).toContain('--ion-font-family: Roboto, "Helvetica Neue", sans-serif;');
-    expect(css).toContain('--ion-font-weights-semi-bold: 600;');
-    expect(css).toContain('--ion-font-sizes-sm: 14px;');
-    expect(css).toContain('--ion-font-sizes-sm-rem: 0.875rem;');
-    expect(css).toContain('--ion-font-sizes-md: 16px;');
-    expect(css).toContain('--ion-font-sizes-md-rem: 1rem;');
-    expect(css).toContain('--ion-line-heights-sm: 1.2;');
-  });
-});
-
-describe('injectCSS', () => {
-  it('should inject CSS into the head', () => {
-    const css = 'body { background-color: red; }';
-    injectCSS(css);
-    expect(document.head.innerHTML).toContain(``);
-  });
-
-  it('should inject CSS into an element', async () => {
-    const page = await newSpecPage({
-      components: [CardContent],
-      html: '',
-    });
-
-    const target = page.body.querySelector('ion-card-content')!;
-
-    const css = ':host { background-color: red; }';
-    injectCSS(css, target);
-
-    expect(target.innerHTML).toContain(``);
-  });
-
-  it('should inject CSS into an element with a shadow root', async () => {
-    const page = await newSpecPage({
-      components: [Chip],
-      html: '',
-    });
-
-    const target = page.body.querySelector('ion-chip')!;
-    const shadowRoot = target.shadowRoot;
-    expect(shadowRoot).toBeTruthy();
-
-    const css = ':host { background-color: red; }';
-    injectCSS(css, shadowRoot!);
-
-    expect(shadowRoot!.innerHTML).toContain(``);
-  });
-});
-
-describe('generateGlobalThemeCSS', () => {
-  it('should generate global CSS for a given theme', () => {
-    const theme = {
-      palette: {
-        light: {},
-        dark: {
-          enabled: 'never',
-        },
-      },
-      borderWidth: {
-        sm: '4px',
-      },
-      spacing: {
-        md: '12px',
-      },
-      dynamicFont: '-apple-system-body',
-    };
-
-    const css = generateGlobalThemeCSS(theme).replace(/\s/g, '');
-
-    const expectedCSS = `
-      :root {
-        --ion-border-width-sm: 4px;
-        --ion-spacing-md: 12px;
-        --ion-dynamic-font: -apple-system-body;
-      }
-    `.replace(/\s/g, '');
-
-    expect(css).toBe(expectedCSS);
-  });
-
-  it('should generate global CSS for a given theme with light palette', () => {
-    const theme = {
-      palette: {
-        light: {
-          color: {
-            primary: {
-              bold: {
-                base: '#0054e9',
-                contrast: '#ffffff',
-                shade: '#0041c4',
-                tint: '#0065ff',
-              },
-              subtle: {
-                base: '#0054e9',
-                contrast: '#ffffff',
-                shade: '#0041c4',
-                tint: '#0065ff',
-              },
-            },
-            red: {
-              50: '#ffebee',
-              100: '#ffcdd2',
-              200: '#ef9a9a',
-            },
-          },
-        },
-        dark: {
-          enabled: 'never',
-        },
-      },
-      borderWidth: {
-        sm: '4px',
-      },
-      spacing: {
-        md: '12px',
-      },
-      dynamicFont: '-apple-system-body',
-    };
-
-    const css = generateGlobalThemeCSS(theme).replace(/\s/g, '');
-
-    const expectedCSS = `
-      :root {
-        --ion-border-width-sm: 4px;
-        --ion-spacing-md: 12px;
-        --ion-dynamic-font: -apple-system-body;
-
-        --ion-color-primary-bold: #0054e9;
-        --ion-color-primary-bold-contrast: #ffffff;
-        --ion-color-primary-bold-shade: #0041c4;
-        --ion-color-primary-bold-tint: #0065ff;
-        --ion-color-primary-subtle: #0054e9;
-        --ion-color-primary-subtle-contrast: #ffffff;
-        --ion-color-primary-subtle-shade: #0041c4;
-        --ion-color-primary-subtle-tint: #0065ff;
-        --ion-color-red-50: #ffebee;
-        --ion-color-red-100: #ffcdd2;
-        --ion-color-red-200: #ef9a9a;
-      }
-    `.replace(/\s/g, '');
-
-    expect(css).toBe(expectedCSS);
-  });
-
-  it('should not include component or palette variables in global CSS', () => {
-    const theme = {
-      palette: {
-        light: {},
-        dark: {
-          enabled: 'never',
-        },
-      },
-      borderWidth: {
-        sm: '4px',
-      },
-      spacing: {
-        md: '12px',
-      },
-      components: {
-        IonChip: {
-          hue: {
-            subtle: {
-              bg: 'red',
-            },
-          },
-          shape: {
-            round: {
-              borderRadius: '4px',
-            },
-          },
-        },
-        IonButton: {
-          color: {
-            primary: {
-              bg: 'blue',
-            },
-          },
-        },
-      },
-    };
-
-    const css = generateGlobalThemeCSS(theme);
-
-    // Should include global design tokens
-    expect(css).toContain('--ion-border-width-sm: 4px');
-    expect(css).toContain('--ion-spacing-md: 12px');
-
-    // Should NOT include component variables
-    expect(css).not.toContain('--ion-components-ion-chip-hue-subtle-bg');
-    expect(css).not.toContain('--ion-components-ion-chip-shape-round-border-radius');
-    expect(css).not.toContain('--ion-components-ion-button-color-primary-bg');
-    expect(css).not.toContain('components');
-
-    // Should NOT include palette variables
-    expect(css).not.toContain('--ion-color-palette-dark-enabled-never');
-    expect(css).not.toContain('palette');
-  });
-
-  it('should generate global CSS for a given theme with dark palette enabled for system preference', () => {
-    const theme = {
-      palette: {
-        light: {},
-        dark: {
-          enabled: 'system',
-          color: {
-            primary: {
-              bold: {
-                base: '#0054e9',
-                contrast: '#ffffff',
-                shade: '#0041c4',
-                tint: '#0065ff',
-              },
-              subtle: {
-                base: '#0054e9',
-                contrast: '#ffffff',
-                shade: '#0041c4',
-                tint: '#0065ff',
-              },
-            },
-            red: {
-              50: '#ffebee',
-              100: '#ffcdd2',
-              200: '#ef9a9a',
-            },
-          },
-        },
-      },
-      borderWidth: {
-        sm: '4px',
-      },
-      spacing: {
-        md: '12px',
-      },
-      dynamicFont: '-apple-system-body',
-    };
-
-    const css = generateGlobalThemeCSS(theme).replace(/\s/g, '');
-
-    const expectedCSS = `
-      :root {
-        --ion-border-width-sm: 4px;
-        --ion-spacing-md: 12px;
-        --ion-dynamic-font: -apple-system-body;
-      }
-
-      @media(prefers-color-scheme: dark) {
-        :root {
-          --ion-enabled: system;
-          --ion-color-primary-bold: #0054e9;
-          --ion-color-primary-bold-contrast: #ffffff;
-          --ion-color-primary-bold-shade: #0041c4;
-          --ion-color-primary-bold-tint: #0065ff;
-          --ion-color-primary-subtle: #0054e9;
-          --ion-color-primary-subtle-contrast: #ffffff;
-          --ion-color-primary-subtle-shade: #0041c4;
-          --ion-color-primary-subtle-tint: #0065ff;
-          --ion-color-red-50: #ffebee;
-          --ion-color-red-100: #ffcdd2;
-          --ion-color-red-200: #ef9a9a;
-        }
-      }
-    `.replace(/\s/g, '');
-
-    expect(css).toBe(expectedCSS);
-  });
-});
-
-describe('generateComponentThemeCSS', () => {
-  it('should generate component theme CSS for a given theme', () => {
-    const IonChip = {
-      hue: {
-        subtle: {
-          bg: 'red',
-          color: 'white',
-          borderColor: 'black',
-        },
-        bold: {
-          bg: 'blue',
-          color: 'white',
-          borderColor: 'black',
-        },
-      },
-    };
-
-    const css = generateComponentThemeCSS(IonChip, 'chip').replace(/\s/g, '');
-
-    const expectedCSS = `
-      :host(.chip-themed) {
-        --ion-chip-hue-subtle-bg: red;
-        --ion-chip-hue-subtle-color: white;
-        --ion-chip-hue-subtle-border-color: black;
-        --ion-chip-hue-bold-bg: blue;
-        --ion-chip-hue-bold-color: white;
-        --ion-chip-hue-bold-border-color: black;
-      }
-    `.replace(/\s/g, '');
-
-    expect(css).toBe(expectedCSS);
-  });
-});
From f565952b829a2ac00870a8db0786f6d3984158f8 Mon Sep 17 00:00:00 2001
From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Date: Tue, 23 Sep 2025 16:02:40 -0400
Subject: [PATCH 02/22] test(themes): add basic, color and typography tests
---
 core/src/themes/test/basic/index.html      | 288 +++++++++++++++++++++
 core/src/themes/test/color/index.html      | 239 +++++++++++++++++
 core/src/themes/test/typography/index.html | 191 ++++++++++++++
 3 files changed, 718 insertions(+)
 create mode 100644 core/src/themes/test/basic/index.html
 create mode 100644 core/src/themes/test/color/index.html
 create mode 100644 core/src/themes/test/typography/index.html
diff --git a/core/src/themes/test/basic/index.html b/core/src/themes/test/basic/index.html
new file mode 100644
index 00000000000..ec328d34dd7
--- /dev/null
+++ b/core/src/themes/test/basic/index.html
@@ -0,0 +1,288 @@
+
+
+  
+    
+    Themes - Basic
+    
+    
+    
+    
+    
+
+    
+
+    
+  
+
+  
+    
+      
+        
+          Themes - Basic
+        
+      
+
+      
+        
+          
+            
Scaling
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+          
+          
+            
Spacing
+            
+            
+            
+            
+            
+            
+            
+            
+          
+          
+            
Radii
+            
none
+            
xxs
+            
xs
+            
sm
+            
md
+            
lg
+            
xl
+            
xxl
+          
+          
+            
Border Width
+            
none
+            
xxs
+            
xs
+            
sm
+            
md
+            
lg
+            
xl
+            
xxl
+          
+        
 
+      
+    
+  
+
diff --git a/core/src/themes/test/color/index.html b/core/src/themes/test/color/index.html
new file mode 100644
index 00000000000..fb186978291
--- /dev/null
+++ b/core/src/themes/test/color/index.html
@@ -0,0 +1,239 @@
+
+
+  
+    
+    Themes - Color
+    
+    
+    
+    
+    
+
+    
+
+    
+  
+
+  
+    
+      
+        
+          Themes - Color
+        
+      
+
+      
+        
+          
+            
Bold Colors
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+          
+
+          
+            
Subtle Colors
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+
+            
+              Base
+              Shade
+              Tint
+              Foreground
+            
+          
+        
 
+      
+    
+  
+
diff --git a/core/src/themes/test/typography/index.html b/core/src/themes/test/typography/index.html
new file mode 100644
index 00000000000..8c2b7d3557b
--- /dev/null
+++ b/core/src/themes/test/typography/index.html
@@ -0,0 +1,191 @@
+
+
+  
+    
+    Themes - Typography
+    
+    
+    
+    
+    
+
+    
+
+    
+  
+
+  
+    
+      
+        
+          Themes - Typography
+        
+      
+
+      
+        
+          
+            
Font Size
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+          
+          
+            
Font Weight
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
+          
+          
+            
Line Height
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+            
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
+          
+        
 
+      
+    
+  
+
From 58b006d61cb41c7a10cb8dce128f229466de7a18 Mon Sep 17 00:00:00 2001
From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Date: Tue, 23 Sep 2025 16:03:15 -0400
Subject: [PATCH 03/22] feat(themes): define base tokens, color functions and
 interfaces
---
 core/src/global/ionic-global.ts        |   2 +-
 core/src/themes/base/dark.tokens.ts    | 164 ++++++++++++++++++++++
 core/src/themes/base/default.tokens.ts | 105 +++++++++++++-
 core/src/themes/base/light.tokens.ts   | 163 +++++++++++++++++++++
 core/src/themes/themes.interfaces.ts   | 145 +++++++++++++++++++
 core/src/utils/theme.ts                | 187 ++++++++++++++++++++++++-
 6 files changed, 757 insertions(+), 9 deletions(-)
 create mode 100644 core/src/themes/themes.interfaces.ts
diff --git a/core/src/global/ionic-global.ts b/core/src/global/ionic-global.ts
index 95e788aeb58..827c1810847 100644
--- a/core/src/global/ionic-global.ts
+++ b/core/src/global/ionic-global.ts
@@ -4,7 +4,7 @@ import { applyGlobalTheme, getCustomTheme } from '@utils/theme';
 
 import type { IonicConfig, Mode, Theme } from '../interface';
 import { defaultTheme as baseTheme } from '../themes/base/default.tokens';
-import type { Theme as BaseTheme } from '../themes/base/default.tokens';
+import type { BaseTheme } from '../themes/themes.interfaces';
 import { shouldUseCloseWatcher } from '../utils/hardware-back-button';
 import { isPlatform, setupPlatforms } from '../utils/platform';
 
diff --git a/core/src/themes/base/dark.tokens.ts b/core/src/themes/base/dark.tokens.ts
index e69de29bb2d..529fbed397d 100644
--- a/core/src/themes/base/dark.tokens.ts
+++ b/core/src/themes/base/dark.tokens.ts
@@ -0,0 +1,164 @@
+import { mix } from '../../utils/theme';
+import type { DarkTheme } from '../themes.interfaces';
+
+const colors = {
+  primary: '#4d8dff',
+  secondary: '#46b1ff',
+  tertiary: '#8482fb',
+  success: '#2dd55b',
+  warning: '#ffce31',
+  danger: '#f24c58',
+  light: '#222428',
+  medium: '#989aa2',
+  dark: '#f4f5f8',
+};
+
+export const darkTheme: DarkTheme = {
+  enabled: 'never',
+  color: {
+    primary: {
+      bold: {
+        base: colors.primary,
+        contrast: '#fff',
+        foreground: mix(colors.primary, '#000', '4%'),
+        shade: mix(colors.primary, '#000', '4%'),
+        tint: mix(colors.primary, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.primary, '8%'),
+        contrast: colors.primary,
+        foreground: mix(colors.primary, '#000', '4%'),
+        shade: mix('#000', colors.primary, '4%'),
+        tint: mix('#000', colors.primary, '12%'),
+      },
+    },
+    secondary: {
+      bold: {
+        base: colors.secondary,
+        contrast: '#fff',
+        foreground: mix(colors.secondary, '#000', '4%'),
+        shade: mix(colors.secondary, '#000', '4%'),
+        tint: mix(colors.secondary, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.secondary, '8%'),
+        contrast: colors.secondary,
+        foreground: mix(colors.secondary, '#000', '4%'),
+        shade: mix('#000', colors.secondary, '4%'),
+        tint: mix('#000', colors.secondary, '12%'),
+      },
+    },
+    tertiary: {
+      bold: {
+        base: colors.tertiary,
+        contrast: '#fff',
+        foreground: mix(colors.tertiary, '#000', '4%'),
+        shade: mix(colors.tertiary, '#000', '4%'),
+        tint: mix(colors.tertiary, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.tertiary, '8%'),
+        contrast: colors.tertiary,
+        foreground: mix(colors.tertiary, '#000', '4%'),
+        shade: mix('#000', colors.tertiary, '4%'),
+        tint: mix('#000', colors.tertiary, '12%'),
+      },
+    },
+    success: {
+      bold: {
+        base: colors.success,
+        contrast: '#fff',
+        foreground: mix(colors.success, '#000', '4%'),
+        shade: mix(colors.success, '#000', '4%'),
+        tint: mix(colors.success, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.success, '8%'),
+        contrast: colors.success,
+        foreground: mix(colors.success, '#000', '4%'),
+        shade: mix('#000', colors.success, '4%'),
+        tint: mix('#000', colors.success, '12%'),
+      },
+    },
+    warning: {
+      bold: {
+        base: colors.warning,
+        contrast: '#fff',
+        foreground: mix(colors.warning, '#000', '4%'),
+        shade: mix(colors.warning, '#000', '4%'),
+        tint: mix(colors.warning, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.warning, '8%'),
+        contrast: colors.warning,
+        foreground: mix(colors.warning, '#000', '4%'),
+        shade: mix('#000', colors.warning, '4%'),
+        tint: mix('#000', colors.warning, '12%'),
+      },
+    },
+    danger: {
+      bold: {
+        base: colors.danger,
+        contrast: '#fff',
+        foreground: mix(colors.danger, '#000', '4%'),
+        shade: mix(colors.danger, '#000', '4%'),
+        tint: mix(colors.danger, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.danger, '8%'),
+        contrast: colors.danger,
+        foreground: mix(colors.danger, '#000', '4%'),
+        shade: mix('#000', colors.danger, '4%'),
+        tint: mix('#000', colors.danger, '12%'),
+      },
+    },
+    light: {
+      bold: {
+        base: colors.light,
+        contrast: '#000',
+        foreground: mix(colors.light, '#000', '4%'),
+        shade: mix(colors.light, '#000', '4%'),
+        tint: mix(colors.light, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.light, '8%'),
+        contrast: colors.light,
+        foreground: mix(colors.light, '#000', '4%'),
+        shade: mix('#000', colors.light, '4%'),
+        tint: mix('#000', colors.light, '12%'),
+      },
+    },
+    medium: {
+      bold: {
+        base: colors.medium,
+        contrast: '#fff',
+        foreground: mix(colors.medium, '#000', '4%'),
+        shade: mix(colors.medium, '#000', '4%'),
+        tint: mix(colors.medium, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.medium, '8%'),
+        contrast: colors.medium,
+        foreground: mix(colors.medium, '#000', '4%'),
+        shade: mix('#000', colors.medium, '4%'),
+        tint: mix('#000', colors.medium, '12%'),
+      },
+    },
+    dark: {
+      bold: {
+        base: colors.dark,
+        contrast: '#fff',
+        foreground: mix(colors.dark, '#000', '4%'),
+        shade: mix(colors.dark, '#000', '4%'),
+        tint: mix(colors.dark, '#fff', '12%'),
+      },
+      subtle: {
+        base: mix('#000', colors.dark, '8%'),
+        contrast: colors.dark,
+        foreground: mix(colors.dark, '#000', '4%'),
+        shade: mix('#000', colors.dark, '4%'),
+        tint: mix('#000', colors.dark, '12%'),
+      },
+    },
+  },
+};
diff --git a/core/src/themes/base/default.tokens.ts b/core/src/themes/base/default.tokens.ts
index 7278514d484..979be9dcd46 100644
--- a/core/src/themes/base/default.tokens.ts
+++ b/core/src/themes/base/default.tokens.ts
@@ -1,10 +1,101 @@
-export const defaultTheme = {
+import type { DefaultTheme } from '../themes.interfaces';
+
+import { darkTheme } from './dark.tokens';
+import { lightTheme } from './light.tokens';
+
+export const defaultTheme: DefaultTheme = {
   palette: {
-    light: {},
-    dark: {
-      enabled: 'system',
-    },
+    light: lightTheme,
+    dark: darkTheme,
+  },
+
+  spacing: {
+    none: '0',
+    xxs: '4px',
+    xs: '6px',
+    sm: '8px',
+    md: '12px',
+    lg: '16px',
+    xl: '24px',
+    xxl: '32px',
+  },
+
+  scaling: {
+    0: '0',
+    100: '4px',
+    150: '6px',
+    200: '8px',
+    250: '10px',
+    300: '12px',
+    350: '14px',
+    400: '16px',
+    450: '18px',
+    500: '20px',
+    550: '22px',
+    600: '24px',
+    650: '26px',
+    700: '28px',
+    750: '30px',
+    800: '32px',
+    850: '34px',
+    900: '36px',
+  },
+
+  borderWidth: {
+    none: '0',
+    xxs: '1px',
+    xs: '2px',
+    sm: '4px',
+    md: '6px',
+    lg: '8px',
+    xl: '10px',
+    xxl: '12px',
   },
-};
 
-export type Theme = typeof defaultTheme;
+  radii: {
+    none: '0',
+    xxs: '1px',
+    xs: '2px',
+    sm: '4px',
+    md: '8px',
+    lg: '12px',
+    xl: '16px',
+    xxl: '32px',
+  },
+
+  fontFamily: 'Roboto, "Helvetica Neue", sans-serif',
+  dynamicFont: '-apple-system-body',
+
+  fontSize: {
+    root: '16px',
+    xxs: '10px',
+    xs: '12px',
+    sm: '14px',
+    md: '16px',
+    lg: '18px',
+    xl: '20px',
+    xxl: '24px',
+  },
+
+  fontWeight: {
+    thin: '100',
+    extraLight: '200',
+    light: '300',
+    normal: '400',
+    medium: '500',
+    semiBold: '600',
+    bold: '700',
+    extraBold: '800',
+    black: '900',
+  },
+
+  lineHeight: {
+    xxs: '1',
+    xs: '1.2',
+    sm: '1.4',
+    md: '1.6',
+    lg: '1.8',
+    xl: '2',
+    xxl: '2.4',
+  },
+};
diff --git a/core/src/themes/base/light.tokens.ts b/core/src/themes/base/light.tokens.ts
index e69de29bb2d..c87499e2839 100644
--- a/core/src/themes/base/light.tokens.ts
+++ b/core/src/themes/base/light.tokens.ts
@@ -0,0 +1,163 @@
+import { mix } from '../../utils/theme';
+import type { LightTheme } from '../themes.interfaces';
+
+const colors = {
+  primary: '#0054e9',
+  secondary: '#0163aa',
+  tertiary: '#6030ff',
+  success: '#2dd55b',
+  warning: '#ffc409',
+  danger: '#c5000f',
+  light: '#f4f5f8',
+  medium: '#636469',
+  dark: '#222428',
+};
+
+export const lightTheme: LightTheme = {
+  color: {
+    primary: {
+      bold: {
+        base: colors.primary,
+        contrast: '#fff',
+        foreground: mix(colors.primary, '#000', '12%'),
+        shade: mix(colors.primary, '#000', '12%'),
+        tint: mix(colors.primary, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.primary, '8%'),
+        contrast: colors.primary,
+        foreground: mix(colors.primary, '#000', '12%'),
+        shade: mix('#fff', colors.primary, '12%'),
+        tint: mix('#fff', colors.primary, '4%'),
+      },
+    },
+    secondary: {
+      bold: {
+        base: colors.secondary,
+        contrast: '#fff',
+        foreground: mix(colors.secondary, '#000', '12%'),
+        shade: mix(colors.secondary, '#000', '12%'),
+        tint: mix(colors.secondary, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.secondary, '8%'),
+        contrast: colors.secondary,
+        foreground: mix(colors.secondary, '#000', '12%'),
+        shade: mix('#fff', colors.secondary, '12%'),
+        tint: mix('#fff', colors.secondary, '4%'),
+      },
+    },
+    tertiary: {
+      bold: {
+        base: colors.tertiary,
+        contrast: '#fff',
+        foreground: mix(colors.tertiary, '#000', '12%'),
+        shade: mix(colors.tertiary, '#000', '12%'),
+        tint: mix(colors.tertiary, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.tertiary, '8%'),
+        contrast: colors.tertiary,
+        foreground: mix(colors.tertiary, '#000', '12%'),
+        shade: mix('#fff', colors.tertiary, '12%'),
+        tint: mix('#fff', colors.tertiary, '4%'),
+      },
+    },
+    success: {
+      bold: {
+        base: colors.success,
+        contrast: '#fff',
+        foreground: mix(colors.success, '#000', '12%'),
+        shade: mix(colors.success, '#000', '12%'),
+        tint: mix(colors.success, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.success, '8%'),
+        contrast: colors.success,
+        foreground: mix(colors.success, '#000', '12%'),
+        shade: mix('#fff', colors.success, '12%'),
+        tint: mix('#fff', colors.success, '4%'),
+      },
+    },
+    warning: {
+      bold: {
+        base: colors.warning,
+        contrast: '#fff',
+        foreground: mix(colors.warning, '#000', '12%'),
+        shade: mix(colors.warning, '#000', '12%'),
+        tint: mix(colors.warning, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.warning, '8%'),
+        contrast: colors.warning,
+        foreground: mix(colors.warning, '#000', '12%'),
+        shade: mix('#fff', colors.warning, '12%'),
+        tint: mix('#fff', colors.warning, '4%'),
+      },
+    },
+    danger: {
+      bold: {
+        base: colors.danger,
+        contrast: '#fff',
+        foreground: mix(colors.danger, '#000', '12%'),
+        shade: mix(colors.danger, '#000', '12%'),
+        tint: mix(colors.danger, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.danger, '8%'),
+        contrast: colors.danger,
+        foreground: mix(colors.danger, '#000', '12%'),
+        shade: mix('#fff', colors.danger, '12%'),
+        tint: mix('#fff', colors.danger, '4%'),
+      },
+    },
+    light: {
+      bold: {
+        base: colors.light,
+        contrast: '#000',
+        foreground: mix(colors.light, '#000', '12%'),
+        shade: mix(colors.light, '#000', '12%'),
+        tint: mix(colors.light, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.light, '8%'),
+        contrast: colors.light,
+        foreground: mix(colors.light, '#000', '12%'),
+        shade: mix('#fff', colors.light, '12%'),
+        tint: mix('#fff', colors.light, '4%'),
+      },
+    },
+    medium: {
+      bold: {
+        base: colors.medium,
+        contrast: '#fff',
+        foreground: mix(colors.medium, '#000', '12%'),
+        shade: mix(colors.medium, '#000', '12%'),
+        tint: mix(colors.medium, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.medium, '8%'),
+        contrast: colors.medium,
+        foreground: mix(colors.medium, '#000', '12%'),
+        shade: mix('#fff', colors.medium, '12%'),
+        tint: mix('#fff', colors.medium, '4%'),
+      },
+    },
+    dark: {
+      bold: {
+        base: colors.dark,
+        contrast: '#fff',
+        foreground: mix(colors.dark, '#000', '12%'),
+        shade: mix(colors.dark, '#000', '12%'),
+        tint: mix(colors.dark, '#fff', '10%'),
+      },
+      subtle: {
+        base: mix('#fff', colors.dark, '8%'),
+        contrast: colors.dark,
+        foreground: mix(colors.dark, '#000', '12%'),
+        shade: mix('#fff', colors.dark, '12%'),
+        tint: mix('#fff', colors.dark, '4%'),
+      },
+    },
+  },
+};
diff --git a/core/src/themes/themes.interfaces.ts b/core/src/themes/themes.interfaces.ts
new file mode 100644
index 00000000000..3d5ac09a4e0
--- /dev/null
+++ b/core/src/themes/themes.interfaces.ts
@@ -0,0 +1,145 @@
+// Platform-specific theme
+export type PlatformTheme = Omit;
+
+// Base tokens for all palettes
+export type BaseTheme = {
+  // SPACE TOKENS
+  spacing?: {
+    none?: string;
+    xxs?: string;
+    xs?: string;
+    sm?: string;
+    md?: string;
+    lg?: string;
+    xl?: string;
+    xxl?: string;
+  };
+
+  scaling?: {
+    0?: string;
+    100?: string;
+    150?: string;
+    200?: string;
+    250?: string;
+    300?: string;
+    350?: string;
+    400?: string;
+    450?: string;
+    500?: string;
+    550?: string;
+    600?: string;
+    650?: string;
+    700?: string;
+    750?: string;
+    800?: string;
+    850?: string;
+    900?: string;
+  };
+
+  // APPEARANCE TOKENS
+  borderWidth?: {
+    none?: string;
+    xxs?: string;
+    xs?: string;
+    sm?: string;
+    md?: string;
+    lg?: string;
+    xl?: string;
+    xxl?: string;
+  };
+
+  radii?: {
+    none?: string;
+    xxs?: string;
+    xs?: string;
+    sm?: string;
+    md?: string;
+    lg?: string;
+    xl?: string;
+    xxl?: string;
+  };
+
+  // TYPOGRAPHY TOKENS
+  dynamicFont?: string;
+  fontFamily?: string;
+
+  fontWeight?: {
+    thin?: string;
+    extraLight?: string;
+    light?: string;
+    normal?: string;
+    medium?: string;
+    semiBold?: string;
+    bold?: string;
+    extraBold?: string;
+    black?: string;
+  };
+
+  fontSize?: {
+    root?: string;
+    xxs?: string;
+    xs?: string;
+    sm?: string;
+    md?: string;
+    lg?: string;
+    xl?: string;
+    xxl?: string;
+  };
+
+  lineHeight?: {
+    xxs?: string;
+    xs?: string;
+    sm?: string;
+    md?: string;
+    lg?: string;
+    xl?: string;
+    xxl?: string;
+  };
+
+  // COMPONENT OVERRIDES
+  components?: {
+    [key: string]: {
+      [key: string]: string;
+    };
+  };
+
+  // COLOR TOKENS
+  color?: {
+    [key: string]: {
+      bold: {
+        base: string;
+        contrast: string;
+        foreground: string;
+        shade: string;
+        tint: string;
+      };
+      subtle: {
+        base: string;
+        contrast: string;
+        foreground: string;
+        shade: string;
+        tint: string;
+      };
+    };
+  };
+
+  // PLATFORM SPECIFIC OVERRIDES
+  ios?: PlatformTheme;
+  md?: PlatformTheme;
+};
+
+// Dark theme interface
+export type DarkTheme = BaseTheme & {
+  enabled: 'system' | 'always' | 'never' | 'class';
+};
+
+// Light theme interface
+export type LightTheme = BaseTheme;
+
+// Default theme interface
+export type DefaultTheme = BaseTheme & {
+  palette?: {
+    light?: LightTheme;
+    dark?: DarkTheme;
+  };
+};
diff --git a/core/src/utils/theme.ts b/core/src/utils/theme.ts
index d71fe43a385..6b2d6708f9e 100644
--- a/core/src/utils/theme.ts
+++ b/core/src/utils/theme.ts
@@ -89,6 +89,28 @@ export const generateCSSVars = (theme: any, prefix: string = CSS_PROPS_PREFIX):
         return [`${prefix.slice(0, -1)}: ${val};`];
       }
 
+      // Generate rgb variables for base and contrast color variants
+      if (key === 'bold' || key === 'subtle') {
+        if (typeof val === 'object' && val !== null) {
+          return Object.entries(val).flatMap(([property, hexValue]) => {
+            if (typeof hexValue === 'string' && hexValue.startsWith('#')) {
+              // For 'base' property, don't include the property name in the CSS variable
+              const varName = property === 'base' ? `${prefix}${key}` : `${prefix}${key}-${property}`;
+              const cssVars = [`${varName}: ${hexValue};`];
+
+              // Only add RGB values for base and contrast
+              if (property === 'base' || property === 'contrast') {
+                const rgbVarName = property === 'base' ? `${prefix}${key}-rgb` : `${prefix}${key}-${property}-rgb`;
+                cssVars.push(`${rgbVarName}: ${hexToRgb(hexValue)};`);
+              }
+
+              return cssVars;
+            }
+            return [];
+          });
+        }
+      }
+
       // If it's a font-sizes key, create rem version
       // This is necessary to support the dynamic font size feature
       if (key === 'font-sizes' && typeof val === 'object' && val !== null) {
@@ -114,6 +136,96 @@ export const generateCSSVars = (theme: any, prefix: string = CSS_PROPS_PREFIX):
   return cssProps.join('\n');
 };
 
+/**
+ * Generates a CSS class containing the CSS variables for each color
+ * in the theme. Each color has generic bold and subtle variables that are mapped
+ * to the specific color's bold and subtle variables. The bold colors will temporarily
+ * include a fallback to remove the bold prefix. For example, the primary
+ * color will return the following CSS class:
+ *
+ * ```css
+ * :root .ion-color-primary {
+ *   --ion-color-base: var(--ion-color-primary, var(--ion-color-primary-bold));
+ *   --ion-color-base-rgb: var(--ion-color-primary-rgb, var(--ion-color-primary-bold-rgb));
+ *   --ion-color-contrast: var(--ion-color-primary-contrast, var(--ion-color-primary-bold-contrast));
+ *   --ion-color-contrast-rgb: var(--ion-color-primary-contrast-rgb, var(--ion-color-primary-bold-contrast-rgb));
+ *   --ion-color-shade: var(--ion-color-primary-shade, var(--ion-color-primary-bold-shade));
+ *   --ion-color-tint: var(--ion-color-primary-tint, var(--ion-color-primary-bold-tint));
+ *   --ion-color-foreground: var(--ion-color-primary, var(--ion-color-primary-bold-foreground));
+ *
+ *   --ion-color-subtle-base: var(--ion-color-primary-subtle);
+ *   --ion-color-subtle-base-rgb: var(--ion-color-primary-subtle-rgb);
+ *   --ion-color-subtle-contrast: var(--ion-color-primary-subtle-contrast);
+ *   --ion-color-subtle-contrast-rgb: var(--ion-color-primary-subtle-contrast-rgb);
+ *   --ion-color-subtle-shade: var(--ion-color-primary-subtle-shade);
+ *   --ion-color-subtle-tint: var(--ion-color-primary-subtle-tint);
+ *   --ion-color-subtle-foreground: var(--ion-color-primary-subtle-foreground);
+ * }
+ * ```
+ *
+ * @param theme The theme object containing color definitions
+ * @returns CSS string with .ion-color-{colorName} utility classes
+ */
+export const generateColorClasses = (theme: any): string => {
+  // Look for colors in the light palette first, then fallback to the
+  // direct color property if there is no light palette
+  const colors = theme?.palette?.light?.color || theme?.color;
+
+  if (!colors || typeof colors !== 'object') {
+    return '';
+  }
+
+  const generatedColorClasses: string[] = [];
+
+  Object.keys(colors).forEach((colorName) => {
+    const colorVariants = colors[colorName];
+    if (!colorVariants || typeof colorVariants !== 'object') return;
+
+    const cssVariableRules: string[] = [];
+
+    // Generate CSS variables for bold variant
+    // Includes base color variables without the bold modifier for
+    // backwards compatibility. The foreground variables falls back to the
+    // base color because it is new.
+    if (colorVariants.bold) {
+      cssVariableRules.push(
+        `--ion-color-base: var(--ion-color-${colorName}, var(--ion-color-${colorName}-bold)) !important;`,
+        `--ion-color-base-rgb: var(--ion-color-${colorName}-rgb, var(--ion-color-${colorName}-bold-rgb)) !important;`,
+        `--ion-color-contrast: var(--ion-color-${colorName}-contrast, var(--ion-color-${colorName}-bold-contrast)) !important;`,
+        `--ion-color-contrast-rgb: var(--ion-color-${colorName}-contrast-rgb, var(--ion-color-${colorName}-bold-contrast-rgb)) !important;`,
+        `--ion-color-shade: var(--ion-color-${colorName}-shade, var(--ion-color-${colorName}-bold-shade)) !important;`,
+        `--ion-color-tint: var(--ion-color-${colorName}-tint, var(--ion-color-${colorName}-bold-tint)) !important;`,
+        `--ion-color-foreground: var(--ion-color-${colorName}, var(--ion-color-${colorName}-bold-foreground)) !important;`
+      );
+    }
+
+    // Generate CSS variables for subtle variant
+    if (colorVariants.subtle) {
+      cssVariableRules.push(
+        `--ion-color-subtle-base: var(--ion-color-${colorName}-subtle) !important;`,
+        `--ion-color-subtle-base-rgb: var(--ion-color-${colorName}-subtle-rgb) !important;`,
+        `--ion-color-subtle-contrast: var(--ion-color-${colorName}-subtle-contrast) !important;`,
+        `--ion-color-subtle-contrast-rgb: var(--ion-color-${colorName}-subtle-contrast-rgb) !important;`,
+        `--ion-color-subtle-shade: var(--ion-color-${colorName}-subtle-shade) !important;`,
+        `--ion-color-subtle-tint: var(--ion-color-${colorName}-subtle-tint) !important;`,
+        `--ion-color-subtle-foreground: var(--ion-color-${colorName}-subtle-foreground) !important;`
+      );
+    }
+
+    if (cssVariableRules.length > 0) {
+      const colorUtilityClass = `
+        :root .ion-color-${colorName} {
+          ${cssVariableRules.join('\n  ')}
+        }
+      `;
+
+      generatedColorClasses.push(colorUtilityClass);
+    }
+  });
+
+  return generatedColorClasses.join('\n');
+};
+
 /**
  * Creates a style element and injects its CSS into a target element
  * @param css The CSS string to inject
@@ -172,7 +284,10 @@ export const generateGlobalThemeCSS = (theme: any): string => {
     }
   }
 
-  return css;
+  // Add color classes
+  const colorClasses = generateColorClasses(theme);
+
+  return css + '\n' + colorClasses;
 };
 
 /**
@@ -254,3 +369,73 @@ export const applyComponentTheme = (element: HTMLElement): void => {
     injectCSS(css, root);
   }
 };
+
+/**
+ * Converts a hex color to RGB comma-separated values
+ * @param hex Hex color (e.g., '#ffffff' or '#fff')
+ * @returns RGB string (e.g., '255, 255, 255')
+ */
+export const hexToRgb = (hex: string): string => {
+  const cleanHex = hex.replace('#', '');
+
+  let r: number, g: number, b: number;
+
+  if (cleanHex.length === 3) {
+    // Short hex format like 'fff' → expand to 'ffffff'
+    r = parseInt(cleanHex[0] + cleanHex[0], 16);
+    g = parseInt(cleanHex[1] + cleanHex[1], 16);
+    b = parseInt(cleanHex[2] + cleanHex[2], 16);
+  } else {
+    // Full hex format like 'ffffff'
+    r = parseInt(cleanHex.substr(0, 2), 16);
+    g = parseInt(cleanHex.substr(2, 2), 16);
+    b = parseInt(cleanHex.substr(4, 2), 16);
+  }
+
+  return `${r}, ${g}, ${b}`;
+};
+
+/**
+ * Mixes two hex colors by a given weight percentage
+ * @param baseColor Base color (e.g., '#0054e9')
+ * @param mixColor Color to mix in (e.g., '#000000' or '#fff')
+ * @param weight Weight percentage as string - how much of mixColor to mix into baseColor (e.g., '12%')
+ * @returns Mixed hex color
+ */
+export const mix = (baseColor: string, mixColor: string, weight: string): string => {
+  // Parse weight percentage
+  const w = parseFloat(weight.replace('%', '')) / 100;
+
+  // Parse hex colors
+  const parseHex = (hex: string): [number, number, number] => {
+    const cleanHex = hex.replace('#', '');
+
+    // Short hex format like 'fff' → expand to 'ffffff'
+    if (cleanHex.length === 3) {
+      return [
+        parseInt(cleanHex[0] + cleanHex[0], 16),
+        parseInt(cleanHex[1] + cleanHex[1], 16),
+        parseInt(cleanHex[2] + cleanHex[2], 16),
+      ];
+      // Full hex format like 'ffffff'
+    } else {
+      return [
+        parseInt(cleanHex.substr(0, 2), 16),
+        parseInt(cleanHex.substr(2, 2), 16),
+        parseInt(cleanHex.substr(4, 2), 16),
+      ];
+    }
+  };
+
+  // Parse both colors
+  const [baseR, baseG, baseB] = parseHex(baseColor);
+  const [mixR, mixG, mixB] = parseHex(mixColor);
+
+  // Mix mixColor into baseColor by weight
+  const r = Math.round(baseR * (1 - w) + mixR * w);
+  const g = Math.round(baseG * (1 - w) + mixG * w);
+  const b = Math.round(baseB * (1 - w) + mixB * w);
+
+  const toHex = (n: number) => n.toString(16).padStart(2, '0');
+  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
+};
From 5435bb72473d3b75846cc9023c39462d1e21099a Mon Sep 17 00:00:00 2001
From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Date: Tue, 23 Sep 2025 16:13:50 -0400
Subject: [PATCH 04/22] feat(themes): remove old $colors map and functions and
 use new tokens
---
 core/src/css/core.scss                        |  18 --
 core/src/css/ionic/core.ionic.scss            |  13 -
 core/src/themes/functions.color.scss          | 223 +++---------------
 core/src/themes/ionic/ionic.globals.scss      |   1 -
 .../src/themes/ionic/ionic.theme.default.scss | 200 ----------------
 core/src/themes/native/native.globals.scss    |   1 -
 .../themes/native/native.theme.default.scss   | 113 ---------
 7 files changed, 30 insertions(+), 539 deletions(-)
diff --git a/core/src/css/core.scss b/core/src/css/core.scss
index 3d7a727592e..5d1aa62b4b9 100644
--- a/core/src/css/core.scss
+++ b/core/src/css/core.scss
@@ -4,24 +4,6 @@
 @import "../components/menu/menu.md.vars";
 @import "../components/modal/modal.native.vars";
 
-// Ionic Colors
-// --------------------------------------------------
-
-:root {
-  /**
-   * Set the theme colors from the
-   * `native.theme.default.scss` file.
-   */
-  @include set-theme-colors($colors);
-  @include generate-color-variables();
-
-  @each $color-name, $value in $colors {
-    .ion-color-#{$color-name} {
-      @include generate-color($color-name);
-    }
-  }
-}
-
 // Ionic Font Family
 // --------------------------------------------------
 
diff --git a/core/src/css/ionic/core.ionic.scss b/core/src/css/ionic/core.ionic.scss
index a81974eab6d..5ceea8d614a 100644
--- a/core/src/css/ionic/core.ionic.scss
+++ b/core/src/css/ionic/core.ionic.scss
@@ -4,19 +4,6 @@
 // --------------------------------------------------
 
 :root {
-  /**
-   * Set the theme colors from the
-   * `ionic.theme.default.scss` file.
-   */
-  @include globals.set-theme-colors(globals.$ionic-colors);
-  @include globals.generate-color-variables();
-
-  @each $color-name, $value in globals.$ionic-colors {
-    .ion-color-#{$color-name} {
-      @include globals.generate-color($color-name);
-    }
-  }
-
   /* Default background color of all components to default background surface token */
   --background: #{globals.$ion-bg-surface-default};
 }
diff --git a/core/src/themes/functions.color.scss b/core/src/themes/functions.color.scss
index a48b8e18c09..5e702be1c65 100644
--- a/core/src/themes/functions.color.scss
+++ b/core/src/themes/functions.color.scss
@@ -1,11 +1,5 @@
 @use "sass:map";
 
-// Set the theme colors map to be used by the color functions
-// --------------------------------------------------------------------------------------------
-@mixin set-theme-colors($colorsMap) {
-  $theme-colors: $colorsMap !global;
-}
-
 // Gets the active color's css variable from a variation. Alpha is optional.
 // --------------------------------------------------------------------------------------------
 // Example usage:
@@ -25,50 +19,48 @@
 // Gets the specific color's css variable from the name and variation. Alpha/rgb are optional.
 // --------------------------------------------------------------------------------------------
 // Example usage:
-// ion-color(primary, base) => var(--ion-color-primary, #3880ff)
+// ion-color(primary, base) => var(--ion-color-primary, var(--ion-color-primary-bold))
 // ion-color(secondary, contrast) => var(--ion-color-secondary-contrast)
-// ion-color(primary, base, 0.5) => rgba(var(--ion-color-primary-rgb, 56, 128, 255), 0.5)
+// ion-color(primary, base, 0.5) => rgba(var(--ion-color-primary-rgb), 0.5)
+// ion-color(primary, base, null, true) => var(--ion-color-primary-rgb)
+// ion-color(primary, base, null, null, true) => var(--ion-color-primary-subtle)
 // --------------------------------------------------------------------------------------------
 @function ion-color($name, $variation, $alpha: null, $rgb: null, $subtle: false) {
-  @if not($theme-colors) {
-    @error 'No theme colors set. Please make sure to call set-theme-colors($colorsMap) before using ion-color()';
-  }
-
-  $values: map.get($theme-colors, $name);
-  $values: map.get($values, if($subtle, subtle, bold));
-
-  $value: map.get($values, $variation);
-
-  // TODO(FW-6417): this can be removed when foreground is required
-  // Fallback to "base" variant when "foreground" variant is undefined
-  @if ($variation == foreground and $value == null) {
-    $variation: base;
-    $value: map.get($values, $variation);
-  }
-
-  // If the color requested is subtle we return `--ion-color-{color}-subtle-contrast`,
-  // otherwise we return `--ion-color-{color}-contrast`.
-  $variable: if($subtle, "--ion-color-#{$name}-subtle-#{$variation}", "--ion-color-#{$name}-#{$variation}");
-
-  // If the variation being used is "base", we do not include the variant.
-  // If the color requested is subtle we return `--ion-color-{color}-subtle`,
-  // otherwise we return `--ion-color-{color}`.
-  @if ($variation == base) {
-    $variable: if($subtle, "--ion-color-#{$name}-subtle", "--ion-color-#{$name}");
+  // Build base variable name
+  $base-variable: if($subtle, "--ion-color-#{$name}-subtle", "--ion-color-#{$name}");
+  $variation-suffix: if($variation == base, "", "-#{$variation}");
+  $variable: "#{$base-variable}#{$variation-suffix}";
+
+  // Build fallback variable name (only for bold colors)
+  $fallback-variable: null;
+  @if (not $subtle) {
+    $fallback-base: "--ion-color-#{$name}-bold";
+    $fallback-variable: "#{$fallback-base}#{$variation-suffix}";
   }
 
+  // Handle alpha transparency
   @if ($alpha) {
-    $value: color-to-rgb-list($value);
+    $rgb-var: "#{$variable}-rgb";
+    $fallback-rgb: if($fallback-variable, "#{$fallback-variable}-rgb", null);
 
-    @return rgba(var(#{$variable}-rgb, $value), $alpha);
+    @if ($fallback-rgb) {
+      @return rgba(var(#{$rgb-var}, var(#{$fallback-rgb})), $alpha);
+    } @else {
+      @return rgba(var(#{$rgb-var}), $alpha);
+    }
   }
 
+  // Handle RGB variables
   @if ($rgb) {
-    $value: color-to-rgb-list($value);
-    $variable: #{$variable}-rgb;
+    $variable: "#{$variable}-rgb";
+    $fallback-variable: if($fallback-variable, "#{$fallback-variable}-rgb", null);
   }
 
-  @return var(#{$variable}, $value);
+  @if ($fallback-variable) {
+    @return var(#{$variable}, var(#{$fallback-variable}));
+  } @else {
+    @return var(#{$variable});
+  }
 }
 
 // Mixes a color with black to create its shade.
@@ -97,158 +89,3 @@
   }
   @return #{red($color)}, #{green($color)}, #{blue($color)};
 }
-
-// Generates color variants for the specified color based on the
-// colors map for whichever hue is passed (bold, subtle).
-// --------------------------------------------------------------------------------------------
-// Example usage (bold):
-// .ion-color-primary {
-//   @include generate-color-variants("primary");
-// }
-//
-// Example output (bold):
-// .ion-color-primary {
-//   --ion-color-base: var(--ion-color-primary-base, #105cef) !important;
-//   --ion-color-base-rgb: var(--ion-color-primary-base-rgb, 16, 92, 239) !important;
-//   --ion-color-contrast: var(--ion-color-primary-contrast, #fff) !important;
-//   --ion-color-contrast-rgb: var(--ion-color-primary-contrast-rgb, 255, 255, 255) !important;
-//   --ion-color-shade: var(--ion-color-primary-shade, #0f54da) !important;
-//   --ion-color-tint: var(--ion-color-primary-tint, #94a5f4) !important;
-// }
-// --------------------------------------------------------------------------------------------
-// Example usage (subtle):
-// .ion-color-primary {
-//   @include generate-color-variants("primary", "subtle")
-// }
-//
-// Example output (subtle):
-// .ion-color-primary {
-//   --ion-color-subtle-base: var(--ion-color-primary-subtle-base, #f2f4fd) !important;
-//   --ion-color-subtle-base-rgb: var(--ion-color-primary-subtle-base-rgb, 242, 244, 253) !important;
-//   --ion-color-subtle-contrast: var(--ion-color-primary-subtle-contrast, #105cef) !important;
-//   --ion-color-subtle-contrast-rgb: var(--ion-color-primary-subtle-contrast-rgb, 16, 92, 239) !important;
-//   --ion-color-subtle-shade: var(--ion-color-primary-subtle-shade, #d0d7fa) !important;
-//   --ion-color-subtle-tint: var(--ion-color-primary-subtle-tint, #e9ecfc) !important;
-// }
-// --------------------------------------------------------------------------------------------
-@mixin generate-color-variants($color-name, $hue: "bold") {
-  @if not($theme-colors) {
-    @error 'No theme colors set. Please make sure to call set-theme-colors($colorsMap) before using ion-color()';
-  }
-
-  // Grab the different hue color maps for the
-  // specified color and then grab the map of color variants
-  $hue-colors: map.get($theme-colors, $color-name);
-  $color-variants: map.get($hue-colors, $hue);
-
-  $prefix: if($hue == "subtle", "-subtle", "");
-
-  // TODO(FW-6417) this @if can be removed if we add subtle colors for ios and md
-  // Only proceed if the color variants exist
-  @if $color-variants {
-    // Grab the individual color variants
-    $base: map.get($color-variants, base);
-    $base-rgb: map.get($color-variants, base-rgb);
-    $contrast: map.get($color-variants, contrast);
-    $contrast-rgb: map.get($color-variants, contrast-rgb);
-    $shade: map.get($color-variants, shade);
-    $tint: map.get($color-variants, tint);
-    $foreground: map.get($color-variants, foreground);
-
-    // Generate CSS variables dynamically
-    --ion-color#{$prefix}-base: var(--ion-color-#{$color-name}#{$prefix}, #{$base}) !important;
-    --ion-color#{$prefix}-base-rgb: var(--ion-color-#{$color-name}#{$prefix}-rgb, #{$base-rgb}) !important;
-    --ion-color#{$prefix}-contrast: var(--ion-color-#{$color-name}#{$prefix}-contrast, #{$contrast}) !important;
-    --ion-color#{$prefix}-contrast-rgb: var(
-      --ion-color-#{$color-name}#{$prefix}-contrast-rgb,
-      #{$contrast-rgb}
-    ) !important;
-    --ion-color#{$prefix}-shade: var(--ion-color-#{$color-name}#{$prefix}-shade, #{$shade}) !important;
-    --ion-color#{$prefix}-tint: var(--ion-color-#{$color-name}#{$prefix}-tint, #{$tint}) !important;
-    // TODO(FW-6417): remove the fallback variable when the foreground variable is
-    // required by all palettes for all themes:
-    // --ion-color#{$prefix}-foreground: var(--ion-color-#{$color-name}#{$prefix}-foreground, #{$foreground}) !important;
-    --ion-color#{$prefix}-foreground: var(
-      --ion-color-#{$color-name}#{$prefix}-foreground,
-      var(--ion-color-#{$color-name}#{$prefix}, #{$foreground})
-    ) !important;
-  }
-}
-
-// Generates both bold and subtle color variables
-// for the specified color in the colors map.
-// --------------------------------------------------------------------------------------------
-@mixin generate-color($color-name) {
-  @include generate-color-variants($color-name);
-  @include generate-color-variants($color-name, "subtle");
-}
-
-// Generates color variables for all colors in the colors map for both hues (bold, subtle).
-// --------------------------------------------------------------------------------------------
-// Example usage:
-// :root {
-//   generate-color-variables()
-// }
-//
-// Example output:
-// :root {
-//   --ion-color-primary: #105cef;
-//   --ion-color-primary-rgb: 16, 92, 239;
-//   --ion-color-primary-contrast: #ffffff;
-//   --ion-color-primary-contrast-rgb: 255, 255, 255;
-//   --ion-color-primary-shade: #0f54da;
-//   --ion-color-primary-tint: #94a5f4;
-//   --ion-color-primary-foreground: #105cef;
-//   --ion-color-primary-subtle: #f2f4fd;
-//   --ion-color-primary-subtle-rgb: 242, 244, 253;
-//   --ion-color-primary-subtle-contrast: #105cef;
-//   --ion-color-primary-subtle-contrast-rgb: 16, 92, 239;
-//   --ion-color-primary-subtle-shade: #d0d7fa;
-//   --ion-color-primary-subtle-tint: #e9ecfc;
-//   --ion-color-primary-foreground: #105cef;
-//   ...
-//   --ion-color-dark: #292929;
-//   --ion-color-dark-rgb: 41, 41, 41;
-//   --ion-color-dark-contrast: #ffffff;
-//   --ion-color-dark-contrast-rgb: 255, 255, 255;
-//   --ion-color-dark-shade: #242424;
-//   --ion-color-dark-tint: #4e4e4e;
-//   --ion-color-dark-foreground: #242424;
-//   --ion-color-dark-subtle: #f5f5f5;
-//   --ion-color-dark-subtle-rgb: 245, 245, 245;
-//   --ion-color-dark-subtle-contrast: #292929;
-//   --ion-color-dark-subtle-contrast-rgb: 41, 41, 41;
-//   --ion-color-dark-subtle-shade: #e0e0e0;
-//   --ion-color-dark-subtle-tint: #efefef;
-//   --ion-color-dark-subtle-foreground: #242424;
-// }
-// --------------------------------------------------------------------------------------------
-@mixin generate-color-variables() {
-  @if not($theme-colors) {
-    @error 'No theme colors set. Please make sure to call set-theme-colors($colorsMap) before using ion-color().';
-  }
-
-  @each $color-name, $value in $theme-colors {
-    @each $hue in (bold, subtle) {
-      $colors: map.get($value, $hue);
-
-      @if $colors != null {
-        $prefix: if($hue == subtle, "-subtle", "");
-
-        --ion-color-#{$color-name}#{$prefix}: #{map.get($colors, base)};
-        --ion-color-#{$color-name}#{$prefix}-rgb: #{map.get($colors, base-rgb)};
-        --ion-color-#{$color-name}#{$prefix}-contrast: #{map.get($colors, contrast)};
-        --ion-color-#{$color-name}#{$prefix}-contrast-rgb: #{map.get($colors, contrast-rgb)};
-        --ion-color-#{$color-name}#{$prefix}-shade: #{map.get($colors, shade)};
-        --ion-color-#{$color-name}#{$prefix}-tint: #{map.get($colors, tint)};
-        // TODO(FW-6417): this "if" can be removed when foreground is defined for ios/md
-        // themes. It should not be added until we want foreground to be required for
-        // ios and md because this will be a breaking change, requiring users to add
-        // `--ion-color-{color}-foreground` in order to override the default colors
-        @if (map.get($colors, foreground)) {
-          --ion-color-#{$color-name}#{$prefix}-foreground: #{map.get($colors, foreground)};
-        }
-      }
-    }
-  }
-}
diff --git a/core/src/themes/ionic/ionic.globals.scss b/core/src/themes/ionic/ionic.globals.scss
index 17d0169994b..4b93206b688 100644
--- a/core/src/themes/ionic/ionic.globals.scss
+++ b/core/src/themes/ionic/ionic.globals.scss
@@ -16,4 +16,3 @@
 // Default Theme
 @use "./ionic.theme.default" as ionicTheme;
 @forward "./ionic.theme.default";
-@include color.set-theme-colors(ionicTheme.$ionic-colors);
diff --git a/core/src/themes/ionic/ionic.theme.default.scss b/core/src/themes/ionic/ionic.theme.default.scss
index 267a128206f..30e6ca36c19 100644
--- a/core/src/themes/ionic/ionic.theme.default.scss
+++ b/core/src/themes/ionic/ionic.theme.default.scss
@@ -7,206 +7,6 @@
 // between modes. This should only include variables
 // used to theme the application colors.
 
-// Default Ionic Colors
-// -------------------------------------------------------------------------------------------
-// Color map should provide
-// - bold: a map of the bold color variations
-// - subtle: a map of the subtle color variations
-//
-// Each hue color map should provide
-// - base: The main color used for backgrounds
-// - base-rgb: The base color in RGB format
-// - contrast: A color that ensures readable text on the base color
-// - contrast-rgb: The contrast color in RGB format
-// - shade: A darker variant of the base color, used for pressed/active states
-// - tint: A lighter variant of the base color, used for ?
-// - foreground: The main color used for text and foreground elements
-
-// TODO(ROU-10778, ROU-10875): Sync the color names to the design system of
-// ios and md. This will allow us to have a single color map.
-$ionic-colors: (
-  primary: (
-    bold: (
-      base: globals.$ion-bg-primary-base-default,
-      base-rgb: globals.$ion-bg-primary-base-default-rgb,
-      contrast: globals.$ion-text-inverse,
-      contrast-rgb: globals.$ion-text-inverse-rgb,
-      shade: globals.$ion-bg-primary-base-press,
-      tint: globals.$ion-semantics-primary-600,
-      foreground: globals.$ion-text-primary,
-    ),
-    subtle: (
-      base: globals.$ion-bg-primary-subtle-default,
-      base-rgb: globals.$ion-bg-primary-subtle-default-rgb,
-      contrast: globals.$ion-text-primary,
-      contrast-rgb: globals.$ion-text-primary-rgb,
-      shade: globals.$ion-bg-primary-subtle-press,
-      tint: globals.$ion-semantics-primary-200,
-      foreground: globals.$ion-text-primary,
-    ),
-  ),
-  secondary: (
-    bold: (
-      base: globals.$ion-bg-info-base-default,
-      base-rgb: globals.$ion-bg-info-base-default-rgb,
-      contrast: globals.$ion-text-inverse,
-      contrast-rgb: globals.$ion-text-inverse-rgb,
-      shade: globals.$ion-bg-info-base-press,
-      tint: globals.$ion-semantics-info-700,
-      foreground: globals.$ion-text-info,
-    ),
-    subtle: (
-      base: globals.$ion-bg-info-subtle-default,
-      base-rgb: globals.$ion-bg-info-subtle-default-rgb,
-      contrast: globals.$ion-text-info,
-      contrast-rgb: globals.$ion-text-info-rgb,
-      shade: globals.$ion-bg-info-subtle-press,
-      tint: globals.$ion-semantics-info-200,
-      foreground: globals.$ion-text-info,
-    ),
-  ),
-  tertiary: (
-    bold: (
-      base: globals.$ion-primitives-violet-700,
-      base-rgb: globals.$ion-primitives-violet-700-rgb,
-      contrast: globals.$ion-text-inverse,
-      contrast-rgb: globals.$ion-text-inverse-rgb,
-      shade: globals.$ion-primitives-violet-800,
-      tint: globals.$ion-primitives-violet-600,
-      foreground: globals.$ion-primitives-violet-700,
-    ),
-    subtle: (
-      base: globals.$ion-primitives-violet-100,
-      base-rgb: globals.$ion-primitives-violet-100-rgb,
-      contrast: globals.$ion-primitives-violet-700,
-      contrast-rgb: globals.$ion-primitives-violet-700-rgb,
-      shade: globals.$ion-primitives-violet-300,
-      tint: globals.$ion-primitives-violet-200,
-      foreground: globals.$ion-primitives-violet-700,
-    ),
-  ),
-  success: (
-    bold: (
-      base: globals.$ion-bg-success-base-default,
-      base-rgb: globals.$ion-bg-success-base-default-rgb,
-      contrast: globals.$ion-text-inverse,
-      contrast-rgb: globals.$ion-text-inverse-rgb,
-      shade: globals.$ion-bg-success-base-press,
-      tint: globals.$ion-semantics-success-800,
-      foreground: globals.$ion-text-success,
-    ),
-    subtle: (
-      base: globals.$ion-bg-success-subtle-default,
-      base-rgb: globals.$ion-bg-success-subtle-default-rgb,
-      contrast: globals.$ion-text-success,
-      contrast-rgb: globals.$ion-text-success-rgb,
-      shade: globals.$ion-bg-success-subtle-press,
-      tint: globals.$ion-semantics-success-200,
-      foreground: globals.$ion-text-success,
-    ),
-  ),
-  warning: (
-    bold: (
-      base: globals.$ion-bg-warning-base-default,
-      base-rgb: globals.$ion-bg-warning-base-default-rgb,
-      contrast: globals.$ion-text-default,
-      contrast-rgb: globals.$ion-text-default-rgb,
-      shade: globals.$ion-bg-warning-base-press,
-      tint: globals.$ion-primitives-yellow-300,
-      foreground: globals.$ion-text-warning,
-    ),
-    subtle: (
-      base: globals.$ion-bg-warning-subtle-default,
-      base-rgb: globals.$ion-bg-warning-subtle-default-rgb,
-      contrast: globals.$ion-text-warning,
-      contrast-rgb: globals.$ion-text-warning-rgb,
-      shade: globals.$ion-bg-warning-subtle-press,
-      tint: globals.$ion-primitives-yellow-100,
-      foreground: globals.$ion-text-warning,
-    ),
-  ),
-  danger: (
-    bold: (
-      base: globals.$ion-bg-danger-base-default,
-      base-rgb: globals.$ion-bg-danger-base-default-rgb,
-      contrast: globals.$ion-text-inverse,
-      contrast-rgb: globals.$ion-text-inverse-rgb,
-      shade: globals.$ion-bg-danger-base-press,
-      tint: globals.$ion-semantics-danger-700,
-      foreground: globals.$ion-text-danger,
-    ),
-    subtle: (
-      base: globals.$ion-bg-danger-subtle-default,
-      base-rgb: globals.$ion-bg-danger-subtle-default-rgb,
-      contrast: globals.$ion-text-danger,
-      contrast-rgb: globals.$ion-text-danger-rgb,
-      shade: globals.$ion-bg-danger-subtle-press,
-      tint: globals.$ion-semantics-danger-200,
-      foreground: globals.$ion-text-danger,
-    ),
-  ),
-  light: (
-    bold: (
-      base: globals.$ion-bg-neutral-base-default,
-      base-rgb: globals.$ion-bg-neutral-base-default-rgb,
-      contrast: globals.$ion-text-default,
-      contrast-rgb: globals.$ion-text-default-rgb,
-      shade: globals.$ion-primitives-neutral-600,
-      tint: globals.$ion-primitives-neutral-400,
-      foreground: globals.$ion-text-default,
-    ),
-    subtle: (
-      base: globals.$ion-bg-neutral-subtlest-default,
-      base-rgb: globals.$ion-bg-neutral-subtlest-default-rgb,
-      contrast: globals.$ion-text-default,
-      contrast-rgb: globals.$ion-text-default-rgb,
-      shade: globals.$ion-bg-neutral-subtlest-press,
-      tint: globals.$ion-primitives-neutral-100,
-      foreground: globals.$ion-text-default,
-    ),
-  ),
-  medium: (
-    bold: (
-      base: globals.$ion-bg-neutral-bold-default,
-      base-rgb: globals.$ion-bg-neutral-bold-default-rgb,
-      contrast: globals.$ion-text-inverse,
-      contrast-rgb: globals.$ion-text-inverse-rgb,
-      shade: globals.$ion-bg-neutral-bold-press,
-      tint: globals.$ion-primitives-neutral-900,
-      foreground: globals.$ion-text-default,
-    ),
-    subtle: (
-      base: globals.$ion-bg-neutral-subtle-default,
-      base-rgb: globals.$ion-bg-neutral-subtle-default-rgb,
-      contrast: globals.$ion-text-subtlest,
-      contrast-rgb: globals.$ion-text-subtlest-rgb,
-      shade: globals.$ion-bg-neutral-subtle-press,
-      tint: globals.$ion-primitives-neutral-100,
-      foreground: globals.$ion-text-default,
-    ),
-  ),
-  dark: (
-    bold: (
-      base: globals.$ion-bg-neutral-boldest-default,
-      base-rgb: globals.$ion-bg-neutral-boldest-default-rgb,
-      contrast: globals.$ion-text-inverse,
-      contrast-rgb: globals.$ion-text-inverse-rgb,
-      shade: globals.$ion-bg-neutral-boldest-press,
-      tint: globals.$ion-primitives-neutral-1100,
-      foreground: globals.$ion-text-default,
-    ),
-    subtle: (
-      base: globals.$ion-bg-neutral-subtle-default,
-      base-rgb: globals.$ion-bg-neutral-subtle-default-rgb,
-      contrast: globals.$ion-text-subtle,
-      contrast-rgb: globals.$ion-text-subtle-rgb,
-      shade: globals.$ion-bg-neutral-subtle-press,
-      tint: globals.$ion-primitives-neutral-100,
-      foreground: globals.$ion-text-default,
-    ),
-  ),
-);
-
 // Ionic Tabs & Tab Bar
 // --------------------------------------------------
 
diff --git a/core/src/themes/native/native.globals.scss b/core/src/themes/native/native.globals.scss
index 264098e9759..b87ed699bdf 100644
--- a/core/src/themes/native/native.globals.scss
+++ b/core/src/themes/native/native.globals.scss
@@ -13,7 +13,6 @@
 
 // Default Theme
 @import "./native.theme.default";
-@include set-theme-colors($colors);
 
 // Default General
 // --------------------------------------------------
diff --git a/core/src/themes/native/native.theme.default.scss b/core/src/themes/native/native.theme.default.scss
index 22edeb872f6..db4819c8a94 100644
--- a/core/src/themes/native/native.theme.default.scss
+++ b/core/src/themes/native/native.theme.default.scss
@@ -4,119 +4,6 @@
 // between modes. This should only include variables
 // used to theme the application colors.
 
-// Default Ionic Colors
-// -------------------------------------------------------------------------------------------
-// Color map should provide
-// - base: The main color used for backgrounds
-// - base-rgb: The base color in RGB format
-// - contrast: A color that ensures readable text on the base color
-// - contrast-rgb: The contrast color in RGB format
-// - shade: 12% darker version of the base color (mix with black), used for pressed/active states
-// - tint: 10% lighter version of the base color (mix with white), used for focused/hover states
-
-$primary: #0054e9;
-$secondary: #0163aa;
-$tertiary: #6030ff;
-$success: #2dd55b;
-$warning: #ffc409;
-$danger: #c5000f;
-$light: #f4f5f8;
-$medium: #636469;
-$dark: #222428;
-
-$colors: (
-  primary: (
-    bold: (
-      base: $primary,
-      base-rgb: color-to-rgb-list($primary),
-      contrast: #fff,
-      contrast-rgb: color-to-rgb-list(#fff),
-      shade: get-color-shade($primary),
-      tint: get-color-tint($primary),
-    ),
-  ),
-  secondary: (
-    bold: (
-      base: $secondary,
-      base-rgb: color-to-rgb-list($secondary),
-      contrast: #fff,
-      contrast-rgb: color-to-rgb-list(#fff),
-      shade: get-color-shade($secondary),
-      tint: get-color-tint($secondary),
-    ),
-  ),
-  tertiary: (
-    bold: (
-      base: $tertiary,
-      base-rgb: color-to-rgb-list($tertiary),
-      contrast: #fff,
-      contrast-rgb: color-to-rgb-list(#fff),
-      shade: get-color-shade($tertiary),
-      tint: get-color-tint($tertiary),
-    ),
-  ),
-  success: (
-    bold: (
-      base: $success,
-      base-rgb: color-to-rgb-list($success),
-      contrast: #000,
-      contrast-rgb: color-to-rgb-list(#000),
-      shade: get-color-shade($success),
-      tint: get-color-tint($success),
-    ),
-  ),
-  warning: (
-    bold: (
-      base: $warning,
-      base-rgb: color-to-rgb-list($warning),
-      contrast: #000,
-      contrast-rgb: color-to-rgb-list(#000),
-      shade: get-color-shade($warning),
-      tint: get-color-tint($warning),
-    ),
-  ),
-  danger: (
-    bold: (
-      base: $danger,
-      base-rgb: color-to-rgb-list($danger),
-      contrast: #fff,
-      contrast-rgb: color-to-rgb-list(#fff),
-      shade: get-color-shade($danger),
-      tint: get-color-tint($danger),
-    ),
-  ),
-  light: (
-    bold: (
-      base: $light,
-      base-rgb: color-to-rgb-list($light),
-      contrast: #000,
-      contrast-rgb: color-to-rgb-list(#000),
-      shade: get-color-shade($light),
-      tint: get-color-tint($light),
-    ),
-  ),
-  medium: (
-    bold: (
-      base: $medium,
-      base-rgb: color-to-rgb-list($medium),
-      contrast: #fff,
-      contrast-rgb: color-to-rgb-list(#fff),
-      shade: get-color-shade($medium),
-      tint: get-color-tint($medium),
-    ),
-  ),
-  dark: (
-    bold: (
-      base: $dark,
-      base-rgb: color-to-rgb-list($dark),
-      contrast: #fff,
-      contrast-rgb: color-to-rgb-list(#fff),
-      shade: get-color-shade($dark),
-      tint: get-color-tint($dark),
-    ),
-  ),
-);
-
 // Default Foreground and Background Colors
 // -------------------------------------------------------------------------------------------
 // Used internally to calculate the default steps
From 2183a24b3c2db3c077d3ba926da05c057763b562 Mon Sep 17 00:00:00 2001
From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Date: Tue, 23 Sep 2025 16:40:36 -0400
Subject: [PATCH 05/22] style: lint
---
 core/src/themes/test/basic/index.html | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/core/src/themes/test/basic/index.html b/core/src/themes/test/basic/index.html
index ec328d34dd7..81bfba8139f 100644
--- a/core/src/themes/test/basic/index.html
+++ b/core/src/themes/test/basic/index.html
@@ -31,18 +31,18 @@
         margin: 0;
       }
 
-      [class^="ion-scaling-"],
-      [class^="ion-spacing-"],
-      [class^="ion-radii-"],
-      [class^="ion-border-width-"] {
+      [class^='ion-scaling-'],
+      [class^='ion-spacing-'],
+      [class^='ion-radii-'],
+      [class^='ion-border-width-'] {
         margin: 10px;
       }
 
-      [class^="ion-scaling-"] {
+      [class^='ion-scaling-'] {
         background: #ededed;
       }
 
-      [class^="ion-spacing-"] {
+      [class^='ion-spacing-'] {
         background: #e0ee99;
       }
 
@@ -50,12 +50,12 @@
         background: #ededed;
       }
 
-      [class^="ion-radii-"] {
+      [class^='ion-radii-'] {
         border-width: 2px;
       }
 
-      [class^="ion-radii-"],
-      [class^="ion-border-width-"] {
+      [class^='ion-radii-'],
+      [class^='ion-border-width-'] {
         display: inline-flex;
         align-items: center;
         justify-content: center;
@@ -282,7 +282,7 @@ Border Width
             xxl
           
         
-      
+