-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reference Target #961
Comments
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: w3ctag/gaps#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. |
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. |
Have you considered reversing the relationship of the shadow root to the target node? For example, rather than having the developer set the 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 |
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 <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. |
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 |
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 |
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. |
@LeaVerou Check out how 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. |
As for DXIn 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 attributesA 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>
<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. |
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? |
@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:
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. |
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? |
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? |
@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 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. |
One potential issue I can see with the non- 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 Whereas with the <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 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 |
Would the use of <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>
|
Addressing @LeaVerou's comment above:
For things like For example: <input aria-labelledby="b a">
<div id="a">two</div>
<div id="b">one</div> The accessible name of the @jyasskin said:
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. |
@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):
For (1), I think the current 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 Since 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 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. |
If we go with the current id-based syntax, I wonder what ergonomics improvements we could make:
|
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.
|
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. |
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 |
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 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 |
こんにちは TAG-さん!
I'm requesting a TAG review of Reference Target.
Reference Target is a feature that enables using IDREF attributes such as
for
andaria-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.
The text was updated successfully, but these errors were encountered: