#961: Reference Target

Visit on Github.

Opened Jun 3, 2024

こんにちは TAG-さん!

I'm requesting a TAG review of Reference Target.

Reference Target is a feature that enables using IDREF attributes such as for and aria-labelledby to refer to elements inside a component's shadow DOM, while maintaining encapsulation of the internal details of the shadow DOM. The main goal of this feature is to enable ARIA to work across shadow root boundaries.

Further details:

You should also know that...

There have been a number of competing proposals in this area but little progress towards actually shipping solutions in browsers. See https://alice.pages.igalia.com/blog/how-shadow-dom-and-accessibility-are-in-conflict/.

For that reason this proposal is carefully scoped with the goal of identifying pieces of functionality with good prospects of reaching consensus such that they can be shipped and made available to developers, whose feedback will help inform additional work.

Hence, the proposal in the explainer is broken down into two phases. Phase 1 adds the ability to designate a single element as the target for all IDREF properties that refer to the host, while Phase 2 adds the ability to re-target specific properties individually. Breaking it down in this manner may allow us to reach consensus on Phase 1 while the more complex details of Phase 2 are still under discussion.

So if the TAG has feedback or concerns that apply to Phase 2 only, but is happy with Phase 1, it would be helpful for us if that is called out specifically so that Phase 1 can more easily move forward.

Discussions

Log in to see TAG-private discussions.

Discussed Aug 1, 2024 (See Github)

Matthew: need more time - but a couple of thoughts... the problem is definitely one that needs solving. Broken up into different phases. First phase solves a simpler part of the problem which looks good. They want feedback on phase 1. looks promising. Phase 2 looks well thought out but is complex. They reference other proposals and I don't know if those proposals are ongoing or were shelved... I also noticed that it's moved... it was in AOM and now it's in Web Components... So I'm figuring out what that means... also it would be good to ask people who develop ATs what they think... It looks well thought out...

Matthew: will leave some feedback

Discussed Aug 1, 2024 (See Github)

bumped

Discussed Sep 1, 2024 (See Github)

Jeffrey: meant as an accessibility thing...

Lea: had some concerns about the design.. but also saw excitement from webcomponents people

Jeffrey: it's incredibly needed, my worry is it's across more things than just accessibility... problems with shadow dom and links to #foo not scrolling.. CSS has some similar problems.. deal with in a different way, might not be IDs

Lea: the whole syntax of using id refs of linking elements to other elements is broken and we want a better way to link to other elements, it's not just about aria, it's also about developer ergonomics. Web components authors don't have a good precedent to follow. Assigning ids to everything produces very poor experiences for authors. One one hand this is solving a very prominent pain point, on the other hand my sense is that it's solving it with a bandaid. Also something that needs to be solved asap. Waiting to solve the bigger problem is also suboptimal

Jeffrey: yes. Should generally support this, but describe the overall problem and ask people to keep thinking about how to solve the general problem. Gaps repo!

Lea: exactly! I wrote a post in whatwg about this, I need to edit and post there. [Narrator: It was already posted: https://github.com/w3ctag/gaps/issues/2]

Lea: we also don't have standardised api about how this corresponds to idl attributes. Value, or reference to element... or two idl attributes for value and element.

Jeffrey: accessibility folks using that setter as a way to cross shadow boundaries ... ?? is opposed to crossing the shadow boundary ... to be continued.

Lea: hes' going to have to revise his perspective because there's a ton of use cases

Discussed Sep 1, 2024 (See Github)

Jeffrey: I don't want tod elay anything with that comment. If they found a way forward for any part of it then they should take that path, to make progress.

Jeffrey: we should review phase ii but we should also approve phase i.

jeffrey to write comment

Discussed Sep 1, 2024 (See Github)

Matthew: ... interesting ... the central problem they are trying to solve is one that's been there for a long time - 2 phases - i didn't see anything concerning with the first part - i haven't given the 2nd part enough of a review. This particular thing they are trying to do is straightforward and good. But the point Jeffrey raises is interesting... we need to be somewhat careful. It sounded like Jeffrey's idea was to have more discussion...

defer to plenary where we can have Jeffrey

Comment by @jyasskin Sep 4, 2024 (See Github)

This isn't a TAG opinion, but I wanted to point out that there's a known gap across the whole platform in the ability of IDREF attributes to refer to what authors want: https://github.com/w3ctag/gaps/issues/2. I know that this particular gap in ARIA references has lingered for a very long time, and I don't want to delay getting it fixed, but it's also important to keep looking for a unified strategy to deal with all of these relation types.

Comment by @jyasskin Sep 12, 2024 (See Github)

We discussed in the TAG meetings this week, and while we haven't reviewed Phase 2 yet, we're in favor of you moving ahead with solving the piece of this (Phase 1) that you have general agreement on. We'll continue reviewing Phase 2 in future weeks.

Comment by @LeaVerou Sep 25, 2024 (See Github)

Slides from TPAC breakout: https://onedrive.live.com/edit?id=3C830C8020C680D9!497341&resid=3C830C8020C680D9!497341&ithint=file%2cpptx&authkey=!AIbmxScKhLFKgQ4&wdo=2&cid=3c830c8020c680d9

Discussed Oct 1, 2024 (See Github)

Matthew: still catching up with discussion from TPAC

Discussed Oct 1, 2024 (See Github)

Jeffrey: [summarizes the TPAC discussion from https://www.w3.org/2024/09/25-webcomponents-minutes.html#da4f] We should endorse the first phase, and maybe we can help with the design for the second phase where they have some open questions.

Matthew: +1. Thinking about prior art for phase 2.

Jeffrey: I was surprised that we don't want to follow the data-* pattern, but there seems to be consensus against using hyphens. I'll also find the minutes from TPAC.

Matthew: Think about how this affects the general gap on IDREF in shadow trees.

Peter: Is it right to put the property on the shadow root, or better to put it on the target element inside the shadow root? Seems like it might be better ergonomics if the right target inside the shadow tree needs to change, since you'll probably be updating that element's attributs anyway. Could expose the map at the root as readonly.

Matthew: This might be consistent with the rest of ARIA, where relations are sometimes backwards from HTML.

Peter: That's a mistake in ARIA.

Matthew: Doesn't make this comment invalid; it might just apply more generally. Although it has helped implementation for assistive technology.

Peter: If the browser is helping out, it can expose the right accessibility tree.

Matthew: Should we file a more general issue?

Peter: Start with posting the comment here.

Peter to draft the comment.

<blockquote> Have you considered reversing the relationship of the shadow root to the target node? For example, rather than having the developer set the `referenceTarget` on the shadow node, introduce a `aria-referencetarget` (or some equivalent) attribute which is set on the target node? The shadow root can still have a `referenceTarget` attribute but it's read-only and can be a reference to the actual node.

I'm thinking of the developer ergonomics, for instance if I'm building a select-type control, in the current model I have to set some attribute (probably a class) on the currently selected node to indicate its selected state and trigger style changes, and then I have to also add an id and set the aria-activedescendant in the reference map on the shadow root. In my proposal, the developer would only need to set the aria-activedescendant attribute on the active node (which can also be used as the style trigger) and the reference map can update automatically. This also eliminates the need for ids on everything.

</blockquote>

Posted to https://github.com/w3ctag/design-reviews/issues/961#issuecomment-2411812056

Discussed Oct 1, 2024 (See Github)

Not enough information and not the right people.

Discussed Oct 1, 2024 (See Github)

Jeffrey: the comments gives some reason to give it the way they did - and that it aligns with ARIA... Is that convincing?

Lea: to some degree... We have a principle about consistency... and precedent... no right answer... that's what we have here. Being consistent with a precedent is reasonable. However this isn't just about ARIA .. but any id ref in HTNL... we want this to work down the line... e.g. through slectors or names... Should this just be ID focused? I won't push back if we think we're fine with it. It's an important issue to solve that is holding WC back.

Peter: generally agree - i do want to push back on "ARIA works this way" - ARIA could be improved.

Jefrey: let's invite them to come talk to us?

Dan: breakout A the next week?

jeffrey to reach out

Comment by @plinss Oct 14, 2024 (See Github)

Have you considered reversing the relationship of the shadow root to the target node? For example, rather than having the developer set the referenceTarget on the shadow node, introduce a aria-referencetarget (or some equivalent) attribute which is set on the target node? The shadow root can still have a referenceTarget attribute but it's read-only and can be a reference to the actual node.

I'm thinking of the developer ergonomics, for instance if I'm building a select-type control, in the current model I have to set some attribute (probably a class) on the currently selected node to indicate its selected state and trigger style changes, and then I have to also add an id and set the aria-activedescendant in the reference map on the shadow root. In my proposal, the developer would only need to set the aria-activedescendant attribute on the active node (which can also be used as the style trigger) and the reference map can update automatically. This also eliminates the need for ids on everything.

Comment by @dandclark Oct 15, 2024 (See Github)

That sounds like this considered alternative: https://github.com/WICG/webcomponents/blob/gh-pages/proposals/reference-target-explainer.md#designate-target-elements-using-attributes-instead-of-idref, which @westbrook proposed in more depth in https://github.com/Westbrook/cross-root-aria-reflection/blob/main/cross-root-aria-reflection.md.

IMO the developer ergonomics are similar in the use case you mentioned. They way I would handle the aria-activedescendant case with reference target is that I'd have the shadowRoot's referencetarget or referencetargetmap assign aria-activedescendant to a constant id, e.g. #active, which is also used to apply active styles to the node. And then when the activedescendant changes, I change which element has that ID.

<input role="combobox" aria-activedescendant="fancy-listbox"/>
<fancy-listbox id="fancy-listbox" >
  <template shadowrootmode="open" shadowrootreferencetarget="active">
    <style>
      #active { /* styles for the active element go here */ }
    </style>
    <option>One</option>
    <option id="active">Two</option>
    <option>Three</option>
  </template>
</fancy-listbox>

If the user selects another option, the only thing the dev needs to change is to clear the ID from option Two and add it to the new option. This is the same amount of state that would need to be changed if reference target was set using a property on the targeted element.

Semantically, it also feels to me that the choice of whether or not to delegate a given attribute is something that should be set on the shadow root, akin to delegatesfocus, which maybe is why @westbrook's proposal used both a property on the shadow root and a property on the targeted element.

Comment by @Westbrook Oct 15, 2024 (See Github)

When discussing this at length with the AOM Working Group, the WCCG, and implementors, a primary shortcoming for an attribute marker being applied to an element within the shadow DOM is that in order for the API to scale over time to support mapping more than one attribute to more than one element you needed to inform the shadow root that it would be delegating a value and to which element the value would be delegated. If the delegation/reflection APIs were to stop at a one to one pass through, so no support for something like referenceTargetMap, which is a powerful part of this API (or the Level 2 of this API?), then it might make sense to have such a marker hold all the values applied from outside of the shadow root. However, many implementors were dissatisfied with API that could not scale in some way to support more complex situations, in fact a number of seemingly promising proposals in this area have been overrule for lack of this ability. This become more and more apropos as referenceTarget has expanded to properly support non-aria attributes like the Popover API, Invoker Commands, and other more recent attribute bound interactions which are even less likely to receive a "batch" handoff of attribute values.

Comment by @LeaVerou Oct 22, 2024 (See Github)

@Westbrook the design @plinss is proposing doesn't seem to conflict with that at all. Instead of having a map, you'd have an attribute on the element whose values are the delegated attributes.

I don't feel super strongly about it, but I do think this would be a superior closeness of mapping and thus, provide improved DX, whereas referencing ids is an indirection.

Comment by @Westbrook Oct 23, 2024 (See Github)

I do think this would be a superior closeness of mapping

@LeaVerou when you use "this" here, which approach are you referencing?

Comment by @jyasskin Oct 24, 2024 (See Github)

I believe @LeaVerou's preferring attributes on the nested elements, since you write

  <template shadowrootmode="open">
    <button defaultReferenceTarget></button>
  </template>

or

  <template shadowrootmode="open">
    <button referenceTargetForAttributes="for"></button>
  </template>

instead of

  <template shadowrootmode="open" shadowrootreferencetarget="target">
    <button id="target"></button>
  </template>

or

  <template shadowrootmode="open" shadowrootreferencetargetmap="for: target">
    <button id="target"></button>
  </template>

https://github.com/WICG/webcomponents/blob/gh-pages/proposals/reference-target-explainer.md#designate-target-elements-using-attributes-instead-of-idref says you'd need to also put an attribute on the shadow root to enable this, but why would you need that?

It also says that the multiple elements use case, for example for aria-labelledby, can't handle reordering, which is an interesting case that we didn't think about. @LeaVerou / @plinss, how do you think that should be handled? I don't know the whole list of attributes that need this support, but guessing wildly, maybe it'd help to lift the restriction that "The aria-labelledby property cannot be chained" when it's set on the shadow root?

Comment by @dandclark Oct 24, 2024 (See Github)

I think the ability to easily control the ordering is a solid reason why having a central attribute or set of attributes on the shadow root is valuable.

Developers are used to building an accessible name using a list of IDs with a particular ordering in an aria-labeledby attribute, which maps well to using a list of IDs in a shadowrootmap attribute on the shadow/template. It would be more surprising to have to rely on DOM order or a chaining of aria-labeledby that fails in other contexts.

Comment by @LeaVerou Oct 25, 2024 (See Github)

What @jyasskin said, except I was envisioning the common pattern of using a boolean attribute for the default behavior + values to customize the default behavior, not separate attributes. Also, for the love of all that is good, let's please avoid HTML attributes with 3+ words, since given the convention of not using a separator, they are incredibly hard to read.

@dandclark Could you elaborate, ideally with a code example? I can't understand how this relates to specifying which element a certain idref is delegated to. How does order matter in that? Unless you mean if multiple mappings are specified for the same attribute, which seems like an edge case and not something you design the API around.

Comment by @jyasskin Oct 25, 2024 (See Github)

@LeaVerou Check out how aria-labelledby and aria-describedby can say that an element is labeled or described by multiple other elements. It is an edge case, but it has to be supported somehow.

I'm tempted to say that it should be supported by letting those particular properties recurse, at least at the shadow root, and then you don't need anything like reference-target-map to deal with them. But I don't know enough about ARIA to be confident of that.

Comment by @Westbrook Oct 25, 2024 (See Github)

As for DX

In that the current DX for associating things accessibly is ID references:

<label for="input">Label</label>
<input id="input">

Isn't requiring a new syntax to associate things accessibly just because they are inside a shadow root worse DX?

<label for="input">Label</label>
<my-input id="input">
  <template shadowrootmode="open">
    <!-- because I'm in a shadow root, my ID is no longe enough 😢 -->
    <input id="input" defaultReferenceTarget>
  </template>
</my-input>

As opposed to only needing to address the transport layer via:

<label for="input">Label</label>
<my-input id="input">
  <template shadowrootmode="open" shadowrootreferencetarget="target">
    <!-- same DOM in or out of the shadows 🥳 -->
    <input id="input">
  </template>
</my-input>

For the work I've been a part of, the ability to factor components in and out of shadow roots without needing to change much, if any, of the DOM structure and attributes feels like a big win for maintainability of the work developers are doing here.

Mixed attributes

A further issue with the consumption of multiple DOM internal attributes is their unexpected collision.

While I'm not sure the current answer to which wins here:

 <template shadowrootmode="open" shadowrootreferencetarget="target" shadowrootreferencetargetmap="for: other">
    <button id="target"></button>
    <button id="other"></button>
</template>

Or here:

 <template shadowrootmode="open" shadowrootreferencetargetmap="for: other" shadowrootreferencetarget="target" >
    <button id="target"></button>
    <button id="other"></button>
</template>

IIUC the order doesn't matter because you set a default and then override specific values with the map. These values are all in the same place, parsing can happen in one place, groking can happen in one place, etc.

However, traditionally attribute relations resolve first in DOM order, e.g. the label focuses input "one" when clicked:

<label for="input">Label</label>
<input value="one" id="input" />
<input value="two" id="input" />

So if you rely on attributes across the DOM, which wins?

<template shadowrootmode="open">
    <button defaultReferenceTarget></button>
    <button referenceTargetForAttributes="for"></button>
</template>

defaultReferenceTarget comes first, do we expect the parser to now scan the fully available structure to see if something supersedes it? If not would the relationships be different with:

<template shadowrootmode="open">
    <button referenceTargetForAttributes="for"></button>
    <button defaultReferenceTarget></button>
</template>

Needing to parse the whole tree to clarify the accessible relationship seems like it would change the default performance aspects of doing so. That would get worse as the shadow tree grew, and at the same time as the shadow tree grew it would be much more likely that these two attributes are far apart in that tree making it more difficult to keep (or to acquire) their relationship in your mind at dev time. This pushing of the accessible relationship transport layer could make it surprising and less clear what is happening when these relationships cross between DOM trees raising the likelihood of an accessibility feature lowering the accessibility of the features built with it.

Comment by @LeaVerou Oct 25, 2024 (See Github)

@jyasskin @dandclark

@LeaVerou Check out how aria-labelledby and aria-describedby can say that an element is labeled or described by multiple other elements. It is an edge case, but it has to be supported somehow.

I know that, but it seems like as long as each of these ids is delegated properly, this should just work? Oh I see, you're saying you want to delegate each reference to the CE to multiple elements within its shadow root. In that case, the approach of specifying the attribute on the element seems to work even better: for attributes only taking a single idref the first one wins, for attributes taking multiple, all elements declaring a mapping are taken into account. You both seem to be saying that the order of ids is significant, but I can't find anything in the ARIA spec that says so, are we sure that's the case?

Comment by @LeaVerou Oct 25, 2024 (See Github)

@Westbrook As a meta comment, I think we may have run into the classic big WC use case bifurcation, where some folks are talking about templating use cases and others about packaging up independent bits of functionality for broader reuse, and these use cases have vastly different characteristics so people are coming to these discussions with completely different contexts.

For example, wth most of my context being the latter, moving elements in and out of shadow roots is something I have never considered, since most components I work on are meant to be used more broadly than the pages that include them, and are managed by different people.

That different context might also explain this question:

Isn't requiring a new syntax to associate things accessibly just because they are inside a shadow root worse DX?

I could be wrong, but reading between the lines, it seems like to you this is mainly about overcoming a hurdle in how you define your ARIA attributes; since it's the same person/team writing the mappings for the light DOM and the component, the encapsulation is just a roadblock, they'd rather reference the shadow DOM element directly if they could. If that is correct, perhaps the real solution these use cases need is a general way to reduce encapsulation.

For the second set of use cases though, this is defining a way for the root element to specify which of its shadow DOM descendants certain things should be delegated to, and is largely independent of how exactly the mapping happened in the first place. E.g. if in the future we add an attribute that relates one element to another via a relative selector, the delegation should still work the same. So to me, it seems entirely separate than the idref mechanisms currently used, the actual design goal is to design a good syntax for delegation.

It doesn't help illustrate what @plinss and I are saying that in your code examples, elements already have ids, so it begs the question, why not just use the ids? However, what Peter and I were saying, is that ideally, you shouldn't need to assign ids if you don't need to for another purpose.

But regardless of the syntax we go with, I would fully expect more specific mappings to override more general mappings, rather than just resolving them based on order.

Comment by @Westbrook Oct 25, 2024 (See Github)

I'm not sure I see a practical difference between the two contexts that you're referencing @LeaVerou. While the code above is a very reduced reproduction, the workflow I outlined applies to the work I've seen or been a part of across a number of roles I've worked in. Whether building Spectrum Web Components where we commonly worked to translate examples like those found in ARIA APG into abstract reusable/customizable element or building interactive virtual classrooms, digital asset management or creativity tools that rely on larger custom element architectures, I see this approach being valuable in the way that it manages ID references much like they would be used without shadow DOM.

But, maybe I'm misunderstanding the context you're outlining. Would you be able to share some code outlining how that context would be different? Something that helped clarify how this context would benefit from managing ID references via this net new API surface?

Comment by @michaelwarren1106 Oct 29, 2024 (See Github)

i don’t really have a strong preference for either api as long as something moves forward that helps. but i do think the perf aspect of the two approaches is important to consider.

if the map of els needing ids to shadow dom ids is established on the shadow root/host itself, then the browser doesn’t have to fully parse the shadow root template in order to finish calculating the references. the browser would have the complete map upfront as soon as the shadow root is constructed. likewise, the mapping being on the root doesn’t change the performance of calculating the map as the size of the shadow dom grows. the map will always be in a single spot which may mean that browsers can optimize for it since it’ll be known and predictable.

i do think that the attrs establishing IDREF mappings being on the SD child elements themselves is a more flexible api, but i would be interested in how big the perf tradeoff is for having to parse the whole SD template in order to assign refs.

possibly the perf impact would be mitigated by assigning each IDREF when each attr is encountered, and overwriting the assignment if a conflicting attr is encountered later on. but then i’d worry about the impact of the assignment changing. if i were the kind of dev writing uber-optimized framework code i might balk at references changing mid-parse?

Comment by @LeaVerou Oct 29, 2024 (See Github)

@michaelwarren1106 This can be mitigated by simply using the first instance of the attribute (for references of the same scope). Even with ids the association can change — one can simply remove an element with that id and add a different one, or modify ids at runtime, so that doesn't seem different?

Comment by @michaelwarren1106 Oct 29, 2024 (See Github)

@michaelwarren1106 This can be mitigated by simply using the first instance of the attribute (for references of the same scope). Even with ids the association can change — one can simply remove an element with that id and add a different one, or modify ids at runtime, so that doesn't seem different?

Perhaps, but I'm not sure I know of anything else that changes ref like that during tree parsing? Implementers would be better to weigh in on the pros/cons of that approach, but I would suspect that changing the ref during the parse is wholly different than changing the ref as a result of manipulating the dom.

And actually, I think that manipulating the dom is another perf lever where the "having to parse the whole dom in order for refs to settle out" is maybe not the best. What if the dom structure changes, but the ref map doesnt? If im moving elements around in the dom, but not changing their IDREF relationships to their for= attributes? If the mapping is on each individual element its going to need to be recalculated EVERY time the dom changes, not just the first time.

But if the ref/mapping is set on the shadow root itself, then that frees up the dom to be changed without the IDREFs needing to also be recalculated. And vice-versa, the IDREFs could be changed (potentially, I havent seen any imperative feature that would do that) without being dependent on the dom structure.

Having talked myself into it just now, it seems like disconnecting the actual DOM from the IDREF mapping metadata seems like a good thing to do even if its slightly worse DX? slightly worse DX for way better performance is a tradeoff many people would make I would think.

Comment by @nolanlawson Oct 31, 2024 (See Github)

One potential issue I can see with the non-id approach is that it makes changing the active descendant in a combobox two steps instead of one.

For example, in the current proposal (omitting some details for brevity):

<input role="combobox" aria-activedescendant="listbox">
<fancy-listbox id="listbox">
  <template shadowrootmode="open" shadowrootreferencetargetmap="aria-activedescendant: one">
    <div role="listbox">
      <div id="one" role="option">one</div>
      <div id="two" role="option">two</div>
    </div>
  </template>
</fancy-listbox>

To update the active descendant from one to two is just a matter of updating the shadowrootreferencetargetmap (or IDL equivalent) to point at two instead of one. This is similar to what you might do without shadow DOM – update aria-activedescendant to point to a different ID.

Whereas with the referenceTargetForAttributes counter-proposal:

<input role="combobox" aria-activedescendant="listbox">
<fancy-listbox id="listbox">
  <template shadowrootmode="open">
    <div role="listbox">
      <div role="option" referenceTargetForAttributes="aria-activedescendant">one</div>
      <div role="option">two</div>
    </div>
  </template>
</fancy-listbox>

Now the update would require 2 operations: 1) removing referenceTargetForAttributes from the first list option and 2) adding it to the second.

IMO this isn't a deal-breaker, but given that the first proposal is fewer operations and closer to the non-shadow DOM equivalent, the first would be my preference.

(BTW this combobox likely requires shadowrootreferencetargetmap rather than simply shadowrootreferencetarget because you'd likely also need an aria-controls pointing at the listbox.)

Comment by @Westbrook Oct 31, 2024 (See Github)

Would the use of defaultReferenceTarget or similar fail when moving from out "cannonical" example:

<label for="input">Label</label>
<my-input id="input">
  <template shadowrootmode="open">
    <input id="input" defaultReferenceTarget>
  </template>
</my-input>

To one that took multiple targets, as found here:

<form id="example-form">
  <input type="range" id="b" name="b" value="50" /> +
  <input type="number" id="a" name="a" value="10" /> =
  <output name="result" for="a b">60</output>
</form>

I will admit, it is a bit contrived, but were this factored for reusability via the following, would "simply using the first instance of the attribute" be enough to make this work as expected?

<form id="example-form">
  <calc-el>
    <template shadowrootmode="open">
      <input type="range" id="b" referenceTargetForAttributes="for" value="50" /> +
      <input type="number" id="a" referenceTargetForAttributes="for" value="10" /> =
    </template>
  </calc-el>
  <output name="result" for="a b">60</output>
</form>
  • is this not expected/intended to be covered?
  • did multiple targets via the map get covered?
  • could there be multiple default targets in either syntax? 👀
    <form id="example-form">  <calc-el>    <template shadowrootmode="open">      <input type="range" id="b" defaultReferenceTarget value="50" /> +      <input type="number" id="a" defaultReferenceTarget value="10" /> =    </template>  </calc-el>  <output name="result" for="a b">60</output></form>
    
Discussed Nov 1, 2024 (See Github)

Peter: People have a bunch of opinions.

Dan: Steve Orvell has summarized the tradeoffs: https://github.com/w3ctag/design-reviews/issues/961#issuecomment-2457667230. Somewhat aesthetic. Main concern is about re-ordering, for example for aria-labelledby. We lose that if we push the information down to elements. I don't have a good sense how much developers would miss this.

Alice: Comes down to ordering. Steve's table was useful, but ordering is the only place where it's not just convenience. TL;DR comment: https://github.com/w3ctag/design-reviews/issues/961#issuecomment-2456465629. When is this actually going to be used in practice? For what attributes? We're designing a general solution without understanding the specific problems it's trying to solve. Looking through the problems in my comment, if we can solve the naming problem, the ordering question doesn't seem to actually mention. There are lots of attributes that do take lists of id references. Do we need to support those use cases?

Lea: One of the considerations to keep in mind is that this is not just about ARIA or labels. It's about anything that takes element references. The list of things like that keeps increasing. popovers, invokerrs, input lists. Anchor attribute. These have very different characteristics w.r.t. which shadow dom element you need to refer to?

Alice: Specific examples?

Lea: Not off the top of my head. Do you ever need a datalist inside a component? Need to think more to find specific examples.

Dan: I've written tests where you have a popup inside a shadow dom, and want to trigger it from outside. Don't know if people actually want this?

Westbrook: The idea that it's growing, regardless of the use cases, is a reason not to use new attributes. Every time another group wants to add an ID reference, they'll need a reflected attribute. We'd be setting everyone up to fall into gaps. With popover, we know the context, but the next one won't have it. Want to solve the general problem.

Lea: I don't have a strong opinion. Slightly favor attribute approach. I think the id approach is reasonable, and it has many things going for it. Much easier than the general idref problem. In shadow roots, you assign a fair number of IDs anyway. There are arguments for both. If we have an attribute on the shadow root, want to iterate a bit more on the syntax. Also want to make it work with direct element references. Whatever solution should allow web components to extend the set of idref attributes.

Alice: I want to understand the specific problems we're trying to solve. Think if you scope the problem to just referenceTarget (after bikeshedding), a lot of the distinctions between the two syntaxes disappear, because you lose the ordering problem. Really depends on whether we can justify not doing any kind of mapping.

Dan: I have that feedback. "Please solve the basic referenceTarget problem; dont' get delayed by the mapping problem."

Alice: Would be really nice to know what the more problems are.

Dan: Want to do an origin trial of the basic referenceTarget case. Haven't implemented referenceTargetMap yet. One option is to move forward with that, and we can prod the partners to ask if they'd be happier with the attribute on the elements. You get better responses if people can ship this in production.

Alice: Element references: I think that's explicitly scoped out, having an element reference version of referenceTarget. Not sure if the explainer says anything about an element reference from shadow dom pointing [?] For activedescendant, you have to change things all the time, so an element reference might be nice to have. Solve the basic "there's an input inside the root that needs to substitute for the root". activedescendant is important, but it's niche. I liked Jeffrey's suggestion to let aria-labeledby work when it's on a shadow root. The problem with daisy-chaining might be that you want an input to have a value, but the value becomes part of the label. And the input has its own label, but you want its value to act as the label for something else.

Jeffrey: And that problem doesn't happen if you only allow labeledby to recurse only when it's set on the shadow root.

Peter: Main concern is the mechanics of using IDs to reference everything. When you're dynamically generating component content, assigning IDs is hard. ARIA requires IDs for almost everything. Want to see a path to fix that. Try to reverse the relationship and let the browser help out. Want to improve the DX.

Lea: +1 to Peter. IDs are painful, but this might not be the place to solve it. I wrote an essay about this. But wouldn't want to block referenceTarget on this. What if we don't do either. Specify stuff on shadow root. Instead of just specifying IDs, use selectors that are scoped to shadow root.

Westbrook: Interesting to avoid needing IDs, even if it's hard to manage or keep track of them. Web Components community wants to keep ARIA working. But I'm also excited to share thoughts on how we can expand on those relationships in the future. Wouldn't want to invent new selectors to let web components devs catch up to non-components development.

Peter: In general I'm sympathetic. We see proposals that are inconsistent with some othe rthings, but it matches years of precedent. "Chart a new path" makes people boil the ocean. Counterpoint is that we have an established path that we're trying to get away from, and we keep adding to that path, we create more inertia.

Jeffrey: We don't yet have a plan. The plan isn't just "selectors" since we also need to be able to go up the tree. We should make a plan and then we'll need to upgrade the existing aria-* attributes anyway. referenceTarget and referenceTargetMap (spelled however) will probably be able to upgrade the same way. So I'm leaning toward saying to go ahead with referenceTarget as it's currently designed, with IDs, and then we fix everything at once.

Dan: Use an id on the shadow

Alice: +100 to Jeffrey. When it comes to designing the new system, you need to be able to go up or sideways. Collect specific use cases. It's a different things to get aria-labeledby where maybe you want to reverse a name. You need to think about the cases you need. 1 thing vs multiple things. Collect the cases of the attributes we have currently. As opposed to thinking of them as a general problem.

Lea: I like the idea of "let's solve this problem now, and pave the way for solving the general problem later." What makes that easier? Microsyntax of referenceTargetMap -> separate attributes. Attribute for each mapping. Then the attributes have the same syntax as the bound attirubtes, and upgrading them works the same as upgradinng ARIA.

Jeffrey: Think that came up at TPAC.

Dan: People were wary of microsyntax at TPAC. (https://github.com/WICG/webcomponents/blob/gh-pages/proposals/reference-target-explainer.md#use-separate-attributes-for-each-forwarded-attribute) Still wanted to put it on the shadow root.

Lea: Pending TAG principle against inventing microsyntaxes. Maybe helps forwarding for certain attributes work before others. Huge disparity. Want to delegate for the most. Maybe that could advance independently.

Westbrook: I heard also the feedback on microsyntaxes. Anything specific about the microsyntax that would stop it from working like attributes. Link about crawling up and down DOM. It was frowned upon by lots of people.

Lea: Not about browser internals. More syntax implies that new syntax is hard to introduce w/o introducing parsing ambiguity. w.r.t. #204, maybe this particular naming scheme isn't the only possible one?

Alice: To clarify one thing about exportid: the syntax is noisy, but when you have a substitute element within the shadow root, the syntax was horrible. Every time you want to refer to the real input, you have to use hostid::realid. This wasn't meant to stand on its own. It was a stepping stone toward a better coordination thing; would let you prototype.

Jeffrey: I think we've converged on saying "go ahead with referenceTarget, and we'll work on fixing ARIA in parallel."

Peter: Let's just be careful not to paint ourselves into a corner. Don't want to fix all of ARIA before shipping this. But do think about what the future shoul dlook like and avoid preemptin ghtat. Maybe we delay parts of this, but get the most important parts out.

Lea: Re "delegatesReferences" looks good, not a good use of call time to bikeshed, but if we use multiple attributes, we need a short base name plus suffixes. So the base needs to be very short.

Tess: "delegates" might be an ok name since it copies delegatesFocus.

Peter: Not a fan of over-abbreviating things. Spell it out. But if it shows up 1000 times in a document, that's bad too. As we think about the future, I'd like to be able to take references to DOM nodes. And have that expressible as attributes, so it can be serialized. But as a developer I don't want to think about all the attributes. Think of DX first.

Matthew: To clarify, my understanding is that phase 1 seems good, but phase 2 we want to keep thinking?

Peter: And about it being dynamically generated by the browser.

Lea: Ergonomics is important, but since time is finite, let's make things possible now and easy later. e.g. imperative API later. As a data point, don't know if it reflects the broader set of use cases. My engineers said they were happy with just referenceTarget.

Alice: That is the most common case. The 2 other cases from my post are real cases. Maybe not having a map, but solutions to specific problems. e.g. computed name and some potential ideas for activedescendent. Maybe those cases can be solved in a better way than a map.

Jeffrey: Who's going to work on fixing ARIA?

Alice: That sounds like something to talk to ARIA about. They have a lot of work.

Lea: I would frame it as fixing IDREFs, rather than fixing ARIA.

Peter: Browser vendors care more now, so maybe we can get things that we couldn't have gotten 10 years ago. Would like someone to step up and go think about these things. We shouldn't say "someone should fix things" without following it up.

Jeffrey: Task force? We need to identify a person who will lead the taskforce, and they can recruit people from the right communities. But nothing happens without that leader, and we need to avoid blocking things if we can't volunteer someone to run the task force.

Peter: AI for the TAG.

Dan: We'll go ahead with the origin trial. I'll ask about better names, if they prefer putting it on an element, what other use cases this doesn't cover.

Lea: Westbrook's explainer document has some use cases.

Westbrook: It almost always goes back to the combobox pattern. We have to write web components the hard way, so we don't even run into this wall. Not sure an OT will be enough to get us to the use cases we want to see. As soon as production systems can use this, we'll find use cases.

Dan: Very interested in the ID problem. Reluctant to sign up to drive it today, but want to think about driving it.

Task force members but not leaders: Dan Clark + Alice + Matthew

Discussed Nov 1, 2024 (See Github)

we review a draft comment

*bumped to plenary with a proposed closed

Lea: I would also remind them of the issue "let's find a better name for this" but I'm fine with closing... That was raised in our discussions. Other thoughs: if we go fwd with the ID syntax - how do you specify something to be fwded to the element itself...? Plan for the map is to set defaults - and override for specific attrubutes. But doing one off overriding but not forwarding something, that's not possible...

Dan: we should ask that. And not close yet.

Discussed Nov 1, 2024 (See Github)

Matthew: lea had the question... looks at answer we got to lea's previous question from Alice - satisfied. Looks like now is the time to post the comment we had drafted.

consensus to close with Matthew's draft comment

Peter: we talked about spinning up some kind of task force .. on ID ref. What are next steps?

Matthew: I think it's an important problem

Jeffrey: we need someone to lead the effort - and so far we don't have the time...

Peter: indicates we wait for the result of the election .. or look for someone outside the TAG - neither of which we have to do today. But we should track this. Maybe open an issue...

Matthew: there's an issue in the gaps repository. We can put a comment on there.

Matthew: posts closing comment

closed

Comment by @dandclark Nov 1, 2024 (See Github)

Addressing @LeaVerou's comment above:

I know that, but it seems like as long as each of these ids is delegated properly, this should just work? Oh I see, you're saying you want to delegate each reference to the CE to multiple elements within its shadow root. In that case, the approach of specifying the attribute on the element seems to work even better: for attributes only taking a single idref the first one wins, for attributes taking multiple, all elements declaring a mapping are taken into account. You both seem to be saying that the order of ids is significant, but I can't find anything in the ARIA spec that says so, are we sure that's the case?

For things like aria-labelledby order does matter. See Step 2B of the accessible name computation steps where the IDs in the attribute are processed in order to build up the accessible name.

For example:

<input aria-labelledby="b a">
<div id="a">two</div>
<div id="b">one</div>

The accessible name of the <input> will be "one two". So developers are accustomed to being able to control order by reordering IDs in the attribute value, without depending on DOM order.

@jyasskin said:

@LeaVerou Check out how aria-labelledby and aria-describedby can say that an element is labeled or described by multiple other elements. It is an edge case, but it has to be supported somehow.

I'm tempted to say that it should be supported by letting those particular properties recurse, at least at the shadow root, and then you don't need anything like reference-target-map to deal with them. But I don't know enough about ARIA to be confident of that.

Changing the behavior of labelledby-describedby recursion within shadow roots seems like a recipe for confusion. There is some evidence that developers expect and depend on these properties not chaining. Rather than adding another element of complexity to labeledby/describedby by making them behave differently depending on context I'd prefer to just go with the version of ReferenceTarget that allows reordering these in the way that developers are already accustomed to, by ordering IDs in an attribute list.

Comment by @alice Nov 5, 2024 (See Github)

@Westbrook's example I think illustrates why this problem gets very hard for me to reason about from a general perspective when there's not a 1:1 mapping between the shadow host and the reference target.

Even leaving aside the syntax, I would struggle to reason about what should happen in a case like this:

<form>
  <label for="sum">Equation</label>
  <calc-el id="sum">
    <template shadowrootmode="open">
      <input type="range" referenceTargetForAttributes="for" value="50" /> +
      <input type="number" referenceTargetForAttributes="for" value="10" /> =
    </template>
  </calc-el>
  <output name="result" for="sum">60</output>
</form>

So, I've tried to think about specific cases instead. There are three types of non-contrived cases for this type of API I am reasonably confident I can get my head around (thanks to the folks who have patiently explained the latter two to me across multiple occasions):

  1. Something like<fancy-input>, where there is an <input> inside <fancy-input>'s shadow root which could reasonably substitute for the host <fancy-input>. (I'm using <input> as an example partly because of the number of ways <input>s can relate to other elements, but obviously it's not the only element which is or could be wrapped this way.)
  2. Needing aria-activedescendant (specifically) to refer to a list item which needs to be inside a shadow root in order to encapsulate certain implementation details, such as a recycler view and/or list items being loaded dynamically, when implementing an accessible combobox.
  3. Needing a component to have a computed name, for aria-labelledby/aria-describedby purposes, which doesn't include all of its contents, without excluding from the accessibility tree the content which should be excluded from the name. One example might be a contact chip including a profile picture, a name and a "remove" button, where the contact chip can be used as a label for something else: the profile picture and the remove button should be accessible, but not a part of its computed name. (And, in a slightly contrived case, you might like to list the surname first in the component, but have the computed name list the given name first; why not.)

For (1), I think the current referenceTarget design matches the way I think about the problem: the shadow root encloses some element which could generally substitute for the shadow host other than some encapsulated fanciness. It makes intuitive sense to me that the shadow root should be responsible for redirecting references to allow that substitution to occur whenever another element refers to the host.

That said, I don't necessarily have any objection to having an attribute on the substitute element instead; I can't see any major practical downside of either design for this case, particularly if you can retrieve the reference target from the shadow root for debugging.

For (2), we're arguably getting an upgrade on the status quo regardless of syntax, since the listbox no longer needs to coordinate with the <input> to tell it what its active descendant should be (although it's a bit unfortunate that the <input> still needs to have both an aria-activedescendant attribute and an aria-controls attribute which just point to the same element).

Since aria-activedescendant can only ever point to one element, there's not really a major downside to having an attribute on the element, since there's no ordering issue. So I think for this case, again, there's not a lot in between the two syntax options.

For (3), I do wonder like @jyasskin whether this is the best approach at all. I understand that there is a general expectation around how aria-labelledby will behave, but that expectation is already broken if you're pointing to the shadow host and getting something other than all of its contents as its computed name. So I do wonder whether we could allow aria-labelledby on the shadow root for this case.

And if we really can't come up with a case other than (2) for the reference map, I wonder likewise whether we could find a way to improve on this case in isolation.

Comment by @LeaVerou Nov 5, 2024 (See Github)

If we go with the current id-based syntax, I wonder what ergonomics improvements we could make:

  • "referenceTarget" is an incredibly obscure name. We already have delegatesFocus. What about delegatesReferences, delegatesId or even just delegates?
  • Do we really need two attributes? What if the attribute syntax allowed for specifying a catch-all value, a map, or both? Then a single attribute contains all the information needed to delegate references.
Comment by @sorvell Nov 5, 2024 (See Github)

Not sure what I prefer but trying to think it through, I think this captures most of the points mentioned but happy to correct or amend as needed.

ARIA Info on Advantages Disadvantages
ShadowRoot 1. Centralizes new syntax/API 1. Hard to name things explicitly
2. Uses familiar ID syntax 2. Brittle to match names in two places
3. Easily supports ordering 3. Complex syntax for mapping
4. Dynamism might be simpler 4. Harder to solve general reference problem
Elements 1. Avoids need for explicit names 1. Unclear how to support ordering
2. Avoids need to coordinate names 2. Impact of introducing new global attributes
3. Easy to author changes 3. Novel solution may introduce cognitive load
4. Misses chance to solve general naming problem
Comment by @LeaVerou Nov 5, 2024 (See Github)

Thanks for the summary @sorvell! I'd add to the disadvantages of the current approach that it makes it harder to solve the referencing problem in HTML.

Comment by @LeaVerou Nov 5, 2024 (See Github)

One option that has not been explored: using relative selectors. It both solves the ordering issue and alleviates the need for naming. People who want to use ids can still use ids by just doing #foo, but people who would rather not manage ids, can just specify a selector describing their intent. Could this be the way to have our cake and eat it too?

Comment by @LeaVerou Nov 11, 2024 (See Github)

Another issue we realized: Right now, the design for the whole feature is that an attribute specifies the defaults, and another one-off overrides on an attribute by attribute basis. If no default forwarding is specified, no forwarding happens (i.e. the reference resolves to the element itself). However, the pattern where everything gets delegated to a certain shadow DOM element but some attributes don't get forwarded at all is not supported at all with the id-based syntax. That is also something a selector-based syntax could address, as then :host could be a delegation target as well (= no delegation).


Also, from conversations with folks, the initial reaction for phrase 1 is "yes, forwarding everything to the same element makes sense, all our use cases are like that", then I remind them about the non-a11y idrefs such as popovertarget or form they are way less certain…

Comment by @alice Nov 18, 2024 (See Github)

One issue with using selectors in place of IDs is that selectors don't come with the same constraints as IDs. In particular, almost anything about the element can affect whether it matches a selector, and many selectors can match any number of elements.

This would be an issue for things like aria-activedescendant, which need to match exactly one element, and which need to trigger live updates to the accessibility tree. We could certainly specify that only the first element which matches the selector should be used (like we do in the case of multiple elements with the same ID), but triggering the live updates may become very costly, since whenever anything changes within the relevant tree, we'll essentially have to run querySelector() to check whether the active desendant has changed.

Comment by @matatk Nov 18, 2024 (See Github)

Thank you for your time earlier in the week. As per our discussion, we are happy to see your work on phase 1 progress. It will be great to resolve this significant use case! Please let us know as and when we can provide input on phase 2. We do urge the authors to take the time to find a shorter, more broadly understandable name.

We also discussed the wider, and fundamental, issue of finding an alternative to IDREFs for expressing relationships between elements - we intend to explore this issue separately, and your feedback would be welcome on that thread. Should we set up a group to work on this issue, we will let you know.