Skip to content

feat(button): show only the icon and vertical badges in icon-only buttons - #31648

Open
thetaPC wants to merge 28 commits into
FW-6838-badge-spacingfrom
FW-6838-icon-only
Open

thetaPC wants to merge 28 commits into
FW-6838-badge-spacingfrom
FW-6838-icon-only

Conversation

@thetaPC

@thetaPC thetaPC commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Issue number: resolves internal


What is the current behavior?

An icon-only button renders anything else placed in it: text, icons in the start and end slots, and badges without a vertical position. That content breaks the square shape, as raised in the review of #31043.

What is the new behavior?

  • A button with an icon in the icon-only slot shows only that icon and any badge with a vertical position
  • Elements in the start and end slots aren't rendered. Text and other content without a slot are visually hidden, and the button keeps its square size
  • Hidden content still names the button for assistive technology, including text wrapped in an element like <span> or <ion-label>
  • Text buttons don't change

Does this introduce a breaking change?

  • Yes
  • No

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. Text and elements without a slot are only visually hidden, so they still name the button for assistive technology. To show other content, remove the icon-only slot and use the start or end slot for the icon.

Other information

Check Before After What to look for
Badges in icon-only buttons Before After In the "badge position: none" sections, icon-only buttons no longer show the badge. In the top and bottom sections, the badges still show

@thetaPC
thetaPC added this pull request to stack #31629 October 23, 2026 23:14
@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
ionic-framework Ready Ready Preview Nov 5, 2026 4:14am UTC

Request Review

@thetaPC
thetaPC marked this pull request as ready for review October 29, 2026 16:02
@thetaPC
thetaPC requested a review from a team as a code owner October 29, 2026 16:03
@thetaPC
thetaPC requested a review from ShaneK October 29, 2026 16:03

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good overall, the icon-only sizes hold up across every size and theme! Just a couple of things, mainly wrapped text no longer naming the button for screen readers.

// An icon-only button shows only its icon and badges with a `vertical` position
:host(.button-has-icon-only) ::slotted([slot="start"]),
:host(.button-has-icon-only) ::slotted([slot="end"]),
:host(.button-has-icon-only) ::slotted(:not([slot]):not(ion-badge[vertical])) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This one worries me a bit, because text wrapped in an element loses its accessible name now. With <span>Add item</span> or <ion-label>Add item</ion-label> next to the icon, the button has an empty name, where before it was "Add item", and only bare text still works. A visually hidden span is a pretty common way to label an icon button, so could the default-slot elements be visually hidden instead of display: none? If not, BREAKING.md should say only bare text names the button, since right now it says hidden text still does. A test with wrapped text would catch this either way.

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.

Good catch. In a9d788d, content without a slot is visually hidden instead of display: none, so <span> and <ion-label> name the button again. BREAKING.md and the icon-only slot docs now say it's visually hidden. The test covers bare text, <span> and <ion-label>, and fails on the old code.

One side effect: an inline badge now adds to the name, the same way a vertical badge already did.

Comment thread core/src/components/button/button.scss Outdated
}

// An icon-only button shows only its icon and badges with a `vertical` position
:host(.button-has-icon-only) ::slotted([slot="start"]),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Outside CSS that sets display beats these ::slotted() rules, so the start and end content can come back. In the ionic theme the global link style does it, so <a slot="start" href="#x">Link</a> in an icon-only button still renders, gets its own tab stop, and becomes the button's name. Could we skip rendering the start and end slots when hasIconOnly, or make these !important? An <a slot="start"> case in the tests would cover it.

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.

Went with skipping the slots in 22a4ad5. An icon-only button doesn't render its start and end slots, so their content isn't drawn, focused or used in the name, whatever outside CSS sets. The icon-only tests now run in the ionic theme with an <a slot="start">, and fail on the old code.

This branch was successfully deployed

1 active deployment
Preview — b68068be Deployed Nov 5, 2026 by vercel[bot]
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.

2 participants