Skip to content

feat(badge): add recipe and tokens#31043

Draft
thetaPC wants to merge 30 commits intoionic-modularfrom
FW-6837
Draft

feat(badge): add recipe and tokens#31043
thetaPC wants to merge 30 commits intoionic-modularfrom
FW-6837

Conversation

@thetaPC
Copy link
Copy Markdown
Contributor

@thetaPC thetaPC commented Mar 27, 2026

Issue number: resolves #


What is the current behavior?

What is the new behavior?

Does this introduce a breaking change?

  • Yes
  • No

Other information

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview, Comment Apr 7, 2026 6:21pm

Request Review

Comment on lines -59 to -61
:host ::slotted(ion-badge[vertical]:not(:empty)) {
@include globals.padding(2px);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this completely because based on MD3, the padding should always be consistent regardless of the host component.

Comment on lines -98 to -117
private getHue(): string | undefined {
const { hue } = this;

if (hue !== undefined) {
return hue;
}

const inAvatar = hostContext('ion-avatar', this.el);
const inButton = hostContext('ion-button', this.el);
const inTabButton = hostContext('ion-tab-button', this.el);
const hasContent = this.el.textContent?.trim() !== '' || this.el.querySelector('ion-icon') !== null;

// Return 'bold' if the badge is inside an avatar, button, tab button,
// or has no content
if (inAvatar || inButton || inTabButton || !hasContent) {
return 'bold';
}
/**
* Gets the badge hue. Uses the `hue` property if set, otherwise
* checks the theme config and falls back to 'subtle' if neither is provided.
*/
get hueValue(): string {
const hueConfig = config.getObjectValue('IonBadge.hue', 'bold') as string;
const hue = this.hue || hueConfig;

// Return 'subtle' if the badge contains visible text or an icon
return 'subtle';
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opted out from implementing this complex version of hue getter. It was originally meant for the ionic theme and this might not be the desired outcome for every developer. Instead, OS will should create a wrapper to determine the hue since we want Ionic Modular to be as flexible as possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several edge cases that are specific to OS. In order to lessen the complexity of badge, the following "breaking changes" were done:

  1. Border radius is not automatically changed if the badge is small and soft (:host(.badge-small.badge-soft)). Border radius should not be tied to sizes as it can be confusing of when a dev needs to know the criteria to update their tokens. Since OS wants the border radius to be even smaller than soft then I created a new shape, crisp for OS to use:
<ion-button fill="clear">
  <ion-icon slot="icon-only" name="add"></ion-icon>
  <ion-badge shape="crisp" vertical="top"></ion-badge>
</ion-button>
  1. The styles for :host([vertical]:not(:empty)) were not implemented because they are the same exact styles as size medium. OS can simply update their badges to have size="medium"

  2. The styles for :host(:not(:empty).in-button) were not implemented to reduce the complexity of combinations as mentioned in number 1. OS can adjust their code by adding a small badge within the button as the styles for small as the same exact ones.

<ion-button fill="clear">
  <ion-icon slot="icon-only" name="add"></ion-icon>
  <ion-badge size="small" vertical="top"></ion-badge>
</ion-button>

* Defaults to `"soft"` if both the shape property and theme config are unset.
*/
@Prop() shape?: 'soft' | 'round | rectangular';
@Prop() shape?: 'crisp' | 'soft' | 'round' | 'rectangular';
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Border radius is not automatically changed if the badge is small and soft (:host(.badge-small.badge-soft)). Border radius should not be tied to sizes as it can be confusing of when a dev needs to know the criteria to update their tokens. Since OS wants the border radius to be even smaller than soft then I created a new shape, crisp for OS to use:

<ion-button fill="clear">
  <ion-icon slot="icon-only" name="add"></ion-icon>
  <ion-badge shape="crisp" vertical="top"></ion-badge>
</ion-button>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed the folder from hint to vertical.

<ion-content>
<div id="avatar">
<h2>
Inside Avatar (Navigate to the <a href="../../avatar/test/badge/index.html">Avatar Badges page</a> to see
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we need to test each badge size, badge position, and badge content on each avatar size, I added the link to view all those possibilities so we don't have a large page. This section is only a small snippet for quick testing purposes.

<div id="tab-button">
<h2>
Inside Tab Button (Navigate to the
<a href="../../tab-button/test/badge/index.html">Tab Button Badges page</a> to see all badge variations.)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we need to test each badge size, badge position, and badge content on each icon layout, icon presence, label presence, I added the link to view all those possibilities so we don't have a large page. This section is only a small snippet for quick testing purposes.


<div id="button-top">
<h2>
Inside Button (Navigate to the <a href="../../button/test/badge/index.html">Button Badges page</a> to see
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we need to test each badge size, badge position, and badge content on each button size, icon only or text, I added the link to view all those possibilities so we don't have a large page. This section is only a small snippet for quick testing purposes.


// Anchored Badge
:host(.button-has-badge) .button-native {
--overflow: visible;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't migrate this as the structure might change once button is fully migrated. This will be done when button is worked on.

[`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,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class was only used to position.

Comment on lines -134 to -135
'in-button': hostContext('ion-button', this.el),
'in-tab-button': hostContext('ion-tab-button', this.el),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't needed anymore since position is being generated with the positionBadge utility.

}

if (this.hasBadge) {
this.setupBadgeObserver();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also be adding an observer for the icon only slot? In case the icon changes, we might want to recalculate but that might be overkill?

Comment on lines +250 to +254
height: 'var(--ion-scaling-xxxs)',

min: {
width: 'var(--ion-scaling-xxxs)',
},
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on MD3, it has two sizes: small and large. The small is meant for the empty badge and the large if for the content badge. Their large is 16x16 pixels with a font size of 11px. I implemented the same values for our large size for md to be consistent. This means that the values for small and medium are scaled down. Please make sure to review the sizes carefully, as they might be too small. Maybe MD3 large should really be small for ours. Thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: core @ionic/core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant