diff --git a/BREAKING.md b/BREAKING.md index 0cdd59bdd67..90ca02217d7 100644 --- a/BREAKING.md +++ b/BREAKING.md @@ -64,11 +64,12 @@ The following breaking changes apply to `ion-button`: 7. The `ios` button follows iOS 17 for its font, icons and disabled state. [7](#version-10x-button-ios-17) 8. A badge without a `vertical` position now has a gap between it and the label, and no longer makes the button taller than its `min.height`. To remove the gap, set `IonButton.size..badge.margin.start` for global styles or `--ion-button-size--badge-margin-start` for component-specific styles to `0`. 9. A button with an icon in the `icon-only` slot now shows only that icon and any badge with a `vertical` position. Text, elements in the `start` and `end` slots, and other content are hidden. Hidden text still names the button for assistive technology. To show other content, remove the `icon-only` slot and use the `start` or `end` slot for the icon. -10. CSS variables have been replaced. [10](#version-10x-button-replaced-css-variables) -11. `--padding-*` CSS variables are no longer part of the documented public API (but remain functional). [11](#version-10x-button-padding-variables) -12. The fill, size, shape and expand classes have been renamed. [12](#version-10x-button-renamed-classes) -13. The `default` size has been deprecated. [13](#version-10x-button-deprecated-default-size) -14. Theme classes (`ion-button.md`, `ion-button.ios`) are no longer supported. [14](#version-10x-button-theme-classes) +10. The text of `outline` buttons in `md` and `ios` now uses the color's darker `foreground` variant instead of `base`, matching `clear` buttons, since it sits on the page rather than on a fill of the color. In `ios`, `clear` buttons with a `color` also keep `foreground` on hover and focus. To revert, set `IonButton.hue.bold.outline..color` (`.semantic..color` with a `color`) for global styles or `--ion-button-hue-bold-outline--color` (`--ion-button-hue-bold-outline-semantic--color`) for component-specific styles to the color's `base`. +11. CSS variables have been replaced. [11](#version-10x-button-replaced-css-variables) +12. `--padding-*` CSS variables are no longer part of the documented public API (but remain functional). [12](#version-10x-button-padding-variables) +13. The fill, size, shape and expand classes have been renamed. [13](#version-10x-button-renamed-classes) +14. The `default` size has been deprecated. [14](#version-10x-button-deprecated-default-size) +15. Theme classes (`ion-button.md`, `ion-button.ios`) are no longer supported. [15](#version-10x-button-theme-classes)
Material Design 2 in md
diff --git a/core/api.txt b/core/api.txt index b23bf210a22..3c6ae495183 100644 --- a/core/api.txt +++ b/core/api.txt @@ -439,6 +439,7 @@ ion-button,prop,expand,"block" | "full" | undefined,undefined,false,true ion-button,prop,fill,"clear" | "default" | "outline" | "solid" | undefined,undefined,false,true ion-button,prop,form,HTMLFormElement | string | undefined,undefined,false,false ion-button,prop,href,string | undefined,undefined,false,false +ion-button,prop,hue,"bold" | "subtle" | undefined,undefined,false,false ion-button,prop,mode,"ios" | "md",undefined,false,false ion-button,prop,rel,string | undefined,undefined,false,false ion-button,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false @@ -571,6 +572,126 @@ ion-button,css-prop,--ion-button-hue-bold-solid-semantic-hover-color ion-button,css-prop,--ion-button-hue-bold-solid-semantic-ripple-effect-color ion-button,css-prop,--ion-button-hue-bold-solid-semantic-ripple-effect-opacity ion-button,css-prop,--ion-button-hue-bold-solid-strong-font-weight +ion-button,css-prop,--ion-button-hue-subtle-clear-activated-background +ion-button,css-prop,--ion-button-hue-subtle-clear-activated-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-clear-activated-color +ion-button,css-prop,--ion-button-hue-subtle-clear-activated-opacity +ion-button,css-prop,--ion-button-hue-subtle-clear-default-background +ion-button,css-prop,--ion-button-hue-subtle-clear-default-border-color +ion-button,css-prop,--ion-button-hue-subtle-clear-default-border-style +ion-button,css-prop,--ion-button-hue-subtle-clear-default-border-width +ion-button,css-prop,--ion-button-hue-subtle-clear-default-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-clear-default-color +ion-button,css-prop,--ion-button-hue-subtle-clear-default-font-weight +ion-button,css-prop,--ion-button-hue-subtle-clear-disabled-background +ion-button,css-prop,--ion-button-hue-subtle-clear-disabled-border-color +ion-button,css-prop,--ion-button-hue-subtle-clear-disabled-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-clear-disabled-color +ion-button,css-prop,--ion-button-hue-subtle-clear-disabled-opacity +ion-button,css-prop,--ion-button-hue-subtle-clear-focus-background +ion-button,css-prop,--ion-button-hue-subtle-clear-focus-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-clear-focus-color +ion-button,css-prop,--ion-button-hue-subtle-clear-hover-background +ion-button,css-prop,--ion-button-hue-subtle-clear-hover-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-clear-hover-color +ion-button,css-prop,--ion-button-hue-subtle-clear-hover-opacity +ion-button,css-prop,--ion-button-hue-subtle-clear-ripple-effect-color +ion-button,css-prop,--ion-button-hue-subtle-clear-ripple-effect-opacity +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-activated-background +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-activated-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-default-background +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-default-border-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-default-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-disabled-background +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-disabled-border-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-disabled-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-focus-background +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-focus-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-hover-background +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-hover-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-ripple-effect-color +ion-button,css-prop,--ion-button-hue-subtle-clear-semantic-ripple-effect-opacity +ion-button,css-prop,--ion-button-hue-subtle-clear-strong-font-weight +ion-button,css-prop,--ion-button-hue-subtle-outline-activated-background +ion-button,css-prop,--ion-button-hue-subtle-outline-activated-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-outline-activated-color +ion-button,css-prop,--ion-button-hue-subtle-outline-activated-opacity +ion-button,css-prop,--ion-button-hue-subtle-outline-default-background +ion-button,css-prop,--ion-button-hue-subtle-outline-default-border-color +ion-button,css-prop,--ion-button-hue-subtle-outline-default-border-style +ion-button,css-prop,--ion-button-hue-subtle-outline-default-border-width +ion-button,css-prop,--ion-button-hue-subtle-outline-default-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-outline-default-color +ion-button,css-prop,--ion-button-hue-subtle-outline-default-font-weight +ion-button,css-prop,--ion-button-hue-subtle-outline-disabled-background +ion-button,css-prop,--ion-button-hue-subtle-outline-disabled-border-color +ion-button,css-prop,--ion-button-hue-subtle-outline-disabled-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-outline-disabled-color +ion-button,css-prop,--ion-button-hue-subtle-outline-disabled-opacity +ion-button,css-prop,--ion-button-hue-subtle-outline-focus-background +ion-button,css-prop,--ion-button-hue-subtle-outline-focus-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-outline-focus-color +ion-button,css-prop,--ion-button-hue-subtle-outline-hover-background +ion-button,css-prop,--ion-button-hue-subtle-outline-hover-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-outline-hover-color +ion-button,css-prop,--ion-button-hue-subtle-outline-hover-opacity +ion-button,css-prop,--ion-button-hue-subtle-outline-ripple-effect-color +ion-button,css-prop,--ion-button-hue-subtle-outline-ripple-effect-opacity +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-activated-background +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-activated-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-default-background +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-default-border-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-default-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-disabled-background +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-disabled-border-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-disabled-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-focus-background +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-focus-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-hover-background +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-hover-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-ripple-effect-color +ion-button,css-prop,--ion-button-hue-subtle-outline-semantic-ripple-effect-opacity +ion-button,css-prop,--ion-button-hue-subtle-outline-strong-font-weight +ion-button,css-prop,--ion-button-hue-subtle-solid-activated-background +ion-button,css-prop,--ion-button-hue-subtle-solid-activated-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-solid-activated-color +ion-button,css-prop,--ion-button-hue-subtle-solid-activated-opacity +ion-button,css-prop,--ion-button-hue-subtle-solid-default-background +ion-button,css-prop,--ion-button-hue-subtle-solid-default-border-color +ion-button,css-prop,--ion-button-hue-subtle-solid-default-border-style +ion-button,css-prop,--ion-button-hue-subtle-solid-default-border-width +ion-button,css-prop,--ion-button-hue-subtle-solid-default-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-solid-default-color +ion-button,css-prop,--ion-button-hue-subtle-solid-default-font-weight +ion-button,css-prop,--ion-button-hue-subtle-solid-disabled-background +ion-button,css-prop,--ion-button-hue-subtle-solid-disabled-border-color +ion-button,css-prop,--ion-button-hue-subtle-solid-disabled-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-solid-disabled-color +ion-button,css-prop,--ion-button-hue-subtle-solid-disabled-opacity +ion-button,css-prop,--ion-button-hue-subtle-solid-focus-background +ion-button,css-prop,--ion-button-hue-subtle-solid-focus-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-solid-focus-color +ion-button,css-prop,--ion-button-hue-subtle-solid-hover-background +ion-button,css-prop,--ion-button-hue-subtle-solid-hover-box-shadow +ion-button,css-prop,--ion-button-hue-subtle-solid-hover-color +ion-button,css-prop,--ion-button-hue-subtle-solid-hover-opacity +ion-button,css-prop,--ion-button-hue-subtle-solid-ripple-effect-color +ion-button,css-prop,--ion-button-hue-subtle-solid-ripple-effect-opacity +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-activated-background +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-activated-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-default-background +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-default-border-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-default-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-disabled-background +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-disabled-border-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-disabled-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-focus-background +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-focus-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-hover-background +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-hover-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-ripple-effect-color +ion-button,css-prop,--ion-button-hue-subtle-solid-semantic-ripple-effect-opacity +ion-button,css-prop,--ion-button-hue-subtle-solid-strong-font-weight ion-button,css-prop,--ion-button-letter-spacing ion-button,css-prop,--ion-button-margin-bottom ion-button,css-prop,--ion-button-margin-end diff --git a/core/src/components.d.ts b/core/src/components.d.ts index f6d8682c3fb..236053a961f 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -629,6 +629,10 @@ export namespace Components { * Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered. */ "href": string | undefined; + /** + * Set to `"bold"` for a button with vibrant, bold colors or to `"subtle"` for a button with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset. + */ + "hue"?: Hue; /** * The mode determines the platform behaviors of the component. */ @@ -6488,6 +6492,10 @@ declare namespace LocalJSX { * Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered. */ "href"?: string | undefined; + /** + * Set to `"bold"` for a button with vibrant, bold colors or to `"subtle"` for a button with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset. + */ + "hue"?: Hue; /** * The mode determines the platform behaviors of the component. */ @@ -10493,6 +10501,7 @@ declare namespace LocalJSX { "disabled": boolean; "expand": IonButtonExpand; "fill": IonButtonFill | 'default'; + "hue": Hue; "routerDirection": RouterDirection; "download": string | undefined; "href": string | undefined; diff --git a/core/src/components/button/_button.docs.scss b/core/src/components/button/_button.docs.scss index 1200028e9ef..5598283ba9a 100644 --- a/core/src/components/button/_button.docs.scss +++ b/core/src/components/button/_button.docs.scss @@ -13,6 +13,12 @@ * @prop --ion-button-hue-bold-outline-strong-font-weight: Font weight of the `bold` hue and `outline` fill combination on a `strong` button * @prop --ion-button-hue-bold-clear-default-font-weight: Font weight of the `bold` hue and `clear` fill combination in the default state * @prop --ion-button-hue-bold-clear-strong-font-weight: Font weight of the `bold` hue and `clear` fill combination on a `strong` button + * @prop --ion-button-hue-subtle-solid-default-font-weight: Font weight of the `subtle` hue and `solid` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-strong-font-weight: Font weight of the `subtle` hue and `solid` fill combination on a `strong` button + * @prop --ion-button-hue-subtle-outline-default-font-weight: Font weight of the `subtle` hue and `outline` fill combination in the default state + * @prop --ion-button-hue-subtle-outline-strong-font-weight: Font weight of the `subtle` hue and `outline` fill combination on a `strong` button + * @prop --ion-button-hue-subtle-clear-default-font-weight: Font weight of the `subtle` hue and `clear` fill combination in the default state + * @prop --ion-button-hue-subtle-clear-strong-font-weight: Font weight of the `subtle` hue and `clear` fill combination on a `strong` button * @prop --ion-button-size-small-min-width: Minimum width of the `small` button size * @prop --ion-button-size-small-min-height: Minimum height of the `small` button size, and the width and height of an icon-only button of that size * @prop --ion-button-size-small-font-size: Font size of the `small` button size @@ -264,4 +270,118 @@ * @prop --ion-button-hue-bold-clear-semantic-disabled-background: Background of the `bold` hue and `clear` fill combination in the disabled state when a semantic color is applied * @prop --ion-button-hue-bold-clear-semantic-disabled-color: Text color of the `bold` hue and `clear` fill combination in the disabled state when a semantic color is applied * @prop --ion-button-hue-bold-clear-semantic-disabled-border-color: Border color of the `bold` hue and `clear` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-default-border-color: Border color of the `subtle` hue and `outline` fill combination in the default state + * @prop --ion-button-hue-subtle-outline-default-color: Text color of the `subtle` hue and `outline` fill combination in the default state + * @prop --ion-button-hue-subtle-clear-default-color: Text color of the `subtle` hue and `clear` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-default-background: Background of the `subtle` hue and `solid` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-default-color: Text color of the `subtle` hue and `solid` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-default-border-width: Border width of the `subtle` hue and `solid` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-default-border-style: Border style of the `subtle` hue and `solid` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-default-border-color: Border color of the `subtle` hue and `solid` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-default-box-shadow: Box shadow of the `subtle` hue and `solid` fill combination in the default state + * @prop --ion-button-hue-subtle-solid-ripple-effect-color: Ripple color of the `subtle` hue and `solid` fill combination + * @prop --ion-button-hue-subtle-solid-ripple-effect-opacity: Ripple opacity of the `subtle` hue and `solid` fill combination + * @prop --ion-button-hue-subtle-solid-semantic-default-border-color: Border color of the `subtle` hue and `solid` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-default-background: Background of the `subtle` hue and `solid` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-default-color: Text color of the `subtle` hue and `solid` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-ripple-effect-color: Ripple color of the `subtle` hue and `solid` fill combination when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-ripple-effect-opacity: Ripple opacity of the `subtle` hue and `solid` fill combination when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-focus-color: Text color of the `subtle` hue and `solid` fill combination in the focus state + * @prop --ion-button-hue-subtle-solid-focus-background: Background of the `subtle` hue and `solid` fill combination in the focus state + * @prop --ion-button-hue-subtle-solid-focus-box-shadow: Box shadow of the `subtle` hue and `solid` fill combination in the focus state + * @prop --ion-button-hue-subtle-solid-semantic-focus-background: Background of the `subtle` hue and `solid` fill combination in the focus state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-focus-color: Text color of the `subtle` hue and `solid` fill combination in the focus state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-hover-color: Text color of the `subtle` hue and `solid` fill combination in the hover state + * @prop --ion-button-hue-subtle-solid-hover-opacity: Opacity of the whole button of the `subtle` hue and `solid` fill combination in the hover state + * @prop --ion-button-hue-subtle-solid-hover-background: Background of the `subtle` hue and `solid` fill combination in the hover state + * @prop --ion-button-hue-subtle-solid-hover-box-shadow: Box shadow of the `subtle` hue and `solid` fill combination in the hover state + * @prop --ion-button-hue-subtle-solid-semantic-hover-background: Background of the `subtle` hue and `solid` fill combination in the hover state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-hover-color: Text color of the `subtle` hue and `solid` fill combination in the hover state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-activated-color: Text color of the `subtle` hue and `solid` fill combination in the activated state + * @prop --ion-button-hue-subtle-solid-activated-opacity: Opacity of the whole button of the `subtle` hue and `solid` fill combination in the activated state + * @prop --ion-button-hue-subtle-solid-activated-background: Background of the `subtle` hue and `solid` fill combination in the activated state + * @prop --ion-button-hue-subtle-solid-activated-box-shadow: Box shadow of the `subtle` hue and `solid` fill combination in the activated state + * @prop --ion-button-hue-subtle-solid-semantic-activated-background: Background of the `subtle` hue and `solid` fill combination in the activated state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-activated-color: Text color of the `subtle` hue and `solid` fill combination in the activated state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-disabled-background: Background of the `subtle` hue and `solid` fill combination in the disabled state + * @prop --ion-button-hue-subtle-solid-disabled-opacity: Opacity of the whole button of the `subtle` hue and `solid` fill combination in the disabled state + * @prop --ion-button-hue-subtle-solid-disabled-color: Text color of the `subtle` hue and `solid` fill combination in the disabled state + * @prop --ion-button-hue-subtle-solid-disabled-border-color: Border color of the `subtle` hue and `solid` fill combination in the disabled state + * @prop --ion-button-hue-subtle-solid-disabled-box-shadow: Box shadow of the `subtle` hue and `solid` fill combination in the disabled state + * @prop --ion-button-hue-subtle-solid-semantic-disabled-background: Background of the `subtle` hue and `solid` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-disabled-color: Text color of the `subtle` hue and `solid` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-solid-semantic-disabled-border-color: Border color of the `subtle` hue and `solid` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-default-border-width: Border width of the `subtle` hue and `outline` fill combination in the default state + * @prop --ion-button-hue-subtle-outline-default-border-style: Border style of the `subtle` hue and `outline` fill combination in the default state + * @prop --ion-button-hue-subtle-outline-default-background: Background of the `subtle` hue and `outline` fill combination in the default state + * @prop --ion-button-hue-subtle-outline-default-box-shadow: Box shadow of the `subtle` hue and `outline` fill combination in the default state + * @prop --ion-button-hue-subtle-outline-ripple-effect-color: Ripple color of the `subtle` hue and `outline` fill combination + * @prop --ion-button-hue-subtle-outline-ripple-effect-opacity: Ripple opacity of the `subtle` hue and `outline` fill combination + * @prop --ion-button-hue-subtle-outline-semantic-default-border-color: Border color of the `subtle` hue and `outline` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-default-background: Background of the `subtle` hue and `outline` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-default-color: Text color of the `subtle` hue and `outline` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-ripple-effect-color: Ripple color of the `subtle` hue and `outline` fill combination when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-ripple-effect-opacity: Ripple opacity of the `subtle` hue and `outline` fill combination when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-focus-color: Text color of the `subtle` hue and `outline` fill combination in the focus state + * @prop --ion-button-hue-subtle-outline-focus-background: Background of the `subtle` hue and `outline` fill combination in the focus state + * @prop --ion-button-hue-subtle-outline-focus-box-shadow: Box shadow of the `subtle` hue and `outline` fill combination in the focus state + * @prop --ion-button-hue-subtle-outline-semantic-focus-background: Background of the `subtle` hue and `outline` fill combination in the focus state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-focus-color: Text color of the `subtle` hue and `outline` fill combination in the focus state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-hover-color: Text color of the `subtle` hue and `outline` fill combination in the hover state + * @prop --ion-button-hue-subtle-outline-hover-opacity: Opacity of the whole button of the `subtle` hue and `outline` fill combination in the hover state + * @prop --ion-button-hue-subtle-outline-hover-background: Background of the `subtle` hue and `outline` fill combination in the hover state + * @prop --ion-button-hue-subtle-outline-hover-box-shadow: Box shadow of the `subtle` hue and `outline` fill combination in the hover state + * @prop --ion-button-hue-subtle-outline-semantic-hover-background: Background of the `subtle` hue and `outline` fill combination in the hover state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-hover-color: Text color of the `subtle` hue and `outline` fill combination in the hover state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-activated-color: Text color of the `subtle` hue and `outline` fill combination in the activated state + * @prop --ion-button-hue-subtle-outline-activated-opacity: Opacity of the whole button of the `subtle` hue and `outline` fill combination in the activated state + * @prop --ion-button-hue-subtle-outline-activated-background: Background of the `subtle` hue and `outline` fill combination in the activated state + * @prop --ion-button-hue-subtle-outline-activated-box-shadow: Box shadow of the `subtle` hue and `outline` fill combination in the activated state + * @prop --ion-button-hue-subtle-outline-semantic-activated-background: Background of the `subtle` hue and `outline` fill combination in the activated state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-activated-color: Text color of the `subtle` hue and `outline` fill combination in the activated state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-disabled-background: Background of the `subtle` hue and `outline` fill combination in the disabled state + * @prop --ion-button-hue-subtle-outline-disabled-opacity: Opacity of the whole button of the `subtle` hue and `outline` fill combination in the disabled state + * @prop --ion-button-hue-subtle-outline-disabled-color: Text color of the `subtle` hue and `outline` fill combination in the disabled state + * @prop --ion-button-hue-subtle-outline-disabled-border-color: Border color of the `subtle` hue and `outline` fill combination in the disabled state + * @prop --ion-button-hue-subtle-outline-disabled-box-shadow: Box shadow of the `subtle` hue and `outline` fill combination in the disabled state + * @prop --ion-button-hue-subtle-outline-semantic-disabled-background: Background of the `subtle` hue and `outline` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-disabled-color: Text color of the `subtle` hue and `outline` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-outline-semantic-disabled-border-color: Border color of the `subtle` hue and `outline` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-default-border-width: Border width of the `subtle` hue and `clear` fill combination in the default state + * @prop --ion-button-hue-subtle-clear-default-border-style: Border style of the `subtle` hue and `clear` fill combination in the default state + * @prop --ion-button-hue-subtle-clear-default-border-color: Border color of the `subtle` hue and `clear` fill combination in the default state + * @prop --ion-button-hue-subtle-clear-default-background: Background of the `subtle` hue and `clear` fill combination in the default state + * @prop --ion-button-hue-subtle-clear-default-box-shadow: Box shadow of the `subtle` hue and `clear` fill combination in the default state + * @prop --ion-button-hue-subtle-clear-ripple-effect-color: Ripple color of the `subtle` hue and `clear` fill combination + * @prop --ion-button-hue-subtle-clear-ripple-effect-opacity: Ripple opacity of the `subtle` hue and `clear` fill combination + * @prop --ion-button-hue-subtle-clear-semantic-default-border-color: Border color of the `subtle` hue and `clear` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-default-background: Background of the `subtle` hue and `clear` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-default-color: Text color of the `subtle` hue and `clear` fill combination in the default state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-ripple-effect-color: Ripple color of the `subtle` hue and `clear` fill combination when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-ripple-effect-opacity: Ripple opacity of the `subtle` hue and `clear` fill combination when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-focus-color: Text color of the `subtle` hue and `clear` fill combination in the focus state + * @prop --ion-button-hue-subtle-clear-focus-background: Background of the `subtle` hue and `clear` fill combination in the focus state + * @prop --ion-button-hue-subtle-clear-focus-box-shadow: Box shadow of the `subtle` hue and `clear` fill combination in the focus state + * @prop --ion-button-hue-subtle-clear-semantic-focus-background: Background of the `subtle` hue and `clear` fill combination in the focus state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-focus-color: Text color of the `subtle` hue and `clear` fill combination in the focus state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-hover-color: Text color of the `subtle` hue and `clear` fill combination in the hover state + * @prop --ion-button-hue-subtle-clear-hover-opacity: Opacity of the whole button of the `subtle` hue and `clear` fill combination in the hover state + * @prop --ion-button-hue-subtle-clear-hover-background: Background of the `subtle` hue and `clear` fill combination in the hover state + * @prop --ion-button-hue-subtle-clear-hover-box-shadow: Box shadow of the `subtle` hue and `clear` fill combination in the hover state + * @prop --ion-button-hue-subtle-clear-semantic-hover-background: Background of the `subtle` hue and `clear` fill combination in the hover state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-hover-color: Text color of the `subtle` hue and `clear` fill combination in the hover state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-activated-color: Text color of the `subtle` hue and `clear` fill combination in the activated state + * @prop --ion-button-hue-subtle-clear-activated-opacity: Opacity of the whole button of the `subtle` hue and `clear` fill combination in the activated state + * @prop --ion-button-hue-subtle-clear-activated-background: Background of the `subtle` hue and `clear` fill combination in the activated state + * @prop --ion-button-hue-subtle-clear-activated-box-shadow: Box shadow of the `subtle` hue and `clear` fill combination in the activated state + * @prop --ion-button-hue-subtle-clear-semantic-activated-background: Background of the `subtle` hue and `clear` fill combination in the activated state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-activated-color: Text color of the `subtle` hue and `clear` fill combination in the activated state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-disabled-background: Background of the `subtle` hue and `clear` fill combination in the disabled state + * @prop --ion-button-hue-subtle-clear-disabled-opacity: Opacity of the whole button of the `subtle` hue and `clear` fill combination in the disabled state + * @prop --ion-button-hue-subtle-clear-disabled-color: Text color of the `subtle` hue and `clear` fill combination in the disabled state + * @prop --ion-button-hue-subtle-clear-disabled-border-color: Border color of the `subtle` hue and `clear` fill combination in the disabled state + * @prop --ion-button-hue-subtle-clear-disabled-box-shadow: Box shadow of the `subtle` hue and `clear` fill combination in the disabled state + * @prop --ion-button-hue-subtle-clear-semantic-disabled-background: Background of the `subtle` hue and `clear` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-disabled-color: Text color of the `subtle` hue and `clear` fill combination in the disabled state when a semantic color is applied + * @prop --ion-button-hue-subtle-clear-semantic-disabled-border-color: Border color of the `subtle` hue and `clear` fill combination in the disabled state when a semantic color is applied */ diff --git a/core/src/components/button/button.scss b/core/src/components/button/button.scss index e30441b988d..433848dce1f 100644 --- a/core/src/components/button/button.scss +++ b/core/src/components/button/button.scss @@ -4,6 +4,7 @@ // Button // -------------------------------------------------- +$hues: bold, subtle; $fills: solid, outline, clear; $sizes: small, medium, large; $shapes: crisp, soft, round, rectangular; @@ -133,15 +134,17 @@ $spinner-sizes: xsmall, small, medium, large, xlarge; // Button: Fill Typography // -------------------------------------------------- -@each $fill in $fills { - :host(.button-hue-bold.button-fill-#{$fill}) { - font-weight: var(--ion-button-hue-bold-#{$fill}-default-font-weight); +@each $hue in $hues { + @each $fill in $fills { + :host(.button-hue-#{$hue}.button-fill-#{$fill}) { + font-weight: var(--ion-button-hue-#{$hue}-#{$fill}-default-font-weight); + } } -} -@each $fill in $fills { - :host(.button-hue-bold.button-fill-#{$fill}.button-strong) { - font-weight: var(--ion-button-hue-bold-#{$fill}-strong-font-weight); + @each $fill in $fills { + :host(.button-hue-#{$hue}.button-fill-#{$fill}.button-strong) { + font-weight: var(--ion-button-hue-#{$hue}-#{$fill}-strong-font-weight); + } } } @@ -283,170 +286,172 @@ $spinner-sizes: xsmall, small, medium, large, xlarge; // Button: Hues and Fills // -------------------------------------------------- -/** - * Each fill has a resting look and one for each state. Without a color, - * the button reads the plain tokens. With a color, it reads the `semantic` - * tokens, which use the color's base, contrast and so on. - */ -@each $fill in $fills { - $token: --ion-button-hue-bold-#{$fill}; +@each $hue in $hues { + /** + * Each fill has a resting look and one for each state. Without a color, + * the button reads the plain tokens. With a color, it reads the `semantic` + * tokens, which use the color's base, contrast and so on. + */ + @each $fill in $fills { + $token: --ion-button-hue-#{$hue}-#{$fill}; - :host(.button-hue-bold.button-fill-#{$fill}) { - color: var(#{$token}-default-color); - } + :host(.button-hue-#{$hue}.button-fill-#{$fill}) { + color: var(#{$token}-default-color); + } - :host(.button-hue-bold.button-fill-#{$fill}) .button-native { - border-width: var(#{$token}-default-border-width); - border-style: var(#{$token}-default-border-style); - border-color: var(#{$token}-default-border-color); + :host(.button-hue-#{$hue}.button-fill-#{$fill}) .button-native { + border-width: var(#{$token}-default-border-width); + border-style: var(#{$token}-default-border-style); + border-color: var(#{$token}-default-border-color); - background: var(#{$token}-default-background); + background: var(#{$token}-default-background); - box-shadow: var(#{$token}-default-box-shadow); - } + box-shadow: var(#{$token}-default-box-shadow); + } - // The ripple sits in this shadow root, out of reach of the global token CSS - :host(.button-hue-bold.button-fill-#{$fill}) ion-ripple-effect { - // A variable, because the ripple animates its opacity in a keyframe - --ion-ripple-effect-opacity: var(#{$token}-ripple-effect-opacity); + // The ripple sits in this shadow root, out of reach of the global token CSS + :host(.button-hue-#{$hue}.button-fill-#{$fill}) ion-ripple-effect { + // A variable, because the ripple animates its opacity in a keyframe + --ion-ripple-effect-opacity: var(#{$token}-ripple-effect-opacity); - color: var(#{$token}-ripple-effect-color); - } + color: var(#{$token}-ripple-effect-color); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-color) .button-native { - border-color: var(#{$token}-semantic-default-border-color); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-color) .button-native { + border-color: var(#{$token}-semantic-default-border-color); - background: var(#{$token}-semantic-default-background); - color: var(#{$token}-semantic-default-color); - } + background: var(#{$token}-semantic-default-background); + color: var(#{$token}-semantic-default-color); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-color) ion-ripple-effect { - --ion-ripple-effect-opacity: var(#{$token}-semantic-ripple-effect-opacity); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-color) ion-ripple-effect { + --ion-ripple-effect-opacity: var(#{$token}-semantic-ripple-effect-opacity); - color: var(#{$token}-semantic-ripple-effect-color); + color: var(#{$token}-semantic-ripple-effect-color); + } } -} -/** - * The states are written in this order: focus -> hover -> activated -> disabled. - * Their rules share a specificity, so a later state overrides an earlier - * one: disabled overrides all, and activated overrides hover and focus. - */ + /** + * The states are written in this order: focus -> hover -> activated -> disabled. + * Their rules share a specificity, so a later state overrides an earlier + * one: disabled overrides all, and activated overrides hover and focus. + */ -// Focused -@each $fill in $fills { - $token: --ion-button-hue-bold-#{$fill}; + // Focused + @each $fill in $fills { + $token: --ion-button-hue-#{$hue}-#{$fill}; - :host(.button-hue-bold.button-fill-#{$fill}.ion-focused) { - color: var(#{$token}-focus-color); - } + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-focused) { + color: var(#{$token}-focus-color); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-focused) .button-native { - background: var(#{$token}-focus-background); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-focused) .button-native { + background: var(#{$token}-focus-background); - box-shadow: var(#{$token}-focus-box-shadow); - } + box-shadow: var(#{$token}-focus-box-shadow); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-color.ion-focused) .button-native { - background: var(#{$token}-semantic-focus-background); - color: var(#{$token}-semantic-focus-color); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-color.ion-focused) .button-native { + background: var(#{$token}-semantic-focus-background); + color: var(#{$token}-semantic-focus-color); + } } -} -// Hover -@media (any-hover: hover) { - @each $fill in $fills { - $token: --ion-button-hue-bold-#{$fill}; + // Hover + @media (any-hover: hover) { + @each $fill in $fills { + $token: --ion-button-hue-#{$hue}-#{$fill}; - :host(.button-hue-bold.button-fill-#{$fill}:hover) { - color: var(#{$token}-hover-color); - } + :host(.button-hue-#{$hue}.button-fill-#{$fill}:hover) { + color: var(#{$token}-hover-color); + } - // Disabled keeps its own opacity - :host(.button-hue-bold.button-fill-#{$fill}:not(.button-disabled):hover) { - opacity: var(#{$token}-hover-opacity); - } + // Disabled keeps its own opacity + :host(.button-hue-#{$hue}.button-fill-#{$fill}:not(.button-disabled):hover) { + opacity: var(#{$token}-hover-opacity); + } - :host(.button-hue-bold.button-fill-#{$fill}:hover) .button-native { - background: var(#{$token}-hover-background); + :host(.button-hue-#{$hue}.button-fill-#{$fill}:hover) .button-native { + background: var(#{$token}-hover-background); - box-shadow: var(#{$token}-hover-box-shadow); - } + box-shadow: var(#{$token}-hover-box-shadow); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-color:hover) .button-native { - background: var(#{$token}-semantic-hover-background); - color: var(#{$token}-semantic-hover-color); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-color:hover) .button-native { + background: var(#{$token}-semantic-hover-background); + color: var(#{$token}-semantic-hover-color); + } } } -} -// Activated -@each $fill in $fills { - $token: --ion-button-hue-bold-#{$fill}; + // Activated + @each $fill in $fills { + $token: --ion-button-hue-#{$hue}-#{$fill}; - :host(.button-hue-bold.button-fill-#{$fill}.ion-activated) { - color: var(#{$token}-activated-color); - } + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-activated) { + color: var(#{$token}-activated-color); + } - // Disabled keeps its own opacity - :host(.button-hue-bold.button-fill-#{$fill}.ion-activated:not(.button-disabled)) { - opacity: var(#{$token}-activated-opacity); - } + // Disabled keeps its own opacity + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-activated:not(.button-disabled)) { + opacity: var(#{$token}-activated-opacity); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-activated) .button-native { - background: var(#{$token}-activated-background); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-activated) .button-native { + background: var(#{$token}-activated-background); - box-shadow: var(#{$token}-activated-box-shadow); - } + box-shadow: var(#{$token}-activated-box-shadow); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-color.ion-activated) .button-native { - background: var(#{$token}-semantic-activated-background); - color: var(#{$token}-semantic-activated-color); - } + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-color.ion-activated) .button-native { + background: var(#{$token}-semantic-activated-background); + color: var(#{$token}-semantic-activated-color); + } - /** - * Only `md` mode renders a ripple. When it does, the ripple shows the press, - * so the button keeps its default background and text color instead of the activated ones. - */ - :host(.button-hue-bold.button-fill-#{$fill}.ion-activated) .button-native:has(ion-ripple-effect) { - background: var(#{$token}-default-background); - color: var(#{$token}-default-color); - } + /** + * Only `md` mode renders a ripple. When it does, the ripple shows the press, + * so the button keeps its default background and text color instead of the activated ones. + */ + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-activated) .button-native:has(ion-ripple-effect) { + background: var(#{$token}-default-background); + color: var(#{$token}-default-color); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-color.ion-activated) .button-native:has(ion-ripple-effect) { - background: var(#{$token}-semantic-default-background); - color: var(#{$token}-semantic-default-color); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-color.ion-activated) .button-native:has(ion-ripple-effect) { + background: var(#{$token}-semantic-default-background); + color: var(#{$token}-semantic-default-color); + } } -} -// Disabled -@each $fill in $fills { - $token: --ion-button-hue-bold-#{$fill}; + // Disabled + @each $fill in $fills { + $token: --ion-button-hue-#{$hue}-#{$fill}; - :host(.button-hue-bold.button-fill-#{$fill}.button-disabled) { - // Keeps the default color when the theme doesn't set one - color: var(#{$token}-disabled-color, var(#{$token}-default-color)); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.button-disabled) { + // Keeps the default color when the theme doesn't set one + color: var(#{$token}-disabled-color, var(#{$token}-default-color)); - opacity: var(#{$token}-disabled-opacity); - } + opacity: var(#{$token}-disabled-opacity); + } - :host(.button-hue-bold.button-fill-#{$fill}.button-disabled) .button-native { - // Keeps the default border color when the theme doesn't set one - border-color: var(#{$token}-disabled-border-color, var(#{$token}-default-border-color)); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.button-disabled) .button-native { + // Keeps the default border color when the theme doesn't set one + border-color: var(#{$token}-disabled-border-color, var(#{$token}-default-border-color)); - background: var(#{$token}-disabled-background); + background: var(#{$token}-disabled-background); - box-shadow: var(#{$token}-disabled-box-shadow); - } + box-shadow: var(#{$token}-disabled-box-shadow); + } - :host(.button-hue-bold.button-fill-#{$fill}.ion-color.button-disabled) .button-native { - // Keeps the default border color when the theme doesn't set one - border-color: var(#{$token}-semantic-disabled-border-color, var(#{$token}-semantic-default-border-color)); + :host(.button-hue-#{$hue}.button-fill-#{$fill}.ion-color.button-disabled) .button-native { + // Keeps the default border color when the theme doesn't set one + border-color: var(#{$token}-semantic-disabled-border-color, var(#{$token}-semantic-default-border-color)); - background: var(#{$token}-semantic-disabled-background); + background: var(#{$token}-semantic-disabled-background); - // Keeps the default color when the theme doesn't set one - color: var(#{$token}-semantic-disabled-color, var(#{$token}-semantic-default-color)); + // Keeps the default color when the theme doesn't set one + color: var(#{$token}-semantic-disabled-color, var(#{$token}-semantic-default-color)); + } } } diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index 48716d20be9..d31f50bb29d 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -95,6 +95,14 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf */ @Prop({ reflect: true, mutable: true }) fill?: IonButtonFill | 'default'; + /** + * Set to `"bold"` for a button with vibrant, bold colors or to `"subtle"` for + * a button with muted, subtle colors. + * + * Defaults to `"bold"` if both the hue property and theme config are unset. + */ + @Prop() hue?: Hue; + /** * When using a router, it specifies the transition direction when navigating to * another page using `href`. @@ -294,10 +302,13 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf } /** - * Gets the button hue from the theme config. Defaults to `bold` if it is unset. + * Gets the button hue. Uses the `hue` property if set, otherwise + * checks the theme config. Defaults to `bold` if neither is set. */ get hueValue(): Hue { - return config.getObjectValue('IonButton.hue', 'bold') as Hue; + const hueConfig = config.getObjectValue('IonButton.hue', 'bold') as Hue; + + return this.hue ?? hueConfig; } /** @@ -496,7 +507,6 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf [`${buttonType}-size-${sizeValue}`]: true, [`${buttonType}-shape-${shapeValue}`]: true, [`${buttonType}-fill-${fillValue}`]: true, - // TODO(FW-6838): read the hue prop first in the hue PR [`${buttonType}-hue-${hueValue}`]: true, [`${buttonType}-strong`]: strong, 'in-datetime': this.inDatetime, diff --git a/core/src/components/button/test/a11y/button.e2e.ts b/core/src/components/button/test/a11y/button.e2e.ts index dae8cfd59e2..a3fececd4d9 100644 --- a/core/src/components/button/test/a11y/button.e2e.ts +++ b/core/src/components/button/test/a11y/button.e2e.ts @@ -2,6 +2,11 @@ import AxeBuilder from '@axe-core/playwright'; import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +import { ION_BUTTON_FILLS } from '../../button.interface'; + +/** + * This behavior does not vary across directions. + */ configs({ directions: ['ltr'], palettes: ['light', 'dark'] }).forEach(({ title, config }) => { test.describe(title('button: a11y for ion-color()'), () => { test('should not have accessibility violations', async ({ page }) => { @@ -282,3 +287,33 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { }); }); }); + +/** + * TODO(FW-6417): the subtle palette fails contrast for success, warning and + * light, and in the high contrast palettes. Add them back once it is fixed. + */ +const COLORS = ['primary', 'secondary', 'tertiary', 'danger', 'medium', 'dark']; + +/** + * This behavior does not vary across directions. + */ +configs({ directions: ['ltr'], palettes: ['light', 'dark'] }).forEach(({ title, config }) => { + test.describe(title('button: subtle hue a11y'), () => { + test('should not have accessibility violations', async ({ page }) => { + await page.setContent( + ION_BUTTON_FILLS.map((fill) => + [undefined, ...COLORS] + .map( + (color) => + `Button` + ) + .join('') + ).join(''), + config + ); + + const results = await new AxeBuilder({ page }).analyze(); + expect(results.violations).toEqual([]); + }); + }); +}); diff --git a/core/src/components/button/test/hue/button.e2e.ts b/core/src/components/button/test/hue/button.e2e.ts new file mode 100644 index 00000000000..1a64bff6090 --- /dev/null +++ b/core/src/components/button/test/hue/button.e2e.ts @@ -0,0 +1,76 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +import { HUES } from '../../../../themes/themes.interfaces'; +import { ION_BUTTON_FILLS } from '../../button.interface'; + +const states = ['', 'class="ion-focused"', 'class="ion-activated"', 'disabled']; + +const colors = ['', 'primary', 'secondary', 'tertiary', 'success', 'warning', 'danger', 'light', 'medium', 'dark']; + +/** + * This behavior does not vary across directions. + */ +configs({ directions: ['ltr'], modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('button: hue'), () => { + HUES.forEach((hue) => { + test(`should render ${hue} buttons in each fill and state`, async ({ page }) => { + const sections = ION_BUTTON_FILLS.map( + (fill) => ` +

${fill}

+ ${states + .map( + (state) => + `
${colors + .map( + (color) => + `${ + color || 'Default' + }` + ) + .join('')}
` + ) + .join('\n')} + ` + ).join('\n'); + + await page.setContent( + ` + + +
+ ${sections} +
+ `, + config + ); + + const container = page.locator('.hue-container'); + + // Firefox and Safari leave the area below the viewport transparent + const box = await container.boundingBox(); + await page.setViewportSize({ width: Math.ceil(box!.width), height: Math.ceil(box!.height) }); + + await expect(container).toHaveScreenshot(screenshot(`button-hue-${hue}`)); + }); + }); + }); +}); diff --git a/core/src/components/button/test/hue/index.html b/core/src/components/button/test/hue/index.html new file mode 100644 index 00000000000..4f9cfd9383f --- /dev/null +++ b/core/src/components/button/test/hue/index.html @@ -0,0 +1,97 @@ + + + + + Button - Hue + + + + + + + + + + + + + + + Button - Hue + + + + + + + + + diff --git a/core/src/components/button/test/spinner/button.e2e.ts b/core/src/components/button/test/spinner/button.e2e.ts index 627a3051d78..62c971bcc05 100644 --- a/core/src/components/button/test/spinner/button.e2e.ts +++ b/core/src/components/button/test/spinner/button.e2e.ts @@ -1,6 +1,9 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +/** + * This behavior does not vary across directions. + */ configs({ modes: ['ionic-md', 'md', 'ios'], directions: ['ltr'] }).forEach(({ config, screenshot, title }) => { test.describe(title('button: spinner'), () => { test('should not have visual regressions', async ({ page }) => { diff --git a/core/src/components/button/test/states/button.e2e.ts b/core/src/components/button/test/states/button.e2e.ts index d68676545f0..4097342b59e 100644 --- a/core/src/components/button/test/states/button.e2e.ts +++ b/core/src/components/button/test/states/button.e2e.ts @@ -5,6 +5,9 @@ import { ION_BUTTON_FILLS } from '../../button.interface'; const STATES = ['activated', 'focused']; +/** + * This behavior does not vary across directions. + */ configs({ directions: ['ltr'], modes: ['ionic-md', 'md', 'ios'] }).forEach(({ title, screenshot, config }) => { test.describe(title('button: states'), () => { STATES.forEach((state) => { diff --git a/core/src/themes/ionic/default.tokens.ts b/core/src/themes/ionic/default.tokens.ts index c1e1e1975d1..1e068d15f92 100644 --- a/core/src/themes/ionic/default.tokens.ts +++ b/core/src/themes/ionic/default.tokens.ts @@ -564,6 +564,244 @@ export const defaultTheme: DefaultTheme = { }, }, }, + + subtle: { + solid: { + strong: { + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + default: { + background: ionColor('primary', 'base', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + focus: { + background: ionColor('primary', 'base', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + hover: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + activated: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + // A white wash over the button + disabled: { + background: ionColor('primary', 'base', { subtle: true }), + + opacity: '0.4', + }, + + rippleEffect: { + color: ionColor('primary', 'shade', { subtle: true }), + opacity: '1', + }, + + // Any of the semantic colors like primary, secondary, etc. + semantic: { + default: { + background: currentColor('base', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + focus: { + background: currentColor('base', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + hover: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + activated: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + disabled: { + background: currentColor('base', { subtle: true }), + }, + + rippleEffect: { + color: currentColor('shade', { subtle: true }), + opacity: '1', + }, + }, + }, + + outline: { + strong: { + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + default: { + background: baseColors.backgroundColor, + color: ionColor('primary', 'foreground', { subtle: true }), + + border: { + color: ionColor('primary', 'shade', { subtle: true }), + style: 'solid', + width: 'var(--ion-border-width-xxxs)', + }, + + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + focus: { + background: baseColors.backgroundColor, + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + hover: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + activated: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + // A white wash over the button + disabled: { + background: baseColors.backgroundColor, + + opacity: '0.4', + }, + + rippleEffect: { + color: ionColor('primary', 'shade', { subtle: true }), + opacity: '1', + }, + + semantic: { + default: { + background: baseColors.backgroundColor, + color: currentColor('foreground', { subtle: true }), + + border: { + color: currentColor('shade', { subtle: true }), + }, + }, + + focus: { + background: baseColors.backgroundColor, + color: currentColor('foreground', { subtle: true }), + }, + + hover: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + activated: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + disabled: { + background: baseColors.backgroundColor, + }, + + rippleEffect: { + color: currentColor('shade', { subtle: true }), + opacity: '1', + }, + }, + }, + + clear: { + strong: { + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + default: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + focus: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + hover: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + activated: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + // A white wash over the button + disabled: { + background: 'transparent', + + opacity: '0.4', + }, + + rippleEffect: { + color: ionColor('primary', 'shade', { subtle: true }), + opacity: '1', + }, + + semantic: { + default: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + focus: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + hover: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + activated: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + disabled: { + background: 'transparent', + }, + + rippleEffect: { + color: currentColor('shade', { subtle: true }), + opacity: '1', + }, + }, + }, + }, }, // Sizes diff --git a/core/src/themes/ios/default.tokens.ts b/core/src/themes/ios/default.tokens.ts index 48cb77a5754..162561afa3a 100644 --- a/core/src/themes/ios/default.tokens.ts +++ b/core/src/themes/ios/default.tokens.ts @@ -413,7 +413,7 @@ export const defaultTheme: DefaultTheme = { default: { background: 'transparent', - color: ionColor('primary', 'base'), + color: ionColor('primary', 'foreground'), border: { color: ionColor('primary', 'base'), @@ -428,13 +428,13 @@ export const defaultTheme: DefaultTheme = { focus: { background: ionColor('primary', 'base', { alpha: 0.1 }), - color: ionColor('primary', 'base'), + color: ionColor('primary', 'foreground'), }, // Fades the whole button instead of changing its color hover: { background: 'transparent', - color: ionColor('primary', 'base'), + color: ionColor('primary', 'foreground'), opacity: '0.6', }, @@ -456,7 +456,7 @@ export const defaultTheme: DefaultTheme = { semantic: { default: { background: 'transparent', - color: currentColor('base'), + color: currentColor('foreground'), border: { color: currentColor('base'), @@ -465,12 +465,12 @@ export const defaultTheme: DefaultTheme = { focus: { background: currentColor('base', { alpha: 0.1 }), - color: currentColor('base'), + color: currentColor('foreground'), }, hover: { background: 'transparent', - color: currentColor('base'), + color: currentColor('foreground'), }, activated: { @@ -538,12 +538,12 @@ export const defaultTheme: DefaultTheme = { focus: { background: currentColor('base', { alpha: 0.1 }), - color: currentColor('base'), + color: currentColor('foreground'), }, hover: { background: 'transparent', - color: currentColor('base'), + color: currentColor('foreground'), }, activated: { @@ -558,6 +558,227 @@ export const defaultTheme: DefaultTheme = { }, }, }, + + subtle: { + solid: { + strong: { + font: { + weight: 'var(--ion-font-weight-semi-bold)', + }, + }, + + default: { + background: ionColor('primary', 'base', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + + font: { + weight: 'var(--ion-font-weight-normal)', + }, + }, + + focus: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + hover: { + background: ionColor('primary', 'tint', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + activated: { + background: ionColor('primary', 'shade', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + disabled: { + background: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.08), + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + + // Any of the semantic colors like primary, secondary, etc. + semantic: { + default: { + background: currentColor('base', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + focus: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + hover: { + background: currentColor('tint', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + activated: { + background: currentColor('shade', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + disabled: { + background: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.08), + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + }, + }, + + outline: { + strong: { + font: { + weight: 'var(--ion-font-weight-semi-bold)', + }, + }, + + default: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + + border: { + color: ionColor('primary', 'shade', { subtle: true }), + style: 'solid', + width: 'var(--ion-border-width-25)', + }, + + font: { + weight: 'var(--ion-font-weight-normal)', + }, + }, + + focus: { + background: ionColor('primary', 'base', { alpha: 0.1 }), + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + // Fades the whole button instead of changing its color + hover: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + opacity: '0.6', + }, + + activated: { + background: ionColor('primary', 'base', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + }, + + disabled: { + background: 'transparent', + + border: { + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + + semantic: { + default: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + + border: { + color: currentColor('shade', { subtle: true }), + }, + }, + + focus: { + background: currentColor('base', { alpha: 0.1 }), + color: currentColor('foreground', { subtle: true }), + }, + + hover: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + activated: { + background: currentColor('base', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + disabled: { + background: 'transparent', + + border: { + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + }, + }, + + clear: { + strong: { + font: { + weight: 'var(--ion-font-weight-semi-bold)', + }, + }, + + default: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + + // The clear fill uses a regular weight font + font: { + weight: 'normal', + }, + }, + + focus: { + background: ionColor('primary', 'base', { alpha: 0.1 }), + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + // Fades the whole button instead of changing its color + hover: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + opacity: '0.6', + }, + + activated: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + opacity: '0.4', + }, + + disabled: { + background: 'transparent', + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + + semantic: { + default: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + focus: { + background: currentColor('base', { alpha: 0.1 }), + color: currentColor('foreground', { subtle: true }), + }, + + hover: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + activated: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + disabled: { + background: 'transparent', + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.25), + }, + }, + }, + }, }, // Sizes diff --git a/core/src/themes/md/default.tokens.ts b/core/src/themes/md/default.tokens.ts index 40574bfa7ff..9f0503cbe0e 100644 --- a/core/src/themes/md/default.tokens.ts +++ b/core/src/themes/md/default.tokens.ts @@ -450,7 +450,7 @@ export const defaultTheme: DefaultTheme = { default: { background: 'transparent', - color: ionColor('primary', 'base'), + color: ionColor('primary', 'foreground'), border: { color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), @@ -465,18 +465,18 @@ export const defaultTheme: DefaultTheme = { focus: { background: ionColor('primary', 'base', { alpha: 0.12 }), - color: ionColor('primary', 'base'), + color: ionColor('primary', 'foreground'), }, hover: { background: ionColor('primary', 'base', { alpha: 0.04 }), - color: ionColor('primary', 'base'), + color: ionColor('primary', 'foreground'), }, // The ripple is the pressed feedback activated: { background: 'transparent', - color: ionColor('primary', 'base'), + color: ionColor('primary', 'foreground'), }, disabled: { @@ -496,7 +496,7 @@ export const defaultTheme: DefaultTheme = { semantic: { default: { background: 'transparent', - color: currentColor('base'), + color: currentColor('foreground'), border: { color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), @@ -505,17 +505,17 @@ export const defaultTheme: DefaultTheme = { focus: { background: currentColor('base', { alpha: 0.12 }), - color: currentColor('base'), + color: currentColor('foreground'), }, hover: { background: currentColor('base', { alpha: 0.04 }), - color: currentColor('base'), + color: currentColor('foreground'), }, activated: { background: 'transparent', - color: currentColor('base'), + color: currentColor('foreground'), }, disabled: { @@ -609,6 +609,287 @@ export const defaultTheme: DefaultTheme = { }, }, }, + + subtle: { + solid: { + strong: { + font: { + weight: 'var(--ion-font-weight-bold)', + }, + }, + + default: { + background: ionColor('primary', 'base', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + box: { + shadow: + '0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12)', + }, + + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + focus: { + background: overlay( + ionColor('primary', 'base', { subtle: true }), + ionColor('primary', 'contrast', { alpha: 0.24, subtle: true }) + ), + color: ionColor('primary', 'contrast', { subtle: true }), + + box: { + shadow: + '0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12)', + }, + }, + + hover: { + background: overlay( + ionColor('primary', 'base', { subtle: true }), + ionColor('primary', 'contrast', { alpha: 0.08, subtle: true }) + ), + + color: ionColor('primary', 'contrast', { subtle: true }), + + box: { + shadow: + '0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12)', + }, + }, + + // The ripple is the pressed feedback + activated: { + background: ionColor('primary', 'base', { subtle: true }), + color: ionColor('primary', 'contrast', { subtle: true }), + + box: { + shadow: + '0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12)', + }, + }, + + disabled: { + background: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), + + box: { + shadow: 'none', + }, + + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.38), + }, + + rippleEffect: { + opacity: '0.24', + }, + + // Any of the semantic colors like primary, secondary, etc. + semantic: { + default: { + background: currentColor('base', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + focus: { + background: overlay( + currentColor('base', { subtle: true }), + currentColor('contrast', { alpha: 0.24, subtle: true }) + ), + color: currentColor('contrast', { subtle: true }), + }, + + hover: { + background: overlay( + currentColor('base', { subtle: true }), + currentColor('contrast', { alpha: 0.08, subtle: true }) + ), + color: currentColor('contrast', { subtle: true }), + }, + + activated: { + background: currentColor('base', { subtle: true }), + color: currentColor('contrast', { subtle: true }), + }, + + disabled: { + background: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.38), + }, + + rippleEffect: { + opacity: '0.24', + }, + }, + }, + + outline: { + strong: { + font: { + weight: 'var(--ion-font-weight-bold)', + }, + }, + + default: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + + border: { + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), + style: 'solid', + width: 'var(--ion-border-width-xxxs)', + }, + + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + focus: { + background: ionColor('primary', 'base', { alpha: 0.12 }), + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + hover: { + background: ionColor('primary', 'base', { alpha: 0.04 }), + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + // The ripple is the pressed feedback + activated: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + disabled: { + background: 'transparent', + + border: { + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), + }, + + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.38), + }, + + rippleEffect: { + opacity: '0.12', + }, + + semantic: { + default: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + + border: { + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), + }, + }, + + focus: { + background: currentColor('base', { alpha: 0.12 }), + color: currentColor('foreground', { subtle: true }), + }, + + hover: { + background: currentColor('base', { alpha: 0.04 }), + color: currentColor('foreground', { subtle: true }), + }, + + activated: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + disabled: { + background: 'transparent', + + border: { + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.12), + }, + + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.38), + }, + + rippleEffect: { + opacity: '0.12', + }, + }, + }, + + clear: { + strong: { + font: { + weight: 'var(--ion-font-weight-bold)', + }, + }, + + default: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + + font: { + weight: 'var(--ion-font-weight-medium)', + }, + }, + + focus: { + background: ionColor('primary', 'base', { alpha: 0.12 }), + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + hover: { + background: ionColor('primary', 'base', { alpha: 0.04 }), + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + // The ripple is the pressed feedback + activated: { + background: 'transparent', + color: ionColor('primary', 'foreground', { subtle: true }), + }, + + disabled: { + background: 'transparent', + + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.38), + }, + + rippleEffect: { + opacity: '0.12', + }, + + semantic: { + default: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + focus: { + background: currentColor('base', { alpha: 0.12 }), + color: currentColor('foreground', { subtle: true }), + }, + + hover: { + background: currentColor('base', { alpha: 0.04 }), + color: currentColor('foreground', { subtle: true }), + }, + + activated: { + background: 'transparent', + color: currentColor('foreground', { subtle: true }), + }, + + disabled: { + background: 'transparent', + + color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.38), + }, + + rippleEffect: { + opacity: '0.12', + }, + }, + }, + }, }, // Sizes diff --git a/packages/angular/src/lazy/directives/proxies.ts b/packages/angular/src/lazy/directives/proxies.ts index 4d694967756..59ee7c0c203 100644 --- a/packages/angular/src/lazy/directives/proxies.ts +++ b/packages/angular/src/lazy/directives/proxies.ts @@ -385,14 +385,14 @@ export declare interface IonBreadcrumbs extends Components.IonBreadcrumbs { @ProxyCmp({ - inputs: ['buttonType', 'color', 'disabled', 'download', 'expand', 'fill', 'form', 'href', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', 'strong', 'target', 'type'] + inputs: ['buttonType', 'color', 'disabled', 'download', 'expand', 'fill', 'form', 'href', 'hue', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', 'strong', 'target', 'type'] }) @Component({ selector: 'ion-button', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['buttonType', 'color', { name: 'disabled', transform: nullableBooleanAttribute }, 'download', 'expand', 'fill', 'form', 'href', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', { name: 'strong', transform: nullableBooleanAttribute }, 'target', 'type'], + inputs: ['buttonType', 'color', { name: 'disabled', transform: nullableBooleanAttribute }, 'download', 'expand', 'fill', 'form', 'href', 'hue', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', { name: 'strong', transform: nullableBooleanAttribute }, 'target', 'type'], outputs: ['ionFocus', 'ionBlur'], standalone: false }) diff --git a/packages/angular/src/standalone/directives/ion-button.ts b/packages/angular/src/standalone/directives/ion-button.ts index 3e2d0a4c863..02547f53074 100644 --- a/packages/angular/src/standalone/directives/ion-button.ts +++ b/packages/angular/src/standalone/directives/ion-button.ts @@ -11,14 +11,14 @@ import { defineCustomElement as defineIonButton } from '@ionic/core/components/i @ProxyCmp({ defineCustomElementFn: defineIonButton, - inputs: ['buttonType', 'color', 'disabled', 'download', 'expand', 'fill', 'form', 'href', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', 'strong', 'target', 'type'] + inputs: ['buttonType', 'color', 'disabled', 'download', 'expand', 'fill', 'form', 'href', 'hue', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', 'strong', 'target', 'type'] }) @Component({ selector: 'ion-button', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['buttonType', 'color', { name: 'disabled', transform: nullableBooleanAttribute }, 'download', 'expand', 'fill', 'form', 'href', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', { name: 'strong', transform: nullableBooleanAttribute }, 'target', 'type'], + inputs: ['buttonType', 'color', { name: 'disabled', transform: nullableBooleanAttribute }, 'download', 'expand', 'fill', 'form', 'href', 'hue', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'shape', 'size', { name: 'strong', transform: nullableBooleanAttribute }, 'target', 'type'], outputs: ['ionFocus', 'ionBlur'], }) export class IonButton { diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 03ba586da07..0c6ea959bdf 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -182,6 +182,7 @@ export const IonButton: StencilVueComponent = /*@__PURE__*/ defin 'disabled', 'expand', 'fill', + 'hue', 'routerDirection', 'routerAnimation', 'download',