skip to content

A screen-reader user taps Sync in a fitness app and hears nothing during a 20-second sync or when it ends — what should the loading indicator's accessibility contract specify?

level: seniorimportance: should knowfreq 38%

answer

  1. a wait is a status message
  2. name what is loading
  3. start, milestones, finish, failure
  4. silence after hiding says nothing
  5. mark the updating region busy

basics

~20 s

Name the indicator, announce the wait and occasional progress without moving focus, announce completion or failure explicitly, and mark the updating region busy. WCAG 2.2 SC 4.1.3 Status Messages treats waiting and progress as status messages.

solid answer

~40 s

Under WCAG 2.2, **4.1.3 Status Messages** (Level AA) covers messages about 'the waiting state of an application' and 'the progress of a process', so a sync indicator must reach assistive technology without taking focus. The contract should specify: an **accessible name** that says what is loading ('Syncing workouts'); an announcement when the wait starts; **intermittent** progress at meaningful steps rather than every percent; an explicit announcement when it **ends** — 'Sync complete, 12 workouts added' or the failure and what to do — because simply hiding the spinner is silent to a screen-reader user; and marking the region being refreshed as **busy**, so assistive technologies may wait and present the new content as one update. Focus stays where the user left it.

go deeper

for a junior

Recall that a purely visual spinner is silent to screen-reader users, and that waiting and progress are status messages under WCAG 4.1.3.

for a middle

Explain the full contract: a named indicator, start and occasional progress messages without focus moves, an explicit end or failure message, and a busy region.

for a senior

Diagnose silent or noisy loading with two screen readers, fix the missing end announcement, and keep focus stable when refreshed content replaces the focused element.

for a principal

Put the announcement contract into the system's loading components so no team ships a silent spinner, and state it once, platform-neutral, for web and native teams.

## Why silence is the default failure A loading indicator is usually **purely visual**: an animation appears, then disappears. A sighted user reads both events. A screen-reader user, whose focus is still on the Sync control, gets neither — unless the design specifies how the waiting state reaches assistive technology. The result in the scenario is 20 seconds of silence and no signal that the sync finished. ## What WCAG 2.2 requires **SC 4.1.3 Status Messages** (Level AA) requires that status messages can be presented to users by assistive technologies **without receiving focus**. WCAG's definition of a status message includes information 'on the waiting state of an application' and 'on the progress of a process'. The W3C Understanding document gives two examples directly relevant here: a 'busy' icon whose appearance is announced as 'application busy', and a progress bar whose status is announced **intermittently**. The same document calls out the **removal of status text**: when a 'busy' or 'waiting' message disappears, sighted users read the absence as 'ready', but non-sighted users are unaware of the change unless something tells them. That is precisely the silent end of the sync. ## The contract, component by component | Contract item | Why | In the sync example | |---|---|---| | **Accessible name** | WAI-ARIA 1.2 requires a name for a progress bar; an unnamed spinner gives the screen reader nothing useful to say | 'Syncing workouts' | | **Start announcement** | The user needs to know the tap worked and a wait began | 'Syncing workouts from your watch' | | **Intermittent progress** | Long waits need reassurance, but not a stream of numbers | '20 of 40 workouts' at meaningful steps | | **End announcement** | Hiding the indicator is silent | 'Sync complete, 40 workouts added' | | **Failure announcement** | Errors are status messages too, and must say what to do | 'Sync stopped: watch disconnected. Try again.' | | **Busy region** | Content arriving piecemeal can be read half-built | Mark the history list busy until the new rows are in | | **Focus stays put** | Moving focus to announce is a change the user did not ask for | Focus remains on the Sync control | ## The busy state, precisely WAI-ARIA 1.2 defines a **busy** state for an element being modified. When a region is busy, assistive technologies **may** ignore changes inside it and then process everything as a single update once busy ends. It is a hint, not a guarantee, which is why the contract still requires an explicit end announcement. WAI-ARIA also says that when a progress bar describes loading of a particular region, that region should be marked busy until it has finished loading. ## Announcement frequency - **Too little:** nothing between start and end on a long job, and the user cannot tell slow from stuck. - **Too much:** every percent announced, and the user cannot hear anything else. - **Common practice:** announce at start, at a handful of meaningful milestones or at a slow interval for long jobs, and at the end. Short waits announce only the result. ## Across platforms The model is the same on the web and on native mobile: every platform's accessibility layer offers a way to post a message to the screen reader without moving focus, a way to expose a progress value with a name, and a way to mark content as updating. The spec states the **contract**; each platform's component implements it with its own mechanism, and the markup for the web version belongs to the platform's live-region guidance. ## Testing the contract 1. Trigger the sync with a screen reader running and focus on the control. 2. Confirm the start is announced, progress is occasional, and the end or failure is announced. 3. Confirm focus did not move and that the updated list is read as complete content, not fragments. 4. Repeat with a second screen reader, because announcement behaviour varies between assistive technologies.

  • Should the loading announcement interrupt whatever the screen reader is currently saying?
    Usually not. Waiting and progress messages are routine, so they should queue politely after current speech. Interrupting is reserved for urgent information, such as a failure that needs action now. Choosing how each message interrupts is part of the contract; the markup that implements it belongs to the platform's live-region guidance.
  • The sync replaces the list the user's focus was inside. What should happen to focus?
    Focus must not be lost to the top of the screen when the focused element is removed. The contract should keep focus on an equivalent element in the refreshed list, or on the list's heading, and announce the completion so the user knows why the content changed.

saying these in an interview costs you the question

  • A visible spinner is enough; screen readers will notice it
  • Hiding the spinner automatically tells screen-reader users the wait is over
  • Every percent of progress should be announced so nothing is missed
  • Moving focus to the spinner is the right way to announce loading
  • Marking a region busy guarantees the screen reader waits