Skip to content

status, alert, and progressbar

Three roles cover content that appears or changes after load and must be announced without moving focus: status for transient confirmations, alert for interruptions, and progressbar for determinate or indeterminate progress. A toast that fades in, a validation summary on submit, and a progress bar that fills are all silent to a screen reader user unless the region is declared live before the content changes.

Status — React: Implemented • Web Components: Implemented • iOS: Not yet planned • Android: Not yet planned

There is no partial credit: a live region declared after its content arrives announces nothing. Because the declaration must be in the markup up front, this is exactly the kind of signal a spec can carry and a runtime cannot infer.

Roles

Apply the following roles to elements:

RoleTypeElement
alertControlElement of type: container
statusControlElement of type: container
progressbarControlElement of type: container

All three are literal WAI-ARIA role tokens, so the emission is role="<name>" directly, and all three substitute in place — tag, classes, and children unchanged.

RoleWeb emission
alertrole="alert"
statusrole="status"
progressbarrole="progressbar" + aria-valuemin / aria-valuemax / aria-valuenow

States

These roles are typically applied in conjunction with the following states:

StateEffectClassify?
busyaria-busy on status and progressbarRecommended

The role does not replace the classification — the variant prop carrying busy still wants an entry.

Read more about states in specs.

Specs

Component anatomy typically has elements and roles like:

anatomy:
root:
type: container
role: status
dismiss:
type: instance
instanceOf: iconButton

Figma

Annotate the following layers:

  • root as role:status or role:alert — on the component node
  • a loading or progress component’s root as role:progressbar
  • a dismiss affordance keeps its own role:button in its own component, not here

For progressbar, declare the value binding in conventions/specs.yaml. A progress bar with neither a value nor an indeterminate binding emits the indeterminate form and warns.

React

Implementation

<Toast>Trip saved.</Toast>
<Alert>Your session has expired.</Alert>
<LoadingBar indeterminate />

Before / After

// before
<div className="toast" data-element="root">
{/* … */}
</div>
// after
<div className="toast" data-element="root" role="status">
{/* … */}
</div>

A progress bar with an indeterminate boolean and no value prop:

<div
className="loading-bar"
data-element="root"
role="progressbar"
aria-valuemin={p.min ?? 0}
aria-valuemax={p.max ?? 100}
{/* aria-valuenow omitted — the indeterminate binding resolved true */}
/>

Contract

PropTypeRoleGenerated body
min?number (default 0)progressbar onlyaria-valuemin
max?number (default 100)progressbar onlyaria-valuemax

alert and status add no props. None of the three adds an event handler — an announcement region reports; it does not respond.

Web Components

The role goes on the host, not on an element inside the shadow root. A live region must be in the document the assistive technology is observing, and while shadow content is observed, declaring the region on the host is what keeps it stable across re-renders of the inner tree.

One consequence: content that changes inside the shadow root announces correctly, but replacing the whole host element — rather than mutating its content — re-declares the region and may drop the announcement. Update content, do not swap the element.

iOS

Not yet planned. Intended binding:

alert.accessibilityAddTraits(.isStaticText) plus an announcement notification
statusAn announcement notification
progressbarProgressView, determinate or indeterminate
AnnouncedThe change is spoken without moving focus

Android

Not yet planned. Intended binding:

alertliveRegion = LiveRegionMode.Assertive
statusliveRegion = LiveRegionMode.Polite
progressbarLinearProgressIndicator with progressBarRangeInfo
AnnouncedThe change is spoken without moving focus

Additional details

Choosing alert or status

A declaration of interruption severity, and the designer’s to make — nothing in the markup can derive one from the other.

RolePolitenessUse for
alertAssertive — interrupts whatever is being readErrors, session expiry, destructive-action warnings
statusPolite — waits for a pauseSave confirmations, transient toasts, result counts

Choosing alert for a routine confirmation makes an application hostile to screen reader users, because every toast cuts off the sentence in progress. Choosing status for a genuine error means the error may not be announced until the user stops interacting.

Determinate and indeterminate progress

The split comes from the value convention in conventions/specs.yaml, never from a prop-name guess, and carries two members:

MemberNames
propThe prop carrying progress
indeterminateA boolean prop forcing the indeterminate presentation regardless of value

Two members are necessary because many libraries model an indeterminate loading bar with a boolean and no value prop at all. A value-only mechanism would discard the one signal such a component carries.

Resolution order:

  1. An indeterminate binding that is true suppresses aria-valuenow
  2. Otherwise a resolved value binding produces the determinate form
  3. Otherwise the indeterminate form is emitted, with a warning

An announcement region may contain a control

A toast with a dismiss affordance is the common case. That affordance is an instance carrying its own button role in its own spec; the containing status is unaffected. This is the sanctioned form of two roles in one rendered tree — different elements, different specs.

See also

  • errormessage — validation text wired to one control, a different job from a page-level alert
  • button — the dismiss affordance inside a toast
  • States — the busy state concept
  • Roles overview — the vocabulary and how roles are authored