Reference Target

Interactive Demos

This page shows a few of the things that can be achieved with the Reference Target proposal.
For more information, see the explainer. To use the demos below, load this page in a browser that supports Reference Target. Currently, you can use Edge or Chrome, but you also need to enable the edge://flags/#enable-experimental-web-platform-features flag.

⚠️ Reference target isn't enabled in your browser, so these demos won't work yet.

Description

Source

Demo

Label

Label association across shadow DOM. A <label> element in the light DOM targets an <input> inside a custom element's shadow tree via referenceTarget, enabling click-to-focus and accessibility wiring without exposing internals (try clicking "Checkbox value").

<label for="my-checkbox">Checkbox value (click me to toggle checkbox)</label>
<custom-checkbox id="my-checkbox">
  <template shadowrootmode="open" shadowrootreferencetarget="real-checkbox">
    <input id="real-checkbox" type="checkbox">
  </template>
</custom-checkbox>

Popover

Popover invocation across shadow DOM. A button[popovertarget] in the light DOM toggles a popover living inside a custom element's shadow tree, using referenceTarget to bridge the encapsulation boundary.

<button popovertarget="custom-popover">Toggle popover</button>
<custom-popover id="custom-popover">
  <template shadowrootmode="open" shadowrootreferencetarget="real-popover">
    <style>
      [popover] { border-radius: 50%; padding: 5em 1em; }
    </style>
    <div id="real-popover" popover>Here's the popover content</div>
  </template>
</custom-popover>

aria-labelledby

Fine-grained ARIA labelling. In some cases we might want to use only part of a component's shadow content as the accessible name for another element. aria-labelledby can be combined with referenceTarget to achieve this by pointing to a specific element inside the shadow tree.

<input aria-labelledby="fancy-label">
<fancy-description id="fancy-label">
  <template shadowrootmode="open" shadowrootreferencetarget="inner-label">
    <div id="inner-label">This is the text we want to use as the accessible name.</div>
    <div>This other text won't be included in the accessible name.</div>
 </template>
</fancy-description>

Things Reference Target does ⚠️ not ⚠️ do

To clear up misconceptions about what reference target does, the following table shows some scenarios that are out of scope.

Description

Source

Attribute forwarding

Attribute forwarding is not supported. The referenceTarget attribute does not copy attributes from the light DOM to elements inside the shadow tree.

<fancy-description aria-label="This is an accessible name">
  <template shadowrootmode="open" shadowrootreferencetarget="inner-element">
    <div id="inner-element">The aria-label attribute does not get forwarded to this element.</div>
  </template>
</fancy-description>

References out of shadow DOM

References out of the shadow DOM are not supported. The referenceTarget attribute only works for ID references pointing *into* a shadow tree, but not for references pointing out of the shadow DOM.

<custom-button popovertarget="my-popover">
  <template shadowrootmode="open" shadowrootreferencetarget="real-button">
    <!-- The popovertarget reference isn't forwarded, so this doesn't work. Put another way, 
         reference target only works for ID references pointing *into* a shadow, but in this case
         the ID reference needs to be pointing out of the shadow, from the button to the popover. -->
    <button id="real-button">Open popover?</button>
  </template>
</custom-button>
<div id="my-popover" popover>Popover contents</div>

<!-- There's an alternative way to do this however, setting up the reference using JS: -->
<working-custom-button popovertarget="my-working-popover">
  <template shadowrootmode="open">
    <button id="real-button">Open popover?</button>
  </template>
</working-custom-button>
<div id="my-working-popover" popover>Popover contents</div>
<script>
  customElements.define("working-custom-button", class WorkingCustomButton extends HTMLElement {
    constructor() {
      super();
      this._internals = this.attachInternals();
      const real_button = this._internals.shadowRoot.querySelector("#real-button");
      const popover = document.querySelector("#my-working-popover");
      // Use script to set up the reference to the popover, pointing *out*
      // of the shadow. Note that references set up this way can only
      // point *out* of a shadow root or to a node in the same root.
      // For references pointing *into* a shadow, we need reference target.
      real_button.popoverTargetElement = popover;
    }
  });
</script>