-
Notifications
You must be signed in to change notification settings - Fork 59
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
linking elements cross boundaries is only possible imperatively and leads to anti-patterns #107
Comments
cc @alice, @cookiecrook |
I don't quite follow the proposal - could you possibly add some "strawman" code which illustrates what you mean? |
@alice this proposal is focus on one premise: replacing To provide an actual example: class FancyInput extends HTMLElement {
constructor() {
super();
var shadow = this.attachShadow({
mode: 'open',
delegatesFocus: true,
});
shadow.innerHTML = `
<label id="fancy-label">internal label for input element</label>
<input aria-describedby="fancy-label" />
`;
}
}
customElements.define('fancy-input', FancyInput);
class MyComponent extends HTMLElement {
constructor() {
super();
var shadow = this.attachShadow({
mode: 'open',
});
shadow.innerHTML = `
<label id="my-label">my custom label for fancy-label element</label>
<fancy-input aria-describedby="my-label" />
`;
}
}
customElements.define('my-component', MyComponent); If you declare those two components, and you insert The author of MyComponent will be guessing why is the label specified on its markup doesn't get announced? Although, changing It is important to notice that this is probably going to be one of the most common use-case. We can definitely provide some strawman, but first we will like to know if this was discussed in the past. |
So you're suggesting that using Example: class FancyInput extends HTMLElement {
constructor() {
super();
var shadow = this.attachShadow({
mode: 'open',
delegatesFocus: true,
});
shadow.innerHTML = `
<label for="fancy-input">internal label for input element</label>
<input id="fancy-input" aria-describedby="fancy-input-error" />
<span id="fancy-input-error">internal error message for the input element</span>
`;
}
}
customElements.define('fancy-input', FancyInput);
class MyComponent extends HTMLElement {
constructor() {
super();
var shadow = this.attachShadow({
mode: 'open',
});
shadow.innerHTML = `
<fancy-input aria-describedby="my-additional-error" />
<span id="my-additional-error">my custom error for fancy-input element</label>
`;
}
}
customElements.define('my-component', MyComponent);
<my-component>
#shadow-root
| <fancy-input>
| #shadow-root
| | <label for="fancy-input">internal label for input element</label>
| | <input id="fancy-input" aria-describedby="fancy-input-error my-additional-error" />
| | <span id="fancy-input-error">internal error message for the input element</span>
| </fancy-input>
| <span id="my-additional-error">my custom error for fancy-input element</label>
</my-component> Where Feels super weird that <my-custom-modal role="dialog" aria-modal="true" aria-labelledby="id_of_visible_header" aria-describedby="id_of_modal_body">
<my-custom-modal-header>
#shadow-root
| <h2 id="id_of_visible_header">
| Modal header
| </h2>
</my-custom-modal-header>
<my-custom-modal-body>
#shadow-root
| <div id="id_of_modal_body">
| Random content provided by component consumer
| </div>
</my-custom-modal-body>
</my-custom-modal> |
It's not clear to me how this would work with possible inward-pointing references (e.g. aria-activedescendant) in addition to the outward-pointing references like aria-labelledby. |
This pattern of decorating built-in elements with custom elements has a number of issues like this one. I'm not sure what would be the best API to allow the behaviour you describe, but I agree it's desirable. |
That's ok, maybe we can decouple this from focus, and just have a new configuration for shadow root that signals the auto-wiring of the accessibility descriptors. I should have started with it.
that can works exactly as described above, where the internal element receiving the focus is auto-wired as the active descendant, without having to expose this to user-land. the entire proposal is to attack the most common use-cases so consumers of custom elements can thread them just like regular elements. I'm still missing the part where
we have discussed this particular case, e.g. Again, the part that I want to stress here is that it is Not Okey to ask consumers of custom elements to do something different from what they normally do when using an input. It is also Not Okey to ask authors of custom elements to do very complicated gymnastics to do the right thing about accessibility, because we know where that leads. Obviously we already made mistakes like not supporting labels in the same way they are supported for regular inputs, but we should be able to fix those and provide default behaviors that can cover a lot of grounds for the most common use-cases. |
There are a number of IDREFS-valued ARIA attributes that have no direct relationship to the currently focused element. For example, aria-controls and aria-owns. |
The issue with the "decorator" pattern is fundamentally that you end up with two (or more) nested elements trying to act like a single element - you want the decorated element to act like the single element for some purposes, and the decorator for other purposes. |
FWIW the labeling issue is also being discussed here.
WICG/webcomponents#187
…On Tue, Mar 6, 2018, 4:01 PM Alice ***@***.***> wrote:
The issue with the "decorator" pattern is fundamentally that you end up
with two (or more) nested elements trying to act like a single element -
you want the decorated element to act like the single element for some
purposes, and the decorator for other purposes.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#107 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABBFDY4Ic8qMNtAG5VJ0y0ZBsVHoH82bks5tbyNEgaJpZM4SanSt>
.
|
...and here whatwg/html#3219 - for imperative attaching of labelable element to the label element. |
This is superseded by the proposal in #169, which is a lot more coherent. |
Disclosure: This issue is just to brainstorm on the proposed linking mechanism and maybe open other avenues.
A very common use-case is to have custom elements that should be described by sibling elements,
Assuming that
fancy-input
element contains some internal focusable elements, and itself is relying on delegate focus to facilitate the user interaction, e.g.:It is still impossible to connect those elements from outside (
#global-one
) and from within (input
) without opening some sort of side-channel onfancy-input
element. I see two options to achieve this:<fancy-input>
custom element that when called with one argument (an element), it sets that element into the internal input'sariaDescribedByElements
collection. (with the corresponding guards and deduping).<fancy-input>
custom element that when invoked without arguments return a reference to the internal input element.Either way, it is going to be bad, error prompt or leaky, this is one of those situations when you have to take a poison.
Another option is to observe the aria attributes on the host, walk from the host all the way to the nearest root, and query for the ID from there to try to do some auto-wiring via imperative APIs. This should work fine, but the problem is that since you're in control of the situation as the author of the component, you will have to observe mutations to rewire when needed. This makes the situation very error prompt, while the option 1 and 2 outsource that responsibility to the consumer of the custom element.
Proposal
When discussing this with the team, we were wondering whether or not the delegate focus on the shadow root could be sufficient indication for some sort of compounding mechanism for some of these
aria-*
attributes that reference IDs. In the example above, you can see that from the consumer perspective (the owner of the template), you can do the regular connecting between the two elements via IDs, they are in the same shadow after all.But from the component's author perspective, just signaling that the root should receive the focus (via delegateFocus configuration), could be used to build the right tree under the hood that connects the input with both elements (
#global-one
and#local-one
) without the user having to do so manually.Pros:
Cons:
The text was updated successfully, but these errors were encountered: