🔤 Label in name
Common mistake
A common mistake is setting an accessible name that replaces the visible label entirely. For example, a button that reads “Buy now” on screen is given a
semanticLabelof"Purchase item". Speech-input users who say “Buy now” to activate the button will be ignored, because the programmatic name no longer contains the visible text.
🎯 Relevant elements
This guideline applies to any interactive element that has a visible text label:
- Buttons (
ElevatedButton,TextButton,OutlinedButton)- Navigation items with visible text
- Links and tappable text
- Form fields with visible labels
- Menu items
WCAG Guideline
This guideline is based on WCAG 2.2 — 2.5.3 Label in Name (Level A). When an interactive element has a visible text label, the accessible name must contain that visible text. This ensures that speech-input users who say the visible label can activate the control.
Solution
When adding or overriding a semantic label for an element that already has visible text, make sure the accessible name contains the visible text. It may include additional context, but must not omit or replace the visible label.
// ❌ Don't - accessible name replaces the visible label
ElevatedButton(
onPressed: () => submitOrder(),
child: Text('Buy now'),
);
// with:
Semantics(
label: 'Purchase item',
child: ElevatedButton(
onPressed: () => submitOrder(),
child: Text('Buy now'),
),
);
// ✅ Do - accessible name contains the visible text
Semantics(
label: 'Buy now – 3 items in cart',
child: ElevatedButton(
onPressed: () => submitOrder(),
child: Text('Buy now'),
),
);
// ✅ Do - no override needed; Flutter uses the Text widget's value automatically
ElevatedButton(
onPressed: () => submitOrder(),
child: Text('Buy now'),
);
Note: In most cases you don’t need to set a
semanticLabelat all when aTextwidget is present — Flutter derives the accessible name from it automatically. Only add a label when you need to provide extra context, and ensure the visible text is preserved in it.
Validation & Testing
See the Validation & Testing setup guide for tool configuration.
Manual screen reader testing
Enable TalkBack (Android) or VoiceOver (iOS) and navigate to each interactive element. Verify that the announced name matches or contains the visible text on screen.
flutter_test
testWidgets('button accessible name contains visible label', (tester) async {
await tester.pumpWidget(MyWidget());
final semantics = tester.getSemantics(find.text('Buy now'));
expect(semantics.label, contains('Buy now'));
});
🛠️ Usage baseflow-a11y-components library
This is enforced on the following components:
A11yButton