Skip to content

feat: support aria-busy state announcement - #240

Open
AdamMagued wants to merge 2 commits into
guidepup:mainfrom
AdamMagued:feat/support-aria-busy
Open

AdamMagued wants to merge 2 commits into
guidepup:mainfrom
AdamMagued:feat/support-aria-busy

Conversation

@AdamMagued

Copy link
Copy Markdown
Contributor

Summary

Implements support for the aria-busy state per the W3C WAI-ARIA 1.2 specification, addressing issue #36.

Changes

  1. Accessibility Tree and Node Data:

    • Detects aria-busy="true" on elements and sets busy: boolean on AccessibilityNode.
    • Propagates busy across root and child tree nodes.
    • Maps attribute labels (busy for true, not busy for false).
  2. Focused Element Announcements:

    • Implemented #announceFocusedBusy in Virtual.ts to observe mutations on active/focused elements.
    • Announces "busy" when aria-busy transitions to "true" and "not busy" when set to "false".
    • Avoids duplicate announcements when the focused element is also an active live region.
  3. Live Region Processing:

    • Tracks busy: boolean state across ancestor hierarchies in getLiveRegionAttributes.
    • Announces ${live}: busy when a live region becomes busy.
    • Suppresses intermediate DOM updates while a live region or its ancestors remain busy.
    • Automatically announces updated region content when aria-busy is cleared or set to "false".
  4. Tests and Coverage:

    • Added comprehensive integration tests in test/int/ariaBusy.int.test.ts covering static navigation, accessibility tree attributes, dynamic focused changes, live regions, nested busy ancestry, batch mutations, and edge cases.
    • All 58 test suites (358 tests) pass with 100% test pass rate and satisfy all strict coverage thresholds (statements >= 98%, branches >= 95%, functions 100%, lines >= 98%).

Closes #36

- Expose busy state and attribute mapping on accessibility nodes

- Announce busy and not busy state transitions for focused elements

- Support aria-busy on live regions, suppressing interim updates until busy period completes

- Closes guidepup#36
@cmorten

cmorten commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution, and apologies for the slow review!

Looks very thorough (nice work!) but I'm wondering if the implementation correctly captures the intent of aria-busy?

As stands it's acting as both a state (which I think is great, nice one) but also a live region like announcement, whereas my understanding of the spec is not that we should expect "busy" or "not busy" to be announced, but rather the aria-busy state acts as as lock of sorts on content changes to alter the behaviour of contained live region(s). Namely all changes to content while aria-busy is "true" should be somewhat deferred until "false" again and the combined delta is announced all at once.

Pertinent parts from the spec:

Indicates an element is being modified and that assistive technologies could wait until the modifications are complete before exposing them to the user.

When aria-busy is true for an element, assistive technologies can ignore changes to content that is an accessibility descendant that element and then process all changes made during the busy period as a single, atomic update when aria-busy becomes false.

If an element with role feed is marked busy, assistive technologies might defer rendering changes that occur inside the feed with the exception of user-initiated changes that occur inside the article that the user is reading during the busy period.

REF: https://w3c.github.io/aria/#aria-busy

For this implementation, inclined to not implement anything special/specific for the feed/article behaviours.

In terms of what actual screen readers do, would need to test if what you've implemented is now a standard behaviour (unless you have good evidence already to support this?). Going from https://a11ysupport.io/tech/aria/aria-busy_attribute, which is quite an old manual test and doesn't look at live region behaviours at all, it seems JAWS (at the time at least) might just ignore content with aria-busy="true" applied, and potentially none of the other screen readers support it in any sense (as mention this could be completely out of date though!). If this is still the case it doesn't feel like any of the core screen readers are supporting this (unless the spec has changed since the manual test was implemented)


Testing of what screen readers actually do pending, feels like might be a case of simplifying what you've implemented?

  1. Keep the "accessibility tree and node data" behaviour
  2. Drop the "focused element announcements" behaviour
  3. Drop the "Announces ${live}: busy when a live region becomes busy." behaviour
  4. Keep the rest of the "live region processing" behaviour

What do you think?

Comment thread src/Virtual.ts Outdated
target?.focus();
}

#announceFocusedBusy(mutations: MutationRecord[]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be removed?

Comment thread src/getLiveSpokenPhrase.ts Outdated
};

function getLiveRegionAttributes(
export function getLiveRegionAttributes(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If remove the focus behaviour then can drop the export

Comment thread src/getLiveSpokenPhrase.ts Outdated
if (target === container || targetAncestor === null) {
return {
atomic: atomic ?? DEFAULT_ATOMIC,
busy: busy ?? false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: for consistency

Suggested change
busy: busy ?? false,
busy: busy ?? DEFAULT_BUSY,

Comment thread src/getLiveSpokenPhrase.ts Outdated
Comment on lines +333 to +337
const isBusy = targetElement.getAttribute("aria-busy") === "true";

if (isBusy) {
return `${live}: busy`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think we can drop?

@AdamMagued

Copy link
Copy Markdown
Contributor Author

Agreed — the WAI-ARIA specification defines aria-busy as a state and a lock for deferring live region updates rather than a spoken announcement on focus or transition. Updated the PR per your four points:

  1. Kept aria-busy on accessibility tree nodes and node data.
  2. Removed busy announcements on focused elements.
  3. Removed ${live}: busy announcements when a live region becomes busy.
  4. Kept live region update deferral while aria-busy="true" and atomic flush when aria-busy returns to `"false"".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support aria-busy

2 participants