🏃 Interactive elements

Common mistake

A common mistake with interactive elements is using a label that describes what the element is rather than what it does. For example, labelling a delete button as "trash icon" instead of "Delete item". Screen reader users rely entirely on these labels to understand what will happen when they activate an element. A vague or missing label forces them to guess, which can lead to accidental and irreversible actions.

🎯 Relevant elements

This guideline applies to any element the user can interact with that lacks a visible text label:

  • Icon buttons (IconButton)
  • Floating action buttons (FloatingActionButton)
  • Custom gesture detectors (GestureDetector, InkWell)
  • Toggle buttons and checkboxes with no adjacent label
  • Navigation bar items using only icons

WCAG Guideline

This guideline is based on WCAG 2.2 — 1.1.1 Non-text Content (Level A) and WCAG 2.2 — 4.1.2 Name, Role, Value (Level A). For every interactive element, the name and role must be programmatically determinable, and any element without visible text must have a text alternative that describes the action it triggers.


Solution

Use Flutter’s Semantics wrapper with button: true for clickable elements. Make sure the label describes what the element does, not what it is.

// ❌ Don't - describes the icon, not the action
IconButton(
  icon: Icon(Icons.delete),
  onPressed: () => deleteItem(),
);

// ✅ Do - label describes the triggered action
Semantics(
  label: 'Delete item',
  button: true,
  child: IconButton(
    icon: Icon(Icons.delete),
    onPressed: () => deleteItem(),
  ),
);

// Some widgets support semanticLabel directly
IconButton(
  icon: Icon(Icons.delete),
  tooltip: 'Delete item', // also sets the semantic label
  onPressed: () => deleteItem(),
);
👎 Don’t 👍 Do

Validation & Testing

Verify this guideline using one or more of the methods below. See the Validation & Testing setup guide for tool configuration.

SemanticsDebugger

Confirm that each interactive element shows an action-oriented label.

accessibility_tools

Activate screen reader mode and confirm each interactive element shows an action-oriented label.

TalkBack & VoiceOver

Navigate through all interactive elements. Verify that each element announces a label that clearly describes the action it triggers — not just "button" or an icon name.

flutter_test

Use flutter_test to assert that interactive elements have the correct semantic label and role.

testWidgets('delete button has descriptive semantic label', (tester) async {
  await tester.pumpWidget(MyWidget());

  expect(
    tester.getSemantics(find.byType(IconButton)),
    matchesSemantics(
      label: 'Delete item',
      isButton: true,
    ),
  );
});

🛠️ Usage baseflow-a11y-components library

This is enforced on the following components:

  • A11yButton
  • A11yIcon
  • A11yCard

This site uses Just the Docs, a documentation theme for Jekyll.