Skip to content

fix(tree): traverse open shadow roots and resolved slots in accessibility tree - #239

Open
AdamMagued wants to merge 1 commit into
guidepup:mainfrom
AdamMagued:fix/shadow-dom-traversal
Open

AdamMagued wants to merge 1 commit into
guidepup:mainfrom
AdamMagued:fix/shadow-dom-traversal

Conversation

@AdamMagued

Copy link
Copy Markdown
Contributor

Description

Resolves #182

This PR adds support for traversing open Shadow DOM roots and resolving elements when building the accessibility tree.

Summary of Changes

  1. Open Shadow DOM Traversal:

    • In src/createAccessibilityTree.ts, when a node has an open shadow root (isElement(node) && node.shadowRoot), its child nodes are traversed via node.shadowRoot.childNodes instead of light DOM childNodes.
    • Light DOM children not assigned to any <slot> are excluded from the accessibility tree per the HTML Accessibility API Mappings.
  2. Slot Resolution and Tree Flattening:

    • For <slot> elements, assigned nodes are resolved using slot.assignedNodes({ flatten: true }).
    • If no nodes are assigned, slot fallback child nodes are resolved.
    • Slot elements are treated as transparent containers and unwrapped (analogous to explicit presentational elements), exposing slotted children directly under the appropriate parent in the accessibility tree and preventing duplicate announcements of text children.
  3. Shadow-Aware Node Lookup and Relationship Attributes:

    • In src/getNodeByIdRef.ts, ID references are looked up across open shadow roots recursively when not found in the root container scope.
    • In src/createAccessibilityTree.ts, deep shadow queries are used for [aria-flowto] and [aria-owns] attributes to properly construct alternate reading orders and owned element relationships across shadow DOM boundaries.
  4. DOM Observation and Focus Handling:

    • In src/observeDOM.ts, mutation observers are attached to open shadow roots and dynamically added shadow subtrees so that tree invalidation and live regions work inside web components.
    • In src/Virtual.ts, #handleFocusChange synchronously retrieves event.composedPath()[0] before yielding to ticks, ensuring focus transitions to elements inside shadow DOM correctly update the active node.
    • In src/getElementFromNode.ts and src/getLiveSpokenPhrase.ts, shadow boundary crossings via target.parentNode.host are handled when resolving target elements and live region ancestors.
  5. Tests:

    • Added unit tests in test/createAccessibilityTreeShadowDom.test.ts covering open shadow root traversal, slot unwrapping, fallback content, and shadow-aware getNodeByIdRef.
    • Added comprehensive integration tests in test/int/shadowDom.int.test.ts covering full screen reader navigation, named slots, fallback content, landmark commands, link commands, heading commands, clicking/focusing inside shadow DOM, and live region mutations inside shadow roots.

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.

Tree builder does not traverse open shadow DOM roots

1 participant