Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- [Thumbnail](#version-9x-thumbnail)

Expand Down Expand Up @@ -290,6 +291,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.

<h4 id="version-9x-text">Text</h4>

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.

<h5>New `hue` property and color tokens</h5>

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` |

<h5>Theme classes</h5>

Remove any instances that target the theme classes: `ion-text.md`, `ion-text.ios`.

<h4 id="version-9x-textarea">Textarea</h4>

Converted `ion-textarea` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
Expand Down
4 changes: 3 additions & 1 deletion core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2707,8 +2707,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<never, never> | 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
Expand Down
39 changes: 21 additions & 18 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import { ActionSheetButton } from "./components/action-sheet/action-sheet-interf
import { OverlayEventDetail } from "./utils/overlays-interface";
import { IonicSafeString } from "./utils/sanitization";
import { AlertButton, AlertInput } from "./components/alert/alert-interface";
import { IonBadgeHue, IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interfaces";
import { Hue } from "./themes/themes.interfaces";
import { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interfaces";
import { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./components/router/utils/interface";
import { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface";
import { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface";
import { IonChipFill, IonChipHue, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces";
import { IonChipFill, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces";
import { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interfaces";
import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
import { GalleryColumns, GalleryGap } from "./components/gallery/gallery-interface";
Expand Down Expand Up @@ -54,11 +55,12 @@ export { ActionSheetButton } from "./components/action-sheet/action-sheet-interf
export { OverlayEventDetail } from "./utils/overlays-interface";
export { IonicSafeString } from "./utils/sanitization";
export { AlertButton, AlertInput } from "./components/alert/alert-interface";
export { IonBadgeHue, IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interfaces";
export { Hue } from "./themes/themes.interfaces";
export { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from "./components/badge/badge.interfaces";
export { RouteID, RouterDirection, RouterEventDetail, RouteWrite } from "./components/router/utils/interface";
export { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/breadcrumb-interface";
export { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface";
export { IonChipFill, IonChipHue, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces";
export { IonChipFill, IonChipShape, IonChipSize } from "./components/chip/chip.interfaces";
export { ScrollBaseDetail, ScrollDetail } from "./components/content/content.interfaces";
export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
export { GalleryColumns, GalleryGap } from "./components/gallery/gallery-interface";
Expand Down Expand Up @@ -477,7 +479,7 @@ export namespace Components {
/**
* Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset.
*/
"hue"?: IonBadgeHue;
"hue"?: Hue;
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down Expand Up @@ -884,7 +886,7 @@ export namespace Components {
/**
* Set to `"bold"` for a chip with vibrant, bold colors or to `"subtle"` for a chip with muted, subtle colors. Defaults to `"subtle"` if both the hue property and theme config are unset.
*/
"hue"?: IonChipHue;
"hue"?: Hue;
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down Expand Up @@ -4106,13 +4108,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"?: Hue;
/**
* 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 {
/**
Expand Down Expand Up @@ -6469,7 +6471,7 @@ declare namespace LocalJSX {
/**
* Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset.
*/
"hue"?: IonBadgeHue;
"hue"?: Hue;
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down Expand Up @@ -6911,7 +6913,7 @@ declare namespace LocalJSX {
/**
* Set to `"bold"` for a chip with vibrant, bold colors or to `"subtle"` for a chip with muted, subtle colors. Defaults to `"subtle"` if both the hue property and theme config are unset.
*/
"hue"?: IonChipHue;
"hue"?: Hue;
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down Expand Up @@ -10218,13 +10220,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"?: Hue;
/**
* 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 {
/**
Expand Down Expand Up @@ -10712,7 +10714,7 @@ declare namespace LocalJSX {
}
interface IonBadgeAttributes {
"color": Color;
"hue": IonBadgeHue;
"hue": Hue;
"shape": IonBadgeShape;
"size": IonBadgeSize;
"vertical": IonBadgeVerticalPosition;
Expand Down Expand Up @@ -10800,7 +10802,7 @@ declare namespace LocalJSX {
"outline": boolean;
"fill": IonChipFill;
"disabled": boolean;
"hue": IonChipHue;
"hue": Hue;
"shape": IonChipShape;
"size": IonChipSize;
}
Expand Down Expand Up @@ -11378,6 +11380,7 @@ declare namespace LocalJSX {
}
interface IonTextAttributes {
"color": Color;
"hue": Hue;
}
interface IonTextareaAttributes {
"color": Color;
Expand Down
8 changes: 3 additions & 5 deletions core/src/components/badge/badge.interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IonPadding } from '../../themes/themes.interfaces';
import type { IonPadding, Hue } from '../../themes/themes.interfaces';

export type IonBadgeRecipe = {
font?: {
Expand All @@ -7,7 +7,7 @@ export type IonBadgeRecipe = {

// Hues
hue?: {
[K in IonBadgeHue]?: IonBadgeStateDefinition & {
[K in Hue]?: IonBadgeStateDefinition & {
semantic?: IonBadgeStateDefinition;
};
};
Expand Down Expand Up @@ -80,13 +80,11 @@ type IonBadgeSizeDotDefinition = IonBadgeSizeDefinition & {
};

export type IonBadgeConfig = {
hue?: IonBadgeHue;
hue?: Hue;
size?: IonBadgeSize;
shape?: IonBadgeShape;
};

export const ION_BADGE_HUES = ['bold', 'subtle'] as const;
export type IonBadgeHue = (typeof ION_BADGE_HUES)[number];
export const ION_BADGE_SHAPES = ['crisp', 'soft', 'round', 'rectangular'] as const;
export type IonBadgeShape = (typeof ION_BADGE_SHAPES)[number];
export const ION_BADGE_SIZES = ['small', 'medium', 'large'] as const;
Expand Down
9 changes: 5 additions & 4 deletions core/src/components/badge/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { createColorClasses } from '@utils/theme';

import { config } from '../../global/config';
import type { Color } from '../../interface';
import type { Hue } from '../../themes/themes.interfaces';

import type { IonBadgeHue, IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from './badge.interfaces';
import type { IonBadgeShape, IonBadgeSize, IonBadgeVerticalPosition } from './badge.interfaces';

/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
Expand All @@ -31,7 +32,7 @@ export class Badge implements ComponentInterface {
*
* Defaults to `"bold"` if both the hue property and theme config are unset.
*/
@Prop() hue?: IonBadgeHue;
@Prop() hue?: Hue;

/**
* Set to `"crisp"` for a badge with even slightly rounded corners,
Expand Down Expand Up @@ -84,8 +85,8 @@ export class Badge implements ComponentInterface {
* Gets the badge hue. Uses the `hue` property if set, otherwise
* checks the theme config and falls back to 'bold' if neither is provided.
*/
get hueValue(): IonBadgeHue {
const hueConfig = config.getObjectValue('IonBadge.hue', 'bold') as IonBadgeHue;
get hueValue(): Hue {
const hueConfig = config.getObjectValue('IonBadge.hue', 'bold') as Hue;
const hue = this.hue || hueConfig;

return hue;
Expand Down
4 changes: 2 additions & 2 deletions core/src/components/badge/test/hue/badge.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

import { ION_BADGE_HUES } from '../../../badge/badge.interfaces';
import { HUES } from '../../../../themes/themes.interfaces';

/**
* This behavior does not vary across modes/directions.
Expand All @@ -10,7 +10,7 @@ import { ION_BADGE_HUES } from '../../../badge/badge.interfaces';
*/
configs({ directions: ['ltr'], modes: ['md', 'ionic-md'] }).forEach(({ config, screenshot, title }) => {
test.describe(title('badge: hue'), () => {
ION_BADGE_HUES.forEach((hue) => {
HUES.forEach((hue) => {
test(`should render ${hue} badges`, async ({ page }) => {
await page.setContent(
`
Expand Down
7 changes: 3 additions & 4 deletions core/src/components/chip/chip.interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IonPadding, IonMargin } from '../../themes/themes.interfaces';
import type { IonPadding, IonMargin, Hue } from '../../themes/themes.interfaces';

export type IonChipRecipe = {
letterSpacing?: string | number;
Expand All @@ -13,7 +13,7 @@ export type IonChipRecipe = {

// Hues with fills
hue?: {
[K in IonChipHue]?: IonChipFillDefinition;
[K in Hue]?: IonChipFillDefinition;
};

// Sizes
Expand Down Expand Up @@ -111,12 +111,11 @@ type IonChipAvatarDefinition = IonChipMediaDefinition & {

export type IonChipConfig = {
fill?: IonChipFill;
hue?: IonChipHue;
hue?: Hue;
size?: IonChipSize;
shape?: IonChipShape;
};

export type IonChipFill = 'outline' | 'solid';
export type IonChipHue = 'bold' | 'subtle';
export type IonChipSize = 'small' | 'large';
export type IonChipShape = 'soft' | 'round' | 'rectangular';
9 changes: 5 additions & 4 deletions core/src/components/chip/chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { createColorClasses } from '@utils/theme';

import { config } from '../../global/config';
import type { Color } from '../../interface';
import type { Hue } from '../../themes/themes.interfaces';

import type { IonChipFill, IonChipHue, IonChipSize, IonChipShape } from './chip.interfaces';
import type { IonChipFill, IonChipSize, IonChipShape } from './chip.interfaces';

/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
Expand Down Expand Up @@ -54,7 +55,7 @@ export class Chip implements ComponentInterface {
*
* Defaults to `"subtle"` if both the hue property and theme config are unset.
*/
@Prop() hue?: IonChipHue;
@Prop() hue?: Hue;

/**
* Set to `"soft"` for a chip with slightly rounded corners,
Expand Down Expand Up @@ -98,8 +99,8 @@ export class Chip implements ComponentInterface {
* Gets the chip hue. Uses the `hue` property if set, otherwise
* checks the theme config. Defaults to `subtle` if neither is set.
*/
get hueValue(): IonChipHue {
const hueConfig = config.getObjectValue('IonChip.hue', 'subtle') as IonChipHue;
get hueValue(): Hue {
const hueConfig = config.getObjectValue('IonChip.hue', 'subtle') as Hue;
const hue = this.hue || hueConfig;

return hue;
Expand Down
13 changes: 0 additions & 13 deletions core/src/components/text/test/basic/text.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,5 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
const text = page.locator('ion-text');
await expect(text.nth(0)).toHaveScreenshot(screenshot(`text`));
});
test('should render text with color prop', async ({ page }) => {
await page.setContent(
`
<ion-text color="primary">
<strong>The quick brown fox <ion-text color="success"><sup>jumps</sup></ion-text> over the <ion-text color="danger"><sub>lazy dog</sub></ion-text></strong>
</ion-text>
`,
config
);

const text = page.locator('ion-text');
await expect(text.nth(0)).toHaveScreenshot(screenshot(`text-color`));
});
});
});
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Loading