Skip to content

fix(button): excludeSemantics so semanticLabel replaces child text (a11y)#50

Merged
SiphoChris merged 1 commit into
mainfrom
fix/button-semantics-exclude
Jun 15, 2026
Merged

fix(button): excludeSemantics so semanticLabel replaces child text (a11y)#50
SiphoChris merged 1 commit into
mainfrom
fix/button-semantics-exclude

Conversation

@SiphoChris

Copy link
Copy Markdown
Owner

Button: semanticLabel now truly replaces the child's semantics

Button wrapped its content in Semantics(button: true, enabled:, label: semanticLabel, child: …) without excludeSemantics: true. Its doc-comment promises semanticLabel "replaces the child's semantics (like aria-label)" — but verified empirically (semantics-tree inspection) that with a text child the child's text merges with the label, so a screen reader announces both ("override" and the button's own text). This surfaced while shipping Badge (PR #49), which had the identical pattern and was fixed there.

Verify-first (the spawned task asked for it)

A new RED test Button(semanticLabel: 'override', child: Text('VISIBLE_CHILD')) confirmed the leak: find.bySemanticsLabel('override') found 0 nodes because the node's label was the merged "override\nVISIBLE_CHILD".

The fix is conditional (not Badge's unconditional one)

Unlike Badge — which only wraps in Semantics when semanticLabel != nullButton always wraps (it needs the button:/enabled: flags). With no semanticLabel, the child text must remain the button's accessible name. So:

excludeSemantics: widget.semanticLabel != null,
  • With semanticLabel → exclude the child so the label is the sole name (replaces). ✓
  • Without → keep the child text as the name (the common case — getSemantics(find.text('Save')) still reports isButton). ✓

Tests (TDD)

  • semanticLabel replaces a TEXT child label (no double-announce) — was RED, now GREEN.
  • without semanticLabel, the child text IS the accessible name — guards the no-override path (passed before and after; locks the conditional).
  • All 12 button behavior tests pass; flutter analyze --fatal-infos --fatal-warnings clean; dart format clean.

This makes the existing doc-comment's "replaces" guarantee true in all cases (it previously held only for icon-only buttons, where there's no child text to leak). Spawned from the Badge PR as a focused a11y-correctness follow-up.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
flutterbits Ready Ready Preview, Comment Jun 15, 2026 11:44am

@SiphoChris SiphoChris merged commit 1a1a3a3 into main Jun 15, 2026
8 checks passed
@SiphoChris SiphoChris deleted the fix/button-semantics-exclude branch June 15, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant