← Back to capabilities

Accessibility Component

WCAG 2.2 compliance at the component level — semantic labels, touch targets, button roles, and alt text for base UI widgets.

HelpButton

Appears next to form field labels — for example the Amount field on invoice forms — to open a bottom sheet explaining how the field should be filled in. Accepts either a plain-text message or a custom widget as the help content. Renders nothing if neither is provided, so no phantom button appears in the semantic tree.

HelpButton has a semantic label for screen readers

  1. Given the HelpButton is rendered with title "Amount" and a message
  2. Then it has a semantic label containing "Help"
  3. And it is marked as a button in the semantic tree

HelpButton meets minimum 44×44 touch target

  1. Given the HelpButton is rendered with title "Amount" and a message
  2. Then its tappable area is at least 44 by 44 logical pixels

HelpButton with widget content is accessible

  1. Given a HelpButton is rendered with title "Invoice Number" and a widget content body
  2. Then it has a semantic label containing "Help"
  3. And it is marked as a button in the semantic tree

HelpButton without a message or content is not rendered

  1. Given a HelpButton is rendered without message or content
  2. Then no help button is rendered in the semantic tree

PilButton

The primary call-to-action button used throughout the app — for example "Save invoice" and "Confirm activity". Renders a full-width CupertinoButton styled with the brand accent colour and enforces a minimum 44 dp touch target via the Cupertino framework.

PilButton announces its label to screen readers

  1. Given a PilButton is rendered with label "Confirm"
  2. Then the label "Confirm" is present in the semantic tree
  3. And the button role is present in the semantic tree

PilButton meets minimum 44×44 touch target

  1. Given a PilButton is rendered with label "Confirm"
  2. Then its tappable area is at least 44 by 44 logical pixels

SemanticIconButton

An icon-only button with an explicit semantic label, used for toolbar actions such as sharing invoices and navigating dates. Enforces a minimum 44×44 dp touch target and haptic feedback on press. Carries an enabled / disabled state that is communicated to assistive technologies.

SemanticIconButton announces its label to screen readers

  1. Given a SemanticIconButton is rendered with label "Share invoice"
  2. Then it has a semantic label "Share invoice"
  3. And it is marked as a button in the semantic tree

SemanticIconButton meets minimum 44×44 touch target

  1. Given a SemanticIconButton is rendered with label "Share invoice"
  2. Then its tappable area is at least 44 by 44 logical pixels

Disabled SemanticIconButton is announced as unavailable

  1. Given a SemanticIconButton is rendered with label "Share invoice" and is disabled
  2. Then it has a semantic label "Share invoice"
  3. And it is marked as a button in the semantic tree
  4. And it is marked as disabled

CircularIconButton

A circular icon button used in list section headers — for example "Add activity" on the Play tab. Branded with a coloured circular background and requires a semantic label that is announced by screen readers in place of the icon glyph.

CircularIconButton announces its label to screen readers

  1. Given a CircularIconButton is rendered with label "Add activity"
  2. Then it has a semantic label "Add activity"
  3. And it is marked as a button in the semantic tree

IconButton

A compact icon + optional text label button used for secondary actions — for example Retry on data-load error screens and action bar shortcuts. Excludes the icon glyph from the semantic tree when a text label is present so AT announces only the label. In no-style mode the icon is always excluded.

IconButton with label announces its label to screen readers

  1. Given an IconButton is rendered with label "Add item"
  2. Then the label "Add item" is visible in the semantic tree
  3. And it is marked as a button in the semantic tree

IconButton excludes the icon from semantics when a label is shown

  1. Given an IconButton is rendered with label "Add item"
  2. Then the icon is excluded from the semantic tree

IconButton in no-style mode excludes its icon from semantics

  1. Given an IconButton is rendered in no-style mode with no label
  2. Then the icon is excluded from the semantic tree

ASwitch

A toggle switch used in settings screens and filter forms to represent a binary on / off choice. Built on CupertinoSwitch, which natively exposes the toggle role and current on / off value to assistive technologies.

ASwitch announces toggle role and reflects off state

  1. Given an ASwitch is rendered with initial value off
  2. Then the switch has a toggled state
  3. And the switch is not toggled

ASwitch reflects on state when initialised as on

  1. Given an ASwitch is rendered with initial value on
  2. Then the switch is toggled

SegmentedSelector

A two-option segmented control used to switch between views — for example Activities / Invoices on the Play tab and Income / Expenses on the Pay tab. Each segment carries a button role, its label, and the correct selected state. Tapping a segment fires a callback so the parent widget can update its state.

SegmentedSelector active segment is marked as selected

  1. Given a SegmentedSelector is rendered with options "Activities" and "Invoices"
  2. Then the "Activities" segment is marked as a button and as selected

SegmentedSelector inactive segment is not marked selected

  1. Given a SegmentedSelector is rendered with options "Activities" and "Invoices"
  2. Then the "Invoices" segment is marked as a button and is not selected

All SegmentedSelector segments are marked as buttons in the semantic tree

  1. Given a SegmentedSelector is rendered with options "Overview" and "Details" and "Overview" selected
  2. Then all segments are marked as buttons in the semantic tree

Tapping a SegmentedSelector segment fires the selection callback

  1. Given a SegmentedSelector is rendered with options "Income" and "Expenses" and "Income" selected
  2. When the user taps the "Expenses" segment
  3. Then the onChanged callback is called with index 1

AmountInput

A currency input with ± adjustment chips used on invoice and expense forms for entering monetary amounts. Each chip is a CupertinoButton subject to the WCAG 2.5.8 minimum 24×24 dp touch target requirement for AA conformance.

AmountInput chips meet 24×24 minimum touch target

  1. Given an AmountInput is rendered
  2. Then each adjustment chip has a minimum size of 24 by 24 logical pixels

DateChip

A compact date chip with previous / next day arrow buttons used on the activity scheduling screen to step through dates. Each navigation button carries a descriptive label and button role so screen readers can announce and activate the control.

DateChip previous-day button announces its purpose to screen readers

  1. Given a DateChip is rendered for a date
  2. Then it has a semantic label "Previous day"
  3. And it is marked as a button in the semantic tree

DateChip next-day button announces its purpose to screen readers

  1. Given a DateChip is rendered for a date
  2. Then it has a semantic label "Next day"
  3. And it is marked as a button in the semantic tree

DateChip navigation buttons meet minimum 44×44 touch target

  1. Given a DateChip is rendered for a date
  2. Then each navigation button has a tappable area of at least 44 by 44 logical pixels

DatePicker

A date picker widget used on invoice and expense forms. Renders a calendar icon button labelled with the picker title and a tappable date label that opens a native date picker sheet. Both controls expose their name, role, and purpose to screen readers.

DatePicker calendar button has an accessible label

  1. Given a DatePicker is rendered with picker label "Select date"
  2. Then the calendar button has semantic label "Select date"
  3. And it is marked as a button in the semantic tree

DatePicker date display has an accessible name

  1. Given a DatePicker is rendered with initial date 2025-01-15
  2. Then the date display has a semantic label containing the date

DatePicker date display provides a hint for screen reader users

  1. Given a DatePicker is rendered with initial date 2025-01-15
  2. Then the date display has the hint "double-tap to change date"

FeatureCard

The large gradient cards on the Home screen that navigate to the Play, Pay, and Expenses tabs. Optionally shows a badge count for unread items — this count is appended to the accessible label so screen readers announce the full context.

FeatureCard is reachable as a button with its title as the label

  1. Given a FeatureCard is rendered with title "Play"
  2. Then it has a semantic label "Play"
  3. And it is marked as a button in the semantic tree

FeatureCard badge count is announced alongside the card title

  1. Given a FeatureCard is rendered with title "Pay" and a badge count of 3
  2. Then its semantic label contains "Pay" and "3 new"

AnimatedListTile

The animated row tile used across activity and invoice lists. Slides in on first render and wraps its AListItem configuration in a Semantics node, exposing the semantic label and optional hint so screen readers can announce each row's content and purpose.

AnimatedListTile exposes semantic label and hint when provided

  1. Given an AnimatedListTile is rendered with semanticLabel "Invoice 001, £100" and semanticHint "Opens invoice details"
  2. Then the tile has semantic label "Invoice 001, £100"
  3. And the tile has semantic hint "Opens invoice details"
  4. And it is marked as a button in the semantic tree

SlidableTile

A list tile with a swipe-to-delete action used on activity and expense list items. The delete action label is declared in the Slidable configuration so screen readers can discover and activate it without requiring a physical swipe gesture.

SlidableTile exposes the delete action label for screen readers

  1. Given a SlidableTile is rendered with a tile
  2. Then the delete action label "Delete" is present

Loading

A CupertinoActivityIndicator spinner used as the default loading indicator across the app. Wrapped in a live-region Semantics node so assistive technologies announce "Loading" automatically without the user needing to move focus to the spinner.

Loading widget is announced as a live region

  1. Given the Loading widget is rendered
  2. Then it has a semantic label "Loading"
  3. And it is marked as a live region

Loader

A data-loading orchestrator that conditionally renders Loading while fetching, an error state with a Retry action on failure, or the loaded content on success. Each state is accessible: loading uses a live region; error exposes a labeled Retry.

Loader in the loading state shows a live-region announcement

  1. Given a Loader is rendered in the loading state
  2. Then a live-region label "Loading" is present

Loader in the error state exposes a Retry button with a label

  1. Given a Loader is rendered in the error state
  2. Then the label "Retry" is present in the semantic tree
  3. And a button role is present in the semantic tree

TopTab

A two-tab switcher used within screens to toggle between two content panes — for example Notes / Info on the activity detail screen. Each tab carries a button role, its label, and the correct selected state so screen readers announce which tab is currently active.

TopTab first tab has a button role and its label

  1. Given a TopTab is rendered with tabs "Notes" and "Info"
  2. Then the "Notes" tab has a semantic label and is marked as a button

TopTab second tab has a button role and its label

  1. Given a TopTab is rendered with tabs "Notes" and "Info"
  2. Then the "Info" tab has a semantic label and is marked as a button

Active TopTab tab is announced as selected

  1. Given a TopTab is rendered with "Notes" as the active tab
  2. Then the "Notes" tab is marked as selected

Inactive TopTab tab is not announced as selected

  1. Given a TopTab is rendered with "Notes" as the active tab
  2. Then the "Info" tab is not marked as selected

CupertinoTabBar

The bottom navigation bar used as the main app navigation, containing tabs for Play, Pay, Expenses, Info, and Settings. Each BottomNavigationBarItem carries a label that Flutter exposes directly to assistive technologies.

CupertinoTabBar navigation items expose their label to screen readers

  1. Given a CupertinoTabBar is rendered with items "Play" and "Pay"
  2. Then the "Play" tab label is visible to screen readers
  3. And the "Pay" tab label is visible to screen readers

StatusIndicatorWidget

An animated pulsing dot used to indicate connection or sync status — for example the HMRC connection indicator in the Info tab. Accepts an optional semantic label so the status meaning is not communicated by colour alone.

StatusIndicatorWidget announces its status when a label is provided

  1. Given a StatusIndicatorWidget is rendered with status green and label "Connected"
  2. Then it has a semantic label "Connected"

Images

Asset images are loaded via the assetImage() helper or rendered through AnIcon. Informational images carry a semanticLabel; decorative images are wrapped in ExcludeSemantics so they are invisible to assistive technologies.

assetImage passes semanticLabel through to the Image widget

  1. Given an asset image is rendered with semanticLabel "Activity icon"
  2. Then the image widget has semanticLabel "Activity icon"

AnIcon image uses its label as the semantic label

  1. Given an AnIcon with label "Football" is rendered
  2. Then its image has semanticLabel "Football"

Decorative images are excluded from the semantic tree

  1. Given an image is rendered with decorative true
  2. Then it has no semantic label

CupertinoTextField

The standard text input used across forms — for example searching activities and entering invoice references. Delegates to Flutter's EditableText, which exposes the text field role natively to assistive technologies via the semantic tree.

CupertinoTextField is identified as a text field to screen readers

  1. Given a CupertinoTextField is rendered
  2. Then it is identified as a text field in the semantic tree

CircleButton

A low-level circular icon button used as the foundation for PrimaryCircleButton, RotatingCircleButton, and FadingRotatingIconButton. When a semanticLabel is provided the button is wrapped in a Semantics node that exposes the button role and accessible name to assistive technologies. Callers that omit the label rely on a parent Semantics node (e.g. DatePicker already wraps its CircleButton).

CircleButton with a semantic label announces its name to screen readers

  1. Given a CircleButton is rendered with semanticLabel "Refresh"
  2. Then it has a semantic label "Refresh"
  3. And it is marked as a button in the semantic tree

CircleButton without a semantic label renders without an extra Semantics wrapper

  1. Given a CircleButton is rendered without a semanticLabel
  2. Then no Semantics button wrapper with a label is added

PrimaryCircleButton

The branded add / action circular button used in list section headers across Play, Pay, and Expenses tabs. Forwards a semanticLabel to the underlying CircleButton so the purpose of the action is announced by screen readers rather than the raw icon glyph.

PrimaryCircleButton with a semantic label announces its name to screen readers

  1. Given a PrimaryCircleButton is rendered with semanticLabel "Add activity"
  2. Then it has a semantic label "Add activity"
  3. And it is marked as a button in the semantic tree

FadingRotatingIconButton

An animated circular icon button that fades in when visible and rotates on press. Used for contextual actions that appear and disappear based on UI state. When a semanticLabel is provided the visible button is wrapped in a Semantics node.

FadingRotatingIconButton announces its label when visible

  1. Given a FadingRotatingIconButton is rendered as visible with semanticLabel "Sync"
  2. Then it has a semantic label "Sync"
  3. And it is marked as a button in the semantic tree

FadingRotatingIconButton exposes nothing when hidden

  1. Given a FadingRotatingIconButton is rendered as hidden with semanticLabel "Sync"
  2. Then no element with semantic label "Sync" is present

CupertinoLargeBackButton

A larger-than-standard back button (80×80 hit target) used on detail screens. Wraps the Cupertino back icon in a Semantics node labelled "Back" so screen readers announce the navigation action, which is otherwise conveyed only by an icon glyph.

CupertinoLargeBackButton is announced as "Back" to screen readers

  1. Given a CupertinoLargeBackButton is rendered inside a navigable route
  2. Then it has a semantic label "Back"
  3. And it is marked as a button in the semantic tree

CupertinoLoadingButton

A button that shows its label text when idle and swaps to a spinner while an async action is in progress. The isMaxWidth variant wraps the CupertinoButton in a Semantics node that preserves the label and reflects the enabled/disabled state even while the spinner is displayed, so screen readers announce the button purpose throughout.

CupertinoLoadingButton exposes its label while in loading state

  1. Given a CupertinoLoadingButton is rendered with label "Submit" and loading true
  2. Then it has a semantic label "Submit"
  3. And it is marked as disabled in the semantic tree

CupertinoLoadingButton exposes its label and is enabled when not loading

  1. Given a CupertinoLoadingButton is rendered with label "Submit" and loading false
  2. Then it has a semantic label "Submit"
  3. And it is marked as enabled in the semantic tree

DocumentScannerButton

A GestureDetector wrapper that triggers the device document scanner. Callers pass any child widget as the visible affordance. The button is wrapped in a Semantics node with a configurable label (defaulting to "Scan document") so screen readers can identify and activate the control without seeing the child widget's visual content.

DocumentScannerButton announces its purpose to screen readers

  1. Given a DocumentScannerButton is rendered with its default label
  2. Then it has a semantic label "Scan document"
  3. And it is marked as a button in the semantic tree

DocumentScannerButton uses a custom label when provided

  1. Given a DocumentScannerButton is rendered with semanticLabel "Scan invoice"
  2. Then it has a semantic label "Scan invoice"
  3. And it is marked as a button in the semantic tree

GridSelection

An icon-grid selection widget used for choosing activity types and expense categories. Each cell's GestureDetector is wrapped in a Semantics node using the SelectableItem identifier as the accessible name and reflecting the selected state, so screen readers can identify and operate every grid cell without relying on visual highlighting alone.

GridSelection items expose their identifier as an accessible label

  1. Given a GridSelection is rendered with items "Football" and "Tennis"
  2. Then the "Football" cell has a semantic label "Football" and is marked as a button

GridSelection reflects the selected state of the chosen item

  1. Given a GridSelection is rendered with "Football" pre-selected
  2. Then the "Football" cell is marked as selected
  3. And the "Tennis" cell is not marked as selected

PagedCarousel

A paged icon carousel used for selecting activity-type icons. Previous / Next page buttons carry "Previous page" and "Next page" labels. Each icon cell is wrapped in a Semantics node using the AnIcon label and reflecting its selected state.

PagedCarousel Previous page button announces its purpose to screen readers

  1. Given a PagedCarousel is rendered with icons
  2. Then there is a semantic label "Previous page" in the widget tree

PagedCarousel Next page button announces its purpose to screen readers

  1. Given a PagedCarousel is rendered with icons
  2. Then there is a semantic label "Next page" in the widget tree

PagedCarousel icon cells expose their label and selected state

  1. Given a PagedCarousel is rendered with a "Football" icon selected
  2. Then the "Football" cell is marked as a button and as selected

TaskProgressIndicator

A three-state progress indicator (not started / running / done) used on multi-step operation screens. Each state is wrapped in a Semantics node: the running state is also a live region so assistive technologies announce the transition automatically.

TaskProgressIndicator in the running state is announced as a live region

  1. Given a TaskProgressIndicator is rendered with status running for task "Upload"
  2. Then it has a semantic label "Upload: in progress"
  3. And it is marked as a live region

TaskProgressIndicator in the done state announces completion

  1. Given a TaskProgressIndicator is rendered with status done for task "Upload"
  2. Then it has a semantic label "Upload: done"

TaskProgressIndicator in the not-started state announces waiting

  1. Given a TaskProgressIndicator is rendered with status not started for task "Upload"
  2. Then it has a semantic label "Upload: not started"

Text Scaling

All components must remain usable at system text scales up to 200% (WCAG 1.4.4). Scalable text must not overflow its container or obscure interactive controls when the user increases the system font size.

Components render without layout overflow at 200% system text scale

  1. Given a PilButton is rendered with label "Confirm" at 200% text scale
  2. Then the widget renders without layout overflow