diff --git a/BREAKING.md b/BREAKING.md
index 12fca9b9580..3e62ae575d0 100644
--- a/BREAKING.md
+++ b/BREAKING.md
@@ -26,6 +26,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Item Divider](#version-9x-item-divider)
- [Radio Group](#version-9x-radio-group)
- [Spinner](#version-9x-spinner)
+ - [Text](#version-9x-text)
- [Textarea](#version-9x-textarea)
Global Styles
@@ -289,6 +290,28 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
- `.spinner-[spinner-name]` → `.spinner-name-[spinner-name]`
- Specific theme classes (e.g., `ion-spinner.md`) are no longer supported. Style modifications based on the active theme must be implemented using theme tokens rather than direct class targeting.
+
Text
+
+The following breaking changes apply to `ion-text`:
+
+1. The color applied by the `color` prop is now driven by the centralized Ionic Theming system, scoped to the new `hue` property.
+2. Theme classes (`ion-text.md`, `ion-text.ios`) are no longer supported.
+
+
New `hue` property and color tokens
+
+A new `hue` property selects between vibrant and muted color variants. It defaults to `"bold"`, which preserves prior behavior when `color` is set.
+
+When `color` is set, the text color now reads from a token instead of `--ion-color-base` directly. Global overrides should use the theme tokens; component-specific overrides use the corresponding CSS variables:
+
+| Hue | Token (global) | CSS variable (component-specific) |
+|---|---|---|
+| `bold` | `IonText.hue.bold.semantic.default.color` | `--ion-text-hue-bold-semantic-default-color` |
+| `subtle` | `IonText.hue.subtle.semantic.default.color` | `--ion-text-hue-subtle-semantic-default-color` |
+
+
Theme classes
+
+Remove any instances that target the theme classes: `ion-text.md`, `ion-text.ios`.
+
Textarea
Converted `ion-textarea` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
diff --git a/core/api.txt b/core/api.txt
index 618916e5be4..6defeed5dc0 100644
--- a/core/api.txt
+++ b/core/api.txt
@@ -2699,8 +2699,10 @@ ion-tabs,event,ionTabsWillChange,{ tab: string; },false
ion-text,shadow
ion-text,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record | undefined,undefined,false,true
+ion-text,prop,hue,"bold" | "subtle" | undefined,undefined,false,false
ion-text,prop,mode,"ios" | "md",undefined,false,false
-ion-text,prop,theme,"ios" | "md" | "ionic",undefined,false,false
+ion-text,css-prop,--ion-text-hue-bold-semantic-default-color
+ion-text,css-prop,--ion-text-hue-subtle-semantic-default-color
ion-textarea,shadow
ion-textarea,prop,autoGrow,boolean,false,false,true
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index 1b7292c8df1..440c0e6ceb8 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -44,6 +44,7 @@ import { SelectModalOption } from "./components/select-modal/select-modal-interf
import { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
import { SpinnerSize } from "./components/spinner/spinner.interfaces";
import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
+import { IonTextHue } from "./components/text/text.interfaces";
import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
import { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
@@ -86,6 +87,7 @@ export { SelectModalOption } from "./components/select-modal/select-modal-interf
export { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
export { SpinnerSize } from "./components/spinner/spinner.interfaces";
export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
+export { IonTextHue } from "./components/text/text.interfaces";
export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
export { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
@@ -4075,13 +4077,13 @@ export namespace Components {
*/
"color"?: Color;
/**
- * The mode determines the platform behaviors of the component.
+ * Set to `"bold"` for a text with vibrant, bold colors or to `"subtle"` for a text with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset.
*/
- "mode"?: "ios" | "md";
+ "hue"?: IonTextHue;
/**
- * The theme determines the visual appearance of the component.
+ * The mode determines the platform behaviors of the component.
*/
- "theme"?: "ios" | "md" | "ionic";
+ "mode"?: "ios" | "md";
}
interface IonTextarea {
/**
@@ -10155,13 +10157,13 @@ declare namespace LocalJSX {
*/
"color"?: Color;
/**
- * The mode determines the platform behaviors of the component.
+ * Set to `"bold"` for a text with vibrant, bold colors or to `"subtle"` for a text with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset.
*/
- "mode"?: "ios" | "md";
+ "hue"?: IonTextHue;
/**
- * The theme determines the visual appearance of the component.
+ * The mode determines the platform behaviors of the component.
*/
- "theme"?: "ios" | "md" | "ionic";
+ "mode"?: "ios" | "md";
}
interface IonTextarea {
/**
@@ -11313,6 +11315,7 @@ declare namespace LocalJSX {
}
interface IonTextAttributes {
"color": Color;
+ "hue": IonTextHue;
}
interface IonTextareaAttributes {
"color": Color;
diff --git a/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Chrome-linux.png b/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Chrome-linux.png
index f5fe40421a6..9ae6f0920aa 100644
Binary files a/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Firefox-linux.png b/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Firefox-linux.png
index f3f11dd72c6..33ec09299e0 100644
Binary files a/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Safari-linux.png b/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Safari-linux.png
index 484124fc7c1..f73830aa2b5 100644
Binary files a/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Safari-linux.png and b/core/src/components/text/test/basic/text.e2e.ts-snapshots/text-color-md-ltr-Mobile-Safari-linux.png differ
diff --git a/core/src/components/text/test/hue/index.html b/core/src/components/text/test/hue/index.html
new file mode 100644
index 00000000000..d430806d7f3
--- /dev/null
+++ b/core/src/components/text/test/hue/index.html
@@ -0,0 +1,61 @@
+
+
+
+
+ Text - Hue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Text - Hue
+
+
+
+
+
Text Hue: Bold
+
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+
+
Text Hue: Subtle
+
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+ The quick brown fox jumps over the lazy dog
+
+
+
+
diff --git a/core/src/components/text/text.interfaces.ts b/core/src/components/text/text.interfaces.ts
new file mode 100644
index 00000000000..30e643c57b1
--- /dev/null
+++ b/core/src/components/text/text.interfaces.ts
@@ -0,0 +1,18 @@
+export type IonTextRecipe = {
+ hue?: {
+ [K in IonTextHue]?: {
+ /** Any of the semantic colors like primary, secondary, etc. */
+ semantic?: {
+ default?: {
+ color?: string;
+ };
+ };
+ };
+ };
+};
+
+export type IonTextConfig = {
+ hue?: IonTextHue;
+};
+
+export type IonTextHue = 'bold' | 'subtle';
diff --git a/core/src/components/text/text.scss b/core/src/components/text/text.scss
index d86de15d200..f1f0be8fbd7 100644
--- a/core/src/components/text/text.scss
+++ b/core/src/components/text/text.scss
@@ -1,8 +1,19 @@
-@import "../../themes/native/native.globals";
-
-// Text
+// Text: Common Styles
// --------------------------------------------------
-:host(.ion-color) {
- color: current-color(base);
+:host {
+ /**
+ * @prop --ion-text-hue-bold-semantic-default-color: Color of the `bold` hue when a semantic color is applied
+ * @prop --ion-text-hue-subtle-semantic-default-color: Color of the `subtle` hue when a semantic color is applied
+ */
+
+ color: inherit;
+}
+
+:host(.text-hue-bold.ion-color) {
+ color: var(--ion-text-hue-bold-semantic-default-color);
+}
+
+:host(.text-hue-subtle.ion-color) {
+ color: var(--ion-text-hue-subtle-semantic-default-color);
}
diff --git a/core/src/components/text/text.tsx b/core/src/components/text/text.tsx
index c3fab899fd8..6cc8a5afdb1 100644
--- a/core/src/components/text/text.tsx
+++ b/core/src/components/text/text.tsx
@@ -2,12 +2,13 @@ import type { ComponentInterface } from '@stencil/core';
import { Component, Host, Prop, h } from '@stencil/core';
import { createColorClasses } from '@utils/theme';
-import { getIonTheme } from '../../global/ionic-global';
+import { config } from '../../global/config';
import type { Color } from '../../interface';
+import type { IonTextHue } from './text.interfaces';
+
/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
- * @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component.
*/
@Component({
tag: 'ion-text',
@@ -22,12 +23,30 @@ export class Text implements ComponentInterface {
*/
@Prop({ reflect: true }) color?: Color;
+ /**
+ * Set to `"bold"` for a text with vibrant, bold colors or to `"subtle"` for
+ * a text with muted, subtle colors.
+ *
+ * Defaults to `"bold"` if both the hue property and theme config are unset.
+ */
+ @Prop() hue?: IonTextHue;
+
+ /**
+ * Gets the text hue. Uses the `hue` property if set, otherwise
+ * checks the theme config and falls back to 'bold' if neither is provided.
+ */
+ get hueValue(): IonTextHue {
+ const hueConfig = config.getObjectValue('IonText.hue', 'bold') as IonTextHue;
+
+ return this.hue || hueConfig;
+ }
+
render() {
- const theme = getIonTheme(this);
+ const { hueValue } = this;
return (
diff --git a/core/src/themes/ionic/default.tokens.ts b/core/src/themes/ionic/default.tokens.ts
index 04aa66835c7..948c2b92882 100644
--- a/core/src/themes/ionic/default.tokens.ts
+++ b/core/src/themes/ionic/default.tokens.ts
@@ -41,6 +41,10 @@ export const defaultTheme: DefaultTheme = {
IonSpinner: {
size: 'xsmall',
},
+
+ IonText: {
+ hue: 'bold',
+ },
},
},
@@ -830,5 +834,25 @@ export const defaultTheme: DefaultTheme = {
},
},
},
+
+ IonText: {
+ hue: {
+ bold: {
+ semantic: {
+ default: {
+ color: currentColor('foreground'),
+ },
+ },
+ },
+
+ subtle: {
+ semantic: {
+ default: {
+ color: currentColor('foreground', { subtle: true }),
+ },
+ },
+ },
+ },
+ },
},
};
diff --git a/core/src/themes/ios/default.tokens.ts b/core/src/themes/ios/default.tokens.ts
index 26d8569a84d..a3883b0654c 100644
--- a/core/src/themes/ios/default.tokens.ts
+++ b/core/src/themes/ios/default.tokens.ts
@@ -43,6 +43,10 @@ export const defaultTheme: DefaultTheme = {
IonSpinner: {
size: 'medium',
},
+
+ IonText: {
+ hue: 'bold',
+ },
},
},
@@ -858,5 +862,25 @@ export const defaultTheme: DefaultTheme = {
},
},
},
+
+ IonText: {
+ hue: {
+ bold: {
+ semantic: {
+ default: {
+ color: currentColor('foreground'),
+ },
+ },
+ },
+
+ subtle: {
+ semantic: {
+ default: {
+ color: currentColor('foreground', { subtle: true }),
+ },
+ },
+ },
+ },
+ },
},
};
diff --git a/core/src/themes/md/default.tokens.ts b/core/src/themes/md/default.tokens.ts
index 0fa301c8b2a..0b185db44da 100644
--- a/core/src/themes/md/default.tokens.ts
+++ b/core/src/themes/md/default.tokens.ts
@@ -46,6 +46,10 @@ export const defaultTheme: DefaultTheme = {
IonSpinner: {
size: 'medium',
},
+
+ IonText: {
+ hue: 'bold',
+ },
},
},
@@ -983,5 +987,25 @@ export const defaultTheme: DefaultTheme = {
},
},
},
+
+ IonText: {
+ hue: {
+ bold: {
+ semantic: {
+ default: {
+ color: currentColor('foreground'),
+ },
+ },
+ },
+
+ subtle: {
+ semantic: {
+ default: {
+ color: currentColor('foreground', { subtle: true }),
+ },
+ },
+ },
+ },
+ },
},
};
diff --git a/core/src/themes/themes.interfaces.ts b/core/src/themes/themes.interfaces.ts
index 0a87addcbff..5804badc75d 100644
--- a/core/src/themes/themes.interfaces.ts
+++ b/core/src/themes/themes.interfaces.ts
@@ -4,6 +4,7 @@ import type { IonContentRecipe } from '../components/content/content.interfaces'
import type { IonItemDividerRecipe } from '../components/item-divider/item-divider.interfaces';
import type { IonProgressBarConfig, IonProgressBarRecipe } from '../components/progress-bar/progress-bar.interfaces';
import type { IonSpinnerConfig, IonSpinnerRecipe } from '../components/spinner/spinner.interfaces';
+import type { IonTextConfig, IonTextRecipe } from '../components/text/text.interfaces';
import type { IonicConfig as IonicGlobalConfig } from '../utils/config';
// Platform-specific theme
@@ -249,6 +250,7 @@ export type IonicConfig = IonicGlobalConfig & {
IonChip?: IonChipConfig;
IonProgressBar?: IonProgressBarConfig;
IonSpinner?: IonSpinnerConfig;
+ IonText?: IonTextConfig;
};
};
@@ -292,6 +294,7 @@ type Components = {
IonItemDivider?: IonItemDividerRecipe;
IonProgressBar?: IonProgressBarRecipe;
IonSpinner?: IonSpinnerRecipe;
+ IonText?: IonTextRecipe;
IonCard?: any;
IonItem?: any;
diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts
index 1dd5491da67..880c28a1d30 100644
--- a/packages/angular/src/directives/proxies.ts
+++ b/packages/angular/src/directives/proxies.ts
@@ -2414,14 +2414,14 @@ export declare interface IonTabButton extends Components.IonTabButton {}
@ProxyCmp({
- inputs: ['color', 'mode', 'theme']
+ inputs: ['color', 'hue', 'mode']
})
@Component({
selector: 'ion-text',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['color', 'mode', 'theme'],
+ inputs: ['color', 'hue', 'mode'],
})
export class IonText {
protected el: HTMLIonTextElement;
diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts
index d11f99cd10c..8f8f849b868 100644
--- a/packages/angular/standalone/src/directives/proxies.ts
+++ b/packages/angular/standalone/src/directives/proxies.ts
@@ -2161,14 +2161,14 @@ export declare interface IonTabButton extends Components.IonTabButton {}
@ProxyCmp({
defineCustomElementFn: defineIonText,
- inputs: ['color', 'mode', 'theme']
+ inputs: ['color', 'hue', 'mode']
})
@Component({
selector: 'ion-text',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['color', 'mode', 'theme'],
+ inputs: ['color', 'hue', 'mode'],
standalone: true
})
export class IonText {
diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts
index b2382020f56..64380f598d8 100644
--- a/packages/vue/src/proxies.ts
+++ b/packages/vue/src/proxies.ts
@@ -1042,7 +1042,8 @@ export const IonTab: StencilVueComponent = /*@__PURE__*/ defineConta
export const IonText: StencilVueComponent = /*@__PURE__*/ defineContainer('ion-text', defineIonText, [
- 'color'
+ 'color',
+ 'hue'
]);