HelpButton has a semantic label for screen readers
Given the HelpButton is rendered with title "Amount" and a messageThen it has a semantic label containing "Help"And it is marked as a button in the semantic tree
WCAG 2.2 compliance at the component level — semantic labels, touch targets, button roles, and alt text for base UI widgets.
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.
Given the HelpButton is rendered with title "Amount" and a messageThen it has a semantic label containing "Help"And it is marked as a button in the semantic treeGiven the HelpButton is rendered with title "Amount" and a messageThen its tappable area is at least 44 by 44 logical pixelsGiven a HelpButton is rendered with title "Invoice Number" and a widget content bodyThen it has a semantic label containing "Help"And it is marked as a button in the semantic treeGiven a HelpButton is rendered without message or contentThen no help button is rendered in the semantic treeThe 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.
Given a PilButton is rendered with label "Confirm"Then the label "Confirm" is present in the semantic treeAnd the button role is present in the semantic treeGiven a PilButton is rendered with label "Confirm"Then its tappable area is at least 44 by 44 logical pixelsAn 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.
Given a SemanticIconButton is rendered with label "Share invoice"Then it has a semantic label "Share invoice"And it is marked as a button in the semantic treeGiven a SemanticIconButton is rendered with label "Share invoice"Then its tappable area is at least 44 by 44 logical pixelsGiven a SemanticIconButton is rendered with label "Share invoice" and is disabledThen it has a semantic label "Share invoice"And it is marked as a button in the semantic treeAnd it is marked as disabledA 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.
Given a CircularIconButton is rendered with label "Add activity"Then it has a semantic label "Add activity"And it is marked as a button in the semantic treeA 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.
Given an IconButton is rendered with label "Add item"Then the label "Add item" is visible in the semantic treeAnd it is marked as a button in the semantic treeGiven an IconButton is rendered with label "Add item"Then the icon is excluded from the semantic treeGiven an IconButton is rendered in no-style mode with no labelThen the icon is excluded from the semantic treeA 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.
Given an ASwitch is rendered with initial value offThen the switch has a toggled stateAnd the switch is not toggledGiven an ASwitch is rendered with initial value onThen the switch is toggledA 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.
Given a SegmentedSelector is rendered with options "Activities" and "Invoices"Then the "Activities" segment is marked as a button and as selectedGiven a SegmentedSelector is rendered with options "Activities" and "Invoices"Then the "Invoices" segment is marked as a button and is not selectedGiven a SegmentedSelector is rendered with options "Overview" and "Details" and "Overview" selectedThen all segments are marked as buttons in the semantic treeGiven a SegmentedSelector is rendered with options "Income" and "Expenses" and "Income" selectedWhen the user taps the "Expenses" segmentThen the onChanged callback is called with index 1A 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.
Given an AmountInput is renderedThen each adjustment chip has a minimum size of 24 by 24 logical pixelsA 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.
Given a DateChip is rendered for a dateThen it has a semantic label "Previous day"And it is marked as a button in the semantic treeGiven a DateChip is rendered for a dateThen it has a semantic label "Next day"And it is marked as a button in the semantic treeGiven a DateChip is rendered for a dateThen each navigation button has a tappable area of at least 44 by 44 logical pixelsA 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.
Given a DatePicker is rendered with picker label "Select date"Then the calendar button has semantic label "Select date"And it is marked as a button in the semantic treeGiven a DatePicker is rendered with initial date 2025-01-15Then the date display has a semantic label containing the dateGiven a DatePicker is rendered with initial date 2025-01-15Then the date display has the hint "double-tap to change date"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.
Given a FeatureCard is rendered with title "Play"Then it has a semantic label "Play"And it is marked as a button in the semantic treeGiven a FeatureCard is rendered with title "Pay" and a badge count of 3Then its semantic label contains "Pay" and "3 new"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.
Given an AnimatedListTile is rendered with semanticLabel "Invoice 001, £100" and semanticHint "Opens invoice details"Then the tile has semantic label "Invoice 001, £100"And the tile has semantic hint "Opens invoice details"And it is marked as a button in the semantic treeA 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.
Given a SlidableTile is rendered with a tileThen the delete action label "Delete" is presentA 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.
Given the Loading widget is renderedThen it has a semantic label "Loading"And it is marked as a live regionA 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.
Given a Loader is rendered in the loading stateThen a live-region label "Loading" is presentGiven a Loader is rendered in the error stateThen the label "Retry" is present in the semantic treeAnd a button role is present in the semantic treeA 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.
Given a TopTab is rendered with tabs "Notes" and "Info"Then the "Notes" tab has a semantic label and is marked as a buttonGiven a TopTab is rendered with tabs "Notes" and "Info"Then the "Info" tab has a semantic label and is marked as a buttonGiven a TopTab is rendered with "Notes" as the active tabThen the "Notes" tab is marked as selectedGiven a TopTab is rendered with "Notes" as the active tabThen the "Info" tab is not marked as selectedThe 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.
Given a CupertinoTabBar is rendered with items "Play" and "Pay"Then the "Play" tab label is visible to screen readersAnd the "Pay" tab label is visible to screen readersAn 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.
Given a StatusIndicatorWidget is rendered with status green and label "Connected"Then it has a semantic label "Connected"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.
Given an asset image is rendered with semanticLabel "Activity icon"Then the image widget has semanticLabel "Activity icon"Given an AnIcon with label "Football" is renderedThen its image has semanticLabel "Football"Given an image is rendered with decorative trueThen it has no semantic labelThe 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.
Given a CupertinoTextField is renderedThen it is identified as a text field in the semantic treeA 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).
Given a CircleButton is rendered with semanticLabel "Refresh"Then it has a semantic label "Refresh"And it is marked as a button in the semantic treeGiven a CircleButton is rendered without a semanticLabelThen no Semantics button wrapper with a label is addedThe 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.
Given a PrimaryCircleButton is rendered with semanticLabel "Add activity"Then it has a semantic label "Add activity"And it is marked as a button in the semantic treeAn 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.
Given a FadingRotatingIconButton is rendered as visible with semanticLabel "Sync"Then it has a semantic label "Sync"And it is marked as a button in the semantic treeGiven a FadingRotatingIconButton is rendered as hidden with semanticLabel "Sync"Then no element with semantic label "Sync" is presentA 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.
Given a CupertinoLargeBackButton is rendered inside a navigable routeThen it has a semantic label "Back"And it is marked as a button in the semantic treeA 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.
Given a CupertinoLoadingButton is rendered with label "Submit" and loading trueThen it has a semantic label "Submit"And it is marked as disabled in the semantic treeGiven a CupertinoLoadingButton is rendered with label "Submit" and loading falseThen it has a semantic label "Submit"And it is marked as enabled in the semantic treeA 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.
Given a DocumentScannerButton is rendered with its default labelThen it has a semantic label "Scan document"And it is marked as a button in the semantic treeGiven a DocumentScannerButton is rendered with semanticLabel "Scan invoice"Then it has a semantic label "Scan invoice"And it is marked as a button in the semantic treeAn 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.
Given a GridSelection is rendered with items "Football" and "Tennis"Then the "Football" cell has a semantic label "Football" and is marked as a buttonGiven a GridSelection is rendered with "Football" pre-selectedThen the "Football" cell is marked as selectedAnd the "Tennis" cell is not marked as selectedA 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.
Given a PagedCarousel is rendered with iconsThen there is a semantic label "Previous page" in the widget treeGiven a PagedCarousel is rendered with iconsThen there is a semantic label "Next page" in the widget treeGiven a PagedCarousel is rendered with a "Football" icon selectedThen the "Football" cell is marked as a button and as selectedA 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.
Given a TaskProgressIndicator is rendered with status running for task "Upload"Then it has a semantic label "Upload: in progress"And it is marked as a live regionGiven a TaskProgressIndicator is rendered with status done for task "Upload"Then it has a semantic label "Upload: done"Given a TaskProgressIndicator is rendered with status not started for task "Upload"Then it has a semantic label "Upload: not started"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.
Given a PilButton is rendered with label "Confirm" at 200% text scaleThen the widget renders without layout overflow