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) => `
+