[Research] Add ReferenceTargetController POC for cross-root ARIA#6313
[Research] Add ReferenceTargetController POC for cross-root ARIA#6313Rajdeepc wants to merge 5 commits into
Conversation
- self-contained FACE combobox demo exercising ElementInternals for form value, validation, keyboard, and a11y patterns - documents aria-activedescendant, aria-controls, and popup ownership behavior within shadow DOM boundaries - includes IDREF matrix (light DOM, slotted, shadow, cross-root) with browser pass/fail findings matching the text-field PoC - adds QA tree inspection results confirming role, name, value, and expanded/collapsed states render correctly in the a11y tree Co-authored-by: Cursor <cursoragent@cursor.com>
Implements a reactive controller that approximates the WICG Reference Target proposal for cross-root ARIA relationships. When native `ShadowRoot.referenceTarget` is unavailable, the controller observes light-DOM elements referencing the host by IDREF and materializes equivalent accessible text on the shadow-internal target. Includes demo elements, Storybook stories, play-function tests, and a design note documenting the approach, gaps vs native, and a hybrid recommendation (ship shim now, migrate to native when baseline). Co-authored-by: Cursor <cursoragent@cursor.com>
|
📚 Branch Preview Links🔍 First Generation Visual Regression Test ResultsWhen a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:
Deployed to Azure Blob Storage: If the changes are expected, update the |
Co-authored-by: Cursor <cursoragent@cursor.com>
What this PR does
Today, when a
<label>or anaria-labelledbyattribute in the page tries to point at one of our custom elements, it can't reach the real input hidden inside the component's shadow DOM. The browser spec that will fix this (referenceTarget) exists but no browser ships it yet.This PR builds a small utility (a "reactive controller") that bridges that gap right now. Attach it to any custom element, tell it which internal element is the "real" target, and it automatically:
<label>focus the internal inputGoals
referenceTarget(correctness, performance, mutation/lifecycle edge cases, attribute surface).Non-goals
What was delivered
2nd-gen/packages/core/controllers/reference-target-controller/src/reference-target-controller.tsstories/demo-hosts.ts—<demo-labeled-input>and<demo-described-input>stories/reference-target-controller.stories.ts— visible under Controllers / Reference target (POC)test/reference-target-controller.test.tsresearch/reference-target-poc/DESIGN-NOTE.mdOutcome / recommendation
Hybrid: build the shim now, migrate to native when it reaches baseline.
referenceTargetenabled by default (all behind flags as of May 2026).Acceptance criteria
Given native
referenceTargetis unavailable or disabled in test browsers,When the POC controller is attached to a demo custom element,
Then:
End-to-end scenario works — five scenarios demonstrated (label-for, aria-labelledby, aria-describedby, click-to-focus, dynamic label update); the shadow-internal input receives a correct accessible name.
Basic lifecycle handled — connect/disconnect, host
idchanges, referrer mutations, and host shadow root presence all work; limitations listed in design note section 4.Design note written — approach, comparison with WICG explainer, mapping to native API, 10 gap concerns rated by severity.
DESIGN-NOTE.mdsections 2-5.Screen reader smoke test steps recorded — VoiceOver/NVDA procedure with result columns for reviewers to fill in.
DESIGN-NOTE.mdsection 6.Recommendation with rationale — Hybrid, tied to Chrome Platform Status and Can I use data.
DESIGN-NOTE.mdsection 7.How it works (one-liner usage)
That's it. External labels, descriptions, and error messages that reference the host by ID will now reach the shadow-internal element.
Technical notes and resources
1st-gen/packages/field-label/src/FieldLabel.ts,ElementResolutionController2nd-gen/packages/core/controllers/reference-target-controller/QA
devStorybook tag).Reviewer's checklist
Manual review test cases
Controllers/Reference target (POC)/Label for forwarding→ inspect shadow input → expectaria-label="Full name"aria-description="Must be at least 8 characters"aria-labelupdates to new textaria-labelis removed from the detached shadow input