fix(button): update styling for badge (#30414)

Issue number: internal
---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
Some stylings for the ion-badge when slotted in an ion-button were
missing.

## What is the new behavior?
Updated the styling for the ion-badge when slotted in an ion-button
according to the designs:
- Adjusted badge padding;
- Abandoned the `:has` pseudo-selector for a simple 'button-had-badge'
class;
- Adjusted font-size for icons inside the button;
- Adjusted badge positioning according to button size and badge length.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
This commit is contained in:
Pedro Lourenço
2025-05-29 16:49:55 +01:00
committed by GitHub
parent 8b5215ff03
commit a0d5ad7b8d
49 changed files with 118 additions and 25 deletions

View File

@ -160,17 +160,12 @@
// Badge in Button
// --------------------------------------------------
:host([vertical]:not(.in-tab-button).in-button.badge-small) {
@include globals.position(null, calc(-1 * var(globals.$ion-space-050)));
}
:host([vertical]:not(.in-tab-button).in-button.badge-medium),
:host([vertical]:not(.in-tab-button).in-button.badge-large) {
@include globals.position(null, globals.$ion-space-050);
}
:host(:not(:empty).in-button) {
--padding-start: #{globals.$ion-scale-050};
--padding-end: #{globals.$ion-scale-050};
@include globals.typography(globals.$ion-body-action-xs);
min-width: globals.$ion-scale-400;
height: globals.$ion-scale-400;

View File

@ -80,7 +80,7 @@ export class Badge implements ComponentInterface {
const theme = getIonTheme(this);
const { size } = this;
// TODO(ROU-10747): Remove theme check when sizes are defined for all themes.
// TODO(FW-6355): Remove theme check when sizes are defined for all themes.
if (theme !== 'ionic') {
return undefined;
}
@ -133,6 +133,7 @@ export class Badge implements ComponentInterface {
[`badge-vertical-${this.vertical}`]: this.vertical !== undefined,
'in-button': hostContext('ion-button', this.el),
'in-tab-button': hostContext('ion-tab-button', this.el),
'long-badge': (this.el.textContent?.trim().length ?? 0) > 2,
})}
>
<slot></slot>

View File

@ -56,5 +56,13 @@ configs({ directions: ['ltr'], modes: ['md', 'ios', 'ionic-md'] }).forEach(({ co
await expect(container).toHaveScreenshot(screenshot(`badge-hint-button-bottom`));
});
test('should not have visual regressions for different button sizes', async ({ page }) => {
await page.goto('/src/components/badge/test/hint', config);
const container = page.locator('#button-size');
await expect(container).toHaveScreenshot(screenshot(`badge-hint-button-size`));
});
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -417,6 +417,47 @@
</ion-button>
</div>
</ion-list>
<ion-list id="button-size">
<ion-list-header>
<ion-label> Inside Button - Button Size </ion-label>
</ion-list-header>
<div class="ion-display-flex ion-align-items-center ion-justify-content-around ion-margin">
<ion-button fill="clear">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="top">1</ion-badge>
</ion-button>
<ion-button fill="clear" size="small">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="top">1</ion-badge>
</ion-button>
<ion-button fill="clear" size="medium">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="top">1</ion-badge>
</ion-button>
<ion-button fill="clear" size="large">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="top">1</ion-badge>
</ion-button>
</div>
<div class="ion-display-flex ion-align-items-center ion-justify-content-around ion-margin">
<ion-button fill="clear">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="bottom">1</ion-badge>
</ion-button>
<ion-button fill="clear" size="small">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="bottom">1</ion-badge>
</ion-button>
<ion-button fill="clear" size="medium">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="bottom">1</ion-badge>
</ion-button>
<ion-button fill="clear" size="large">
<ion-icon slot="icon-only" name="add"></ion-icon>
<ion-badge color="danger" size="small" vertical="bottom">1</ion-badge>
</ion-button>
</div>
</ion-list>
</ion-content>
</ion-app>
</body>

View File

@ -304,13 +304,7 @@ ion-ripple-effect {
@include border-radius(inherit);
}
// This rule works for Chrome.
:has(ion-badge) .button-native {
--overflow: visible;
}
// This rule works for the rest of the browsers.
:host(:has(ion-badge)) .button-native {
:host(.button-has-badge) .button-native {
--overflow: visible;
}

View File

@ -131,15 +131,14 @@
::slotted(ion-icon[slot="start"]),
::slotted(ion-icon[slot="end"]),
::slotted(ion-icon[slot="icon-only"]) {
font-size: globals.$ion-font-size-400;
font-size: globals.$ion-font-size-500;
}
:host(.button-small),
:host(.button-large) {
::slotted(ion-icon[slot="start"]),
::slotted(ion-icon[slot="end"]),
::slotted(ion-icon[slot="icon-only"]) {
font-size: inherit;
font-size: globals.$ion-font-size-600;
}
}
@ -264,8 +263,46 @@
@include globals.margin-horizontal(globals.$ion-space-300, null);
}
// Button Badge
// Button with Badge
// --------------------------------------------------
:host ::slotted(ion-badge[vertical]:not(:empty)) {
@include globals.padding(globals.$ion-space-050);
:host(.button-has-badge) {
--padding-top: #{globals.$ion-space-0};
--padding-bottom: #{globals.$ion-space-0};
}
:host(.button-small) ::slotted(ion-badge) {
@include globals.position(null, calc(-1 * globals.$ion-space-050), null, null);
}
:host(.button-medium) {
::slotted(ion-badge.long-badge.badge-vertical-top) {
@include globals.position($top: globals.$ion-space-100);
}
::slotted(ion-badge.long-badge.badge-vertical-bottom) {
@include globals.position($bottom: globals.$ion-space-100);
}
::slotted(ion-badge:not(.long-badge).badge-vertical-top) {
@include globals.position(globals.$ion-space-100, globals.$ion-space-150, null, null);
}
::slotted(ion-badge:not(.long-badge).badge-vertical-bottom) {
@include globals.position(null, globals.$ion-space-150, globals.$ion-space-100, null);
}
}
:host(.button-large) {
::slotted(ion-badge.long-badge.badge-vertical-top) {
@include globals.position($top: globals.$ion-space-200);
}
::slotted(ion-badge.long-badge.badge-vertical-bottom) {
@include globals.position($bottom: globals.$ion-space-200);
}
::slotted(ion-badge:not(.long-badge).badge-vertical-top) {
@include globals.position(globals.$ion-space-200, globals.$ion-space-200, null, null);
}
::slotted(ion-badge:not(.long-badge).badge-vertical-bottom) {
@include globals.position(null, globals.$ion-space-200, globals.$ion-space-200, null);
}
}

View File

@ -206,6 +206,10 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
return !!this.el.querySelector('[slot="icon-only"]');
}
private get hasBadge() {
return !!this.el.querySelector('ion-badge');
}
private get rippleType() {
const hasClearFill = this.fill === undefined || this.fill === 'clear';
@ -345,8 +349,20 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
};
render() {
const { buttonType, type, disabled, rel, target, href, color, expand, hasIconOnly, strong, inheritedAttributes } =
this;
const {
buttonType,
type,
disabled,
rel,
target,
href,
color,
expand,
hasIconOnly,
hasBadge,
strong,
inheritedAttributes,
} = this;
const theme = getIonTheme(this);
const mode = getIonMode(this);
@ -398,6 +414,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
'in-toolbar-color': hostContext('ion-toolbar[color]', this.el),
'in-buttons': hostContext('ion-buttons', this.el),
'button-has-icon-only': hasIconOnly,
'button-has-badge': hasBadge,
'button-disabled': disabled,
'ion-activatable': true,
'ion-focusable': true,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB