Commerce · Concept

Keep focus. Announce progress.

An asynchronous action needs visible state and programmatic feedback without sending keyboard focus somewhere unexpected.

The principle

The asynchronous status principle

The button shows what it can do now. The status region explains what just happened.

State sequence

Make the whole journey perceivable.

Do not jump directly from an initial label to a silent visual checkmark. Waiting, success, and failure are meaningful outcomes.

Ready

Add to cart

The button is operable and names the product context.

Busy

Adding…

The button remains focused, blocks duplicate activation, and exposes a busy state.

Outcome

Added or Try again

Visible state and a complete status message communicate success or failure.

Interactive demo

Choose an outcome and add.

Focus stays on the action button throughout the simulated request.

  • The button becomes “Adding…” and temporarily uses aria-disabled and aria-busy.
  • The pre-existing role="status" region announces complete messages.
  • Failure restores an operable “Try again” action.

Harbor trail boot

$128

Simulated result

Cart: 0 items.

Do / Don’t

Feedback does not need focus.

Status messages exist specifically to expose important updates that do not change context. Moving focus to every confirmation interrupts the person’s workflow.

Don’tMove focus to a toast

The next action becomes uncertain

A confirmation steals focus from the product, forcing the person to navigate back before continuing.

toast.focus(); // unnecessary context change
DoUpdate a persistent status region

Focus stays where work happened

The screen reader announces a complete message while the keyboard remains on the initiating button.

<p role="status" aria-atomic="true">Cart: 0 items.</p>

status.textContent = 'Cart: 1 item. Harbor trail boot added.';

State semantics

Use each attribute for its job.

ARIA does not replace behavior. If aria-disabled="true" keeps a busy button focusable, JavaScript must also block repeated activation.

role="status"

Polite, non-interrupting success, result, and progress feedback that does not receive focus.

aria-busy

Indicates that an element or region is being updated. Remove it when processing finishes.

Not aria-pressed

Adding an item is an action, not a persistent on/off toggle. Do not invent toggle state.

Quick test

Slow the request down.

  1. Simulate a slow response and confirm a visible waiting state appears.
  2. Press the action repeatedly while busy and confirm duplicate requests are blocked.
  3. Confirm keyboard focus remains on the initiating button.
  4. Listen for complete messages that include the product and updated cart quantity.
  5. Test success and failure; failure must offer a clear recovery action.
  6. Confirm the live region exists before its text changes and is not excessively chatty.