Skip to content

fix(tab-button): match height to tab-bar in ionic-md theme#31209

Open
OS-jacobbell wants to merge 12 commits into
nextfrom
FW-7566
Open

fix(tab-button): match height to tab-bar in ionic-md theme#31209
OS-jacobbell wants to merge 12 commits into
nextfrom
FW-7566

Conversation

@OS-jacobbell

@OS-jacobbell OS-jacobbell commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

What is the current behavior?

Tab buttons in the ionic-md theme are slightly shorter than the tab bar. This is inconsistent when dev tools are in use in Firefox, causing screenshot tests to fail.

What is the new behavior?

  • Tab buttons completely fill available vertical space by adding a margin above the button's icon.

Does this introduce a breaking change?

  • Yes
  • No

Other information

The tab bar content height is 48px while the buttons are 44px high with the styles specified. Usually the buttons render 44px high, but sometimes when the button is focused with Firefox dev tools, they render 48px high. When the buttons are made to be 48px high, matching the container, we don't see this issue.

@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview, Comment Jun 17, 2026 9:07pm

Request Review

@OS-jacobbell OS-jacobbell changed the title fix(tab-button): match height to tab-bar in ionic-md style fix(tab-button): match height to tab-bar in ionic-md theme Jun 12, 2026

@thetaPC thetaPC left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey! Great catch on tracking it down to line-height. That's exactly right, and the diagnostic was spot on. Wanted to share why it works and propose a small adjustment.

Why your fix works
The typography mixin applied to :host sets line-height: 20px (from body-action-xs). Because line-height inherits, that 20px cascades into the slotted <ion-icon>. The icon is 24px tall, so it's bigger than the inherited line.

Firefox handles "inline-block taller than its line-height" by doing baseline math that lands on sub-pixel values, and those values can shift by tiny fractions between paint passes. Same layout (the boundingBox() was identical on flaky and passing runs), but anti-aliasing on the icon edges and focus ring renders slightly differently each time. Enough to fail the snapshot.

By dropping line-height from the host, the icon falls back to whatever's inherited from the ancestor chain (currently body), and the mismatch goes away.

Why it only surfaces in tab-button
Three conditions have to line up, and tab-button is the only place all three hold today:

The host applies a typography token whose line-height (20px) is smaller than the pinned icon size (24px). Most components inherit body-md-regular (24px), which matches.
There's a thin visual detail nearby (the 2px focus outline on the rounded corners) where even a fractional pixel shift becomes visible in a screenshot diff.
The snapshot test captures that exact state (the focused tab is in the test).
Other components with ::slotted(ion-icon) have condition 1 latent, but they're missing 2 or 3, so the bug is silent there.

Why it's actually a coincidence
body in typography.ionic.scss:17 applies body-md-regular, whose line-height resolves to 24px. That happens to match the icon's size exactly. 24 == 24 means no mismatch, no baseline math, no flake.

The moment someone changes the body token (or any wrapping component sets a smaller line-height), the icon inherits something less than 24px again and the flake comes back silently. We'd have no signal it regressed until snapshots start failing on Firefox.

Suggested adjustment
Revert the host-side change and instead pin line-height directly on the slotted icon, where the size is already pinned:

 ::slotted(ion-icon) {
   width: globals.$ion-scale-600;
   height: globals.$ion-scale-600;
+  line-height: normal;
 }

Now the icon's line-height is locally controlled and immune to whatever any ancestor decides. The fix becomes invariant rather than coincidence dependent.

Worth noting this same pattern applies anywhere we pin an icon size via ::slotted(ion-icon). There are roughly 20 components doing it. Probably worth targeting ion-icon at a global level (e.g. in typography.ionic.scss) so every wrapping component is protected at once and future migrations don't have to remember this. The tab-button fix here is still the immediate one, with the global change as a follow up.

One meta thing
While you're updating the PR, could you include the root cause in the description? Something like the first two sections above. What the actual mechanism is (inherited line-height plus inline-block baseline math in Firefox), not just what changed. It helps reviewers and future readers tell whether a change addresses the underlying bug or only makes the symptom go away. In this case the line-height observation was the real insight. Capturing it in the PR means the next person hitting flakiness on a similar test has a starting point instead of re-deriving it from scratch.

@OS-jacobbell OS-jacobbell marked this pull request as ready for review June 17, 2026 21:32
@OS-jacobbell OS-jacobbell requested a review from a team as a code owner June 17, 2026 21:32
@OS-jacobbell OS-jacobbell requested a review from gnbm June 17, 2026 21:32
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.

3 participants