Skip to content

[Research] Add ReferenceTargetController POC for cross-root ARIA#6313

Open
Rajdeepc wants to merge 5 commits into
mainfrom
rajdeep-chandra/feat-reference-target-controller-poc
Open

[Research] Add ReferenceTargetController POC for cross-root ARIA#6313
Rajdeepc wants to merge 5 commits into
mainfrom
rajdeep-chandra/feat-reference-target-controller-poc

Conversation

@Rajdeepc
Copy link
Copy Markdown
Contributor

@Rajdeepc Rajdeepc commented May 17, 2026

What this PR does

Today, when a <label> or an aria-labelledby attribute 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:

  • Finds any label or description in the page that references the host element
  • Copies that accessible text onto the shadow-internal target (so screen readers announce it)
  • Makes clicking an external <label> focus the internal input
  • Cleans up after itself when the element is removed from the page
  • Steps aside automatically if the browser gains native support

Goals

  1. Validate whether a controller-based shim gives acceptable accessibility and ergonomics for a small set of representative SWC use cases.
  2. Document gaps versus native referenceTarget (correctness, performance, mutation/lifecycle edge cases, attribute surface).
  3. Produce a clear go / no-go / defer recommendation with estimated follow-up cost if go.

Non-goals

  • Replacing all existing 1st-gen cross-root label logic in one pass.
  • Polyfilling every relationship attribute in the platform.
  • Guaranteed behavior parity with a future native implementation (POC is deliberately narrow).

What was delivered

Deliverable Where to find it
Controller implementation 2nd-gen/packages/core/controllers/reference-target-controller/src/reference-target-controller.ts
Demo custom elements stories/demo-hosts.ts<demo-labeled-input> and <demo-described-input>
5 Storybook stories stories/reference-target-controller.stories.ts — visible under Controllers / Reference target (POC)
7 automated tests (play functions) test/reference-target-controller.test.ts
Design note with gap analysis, smoke-test steps, and recommendation research/reference-target-poc/DESIGN-NOTE.md

Outcome / recommendation

Hybrid: build the shim now, migrate to native when it reaches baseline.

  • No browser ships referenceTarget enabled by default (all behind flags as of May 2026).
  • The shim covers the labelling/description subset that accounts for ~90% of SWC's cross-root ARIA needs.
  • Feature detection is built in — when native support lands, the controller automatically delegates to the browser with zero changes to consuming components.
  • Estimated follow-up to productionize: promote API, adopt in form components, add error-message scenario.

Acceptance criteria

Given native referenceTarget is unavailable or disabled in test browsers,

When the POC controller is attached to a demo custom element,

Then:

  1. 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.

    • ✅ Done. See stories + 7 passing play-function tests.
  2. Basic lifecycle handled — connect/disconnect, host id changes, referrer mutations, and host shadow root presence all work; limitations listed in design note section 4.

    • ✅ Done.
  3. Design note written — approach, comparison with WICG explainer, mapping to native API, 10 gap concerns rated by severity.

    • ✅ Done. See DESIGN-NOTE.md sections 2-5.
  4. Screen reader smoke test steps recorded — VoiceOver/NVDA procedure with result columns for reviewers to fill in.

    • ✅ Done. See DESIGN-NOTE.md section 6.
  5. Recommendation with rationale — Hybrid, tied to Chrome Platform Status and Can I use data.

    • ✅ Done. See DESIGN-NOTE.md section 7.

How it works (one-liner usage)

// In any 2nd-gen component constructor:
new ReferenceTargetController(this, { target: '#internal-input' });

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

  1. Reference Target for Cross-Root ARIA (WICG explainer)
  2. Chrome Platform Status — feature entry
  3. Can I use — referenceTarget
  4. Prior art in repo: 1st-gen/packages/field-label/src/FieldLabel.ts, ElementResolutionController
  5. POC location: 2nd-gen/packages/core/controllers/reference-target-controller/
  6. Comparison checklist for testing native vs shim included in design note section 6

QA

  1. Run the manual a11y smoke tests documented in the design note (section 6).
  2. Verify no regressions in unrelated packages (POC is isolated behind a dev Storybook tag).
  3. Confirm build/lint/test pass.
  4. If recommendation is accepted as go: file follow-up tickets for API design, feature detection strategy, and migration path from shim to native.

Reviewer's checklist

  • Automated tests cover all use cases
  • Validated on supported browsers

Manual review test cases

  • Label-for forwarding: Storybook → Controllers/Reference target (POC)/Label for forwarding → inspect shadow input → expect aria-label="Full name"
  • Click-to-focus: Click the label text → expect shadow input gains focus
  • aria-describedby forwarding: Inspect shadow input → expect aria-description="Must be at least 8 characters"
  • Dynamic re-sync: Click "Toggle label text" → expect aria-label updates to new text
  • Disconnect cleanup: Remove the element via DevTools → expect aria-label is removed from the detached shadow input

Rajdeep Chandra and others added 2 commits May 17, 2026 16:40
- 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>
@Rajdeepc Rajdeepc requested a review from a team as a code owner May 17, 2026 11:37
@Rajdeepc Rajdeepc added a11y Issues or PRs related to accessibility Research Issues that require research and/or RFCs. labels May 17, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 17, 2026

⚠️ No Changeset found

Latest commit: 047b8f3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@Rajdeepc Rajdeepc added the Status:WIP PR is a work in progress or draft label May 17, 2026
@Rajdeepc Rajdeepc marked this pull request as draft May 17, 2026 11:37
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 17, 2026

📚 Branch Preview Links

🔍 First Generation Visual Regression Test Results

When 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: pr-6313

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

@Rajdeepc Rajdeepc marked this pull request as ready for review May 19, 2026 08:21
@Rajdeepc Rajdeepc added Status:Ready for review PR ready for review or re-review. Status:WIP PR is a work in progress or draft and removed Status:WIP PR is a work in progress or draft Status:Ready for review PR ready for review or re-review. labels May 19, 2026
@Rajdeepc Rajdeepc marked this pull request as draft May 19, 2026 08:21
@Rajdeepc Rajdeepc marked this pull request as ready for review May 21, 2026 08:50
@Rajdeepc Rajdeepc added Status:Ready for review PR ready for review or re-review. do-not-merge NO MERGE-Y! Peer review and removed Status:WIP PR is a work in progress or draft Status:Ready for review PR ready for review or re-review. labels May 21, 2026
@Rajdeepc Rajdeepc self-assigned this May 21, 2026
@Rajdeepc Rajdeepc added experiment and removed Research Issues that require research and/or RFCs. labels May 21, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y Issues or PRs related to accessibility do-not-merge NO MERGE-Y! experiment Form Patterns Peer review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant