Skip to content
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

Delegation and attribute reflection #15

Closed
mrego opened this issue Jul 1, 2022 · 8 comments
Closed

Delegation and attribute reflection #15

mrego opened this issue Jul 1, 2022 · 8 comments

Comments

@mrego
Copy link
Collaborator

mrego commented Jul 1, 2022

Thinking about delegation and how it works for something like aria-labelledby.

Imagine we have a <label> and a custom element <x-input> that has an <input> inside, and we want to set aria-labelledby pointing to the label element outside of the Shadow DOM. Something like this:

<label id="mylabel">The label</label>
<x-input1>
  <template shadowroot="open">
    <input aria-labelledby="mylabel" />
  </template>
</x-input1>

In this case the association between the <input> and the <label> is not set, because the <label> is in a different scope (outside of the Shadow DOM tree).

However with ARIA attribute reflection we could make this work, by using input.ariaLabelledByElements = [mylabel]. And then in that case the association will be set.

I've a codepen with different examples: https://codepen.io/mrego/pen/jOzEjOb?editors=1010
With a screen reader, when you focus the 3 first inputs you get a message like "boo entry" (the boo element is there just to confuse the screen reader, otherwise it might try to get the proper label). But on the 4th, 5th and 6th cases, the ones that use attribute reflection, you get "This is label 4/5/6 entry".

This is how attribute element reflection has been specified on the HTML spec, and also what was agreed a while ago: whatwg/html#6063

One question I have is why this is not allowed when setting directly the attribute, like in the first example here. Could that simply just work? Would that make any sense?
If that's a possibility that could solve delegation for element references. Though something else would still be needed for string attributes like aria-label.

CC @joanmarie

@mrego
Copy link
Collaborator Author

mrego commented Jul 7, 2022

Even for string attributes we could try to use attribute reflection.

So imagine the following case:

<x-input id="xinput" aria-label="A label">
  #shadow-root
    <input id="input"/>
</x-input>

And then you could set the aria-label attribute for the inner input using the aria-label on the custom element in JavaScript with input.ariaLabel = xinput.arialabel.

There are some working examples at: https://codepen.io/mrego/pen/mdxVvGm?editors=1010

This is not very nice code, and probably this is missing some use cases; but this doesn't add any new API. Does it makes any sense?

@mrego
Copy link
Collaborator Author

mrego commented Jul 14, 2022

The previous examples were mixing cases of declarative Shadow DOM, and also using the light DOM in some cases, which was kind of confusing (I was mostly playing with different options).

So let's use a new example to clarify things: https://codepen.io/mrego/pen/XWENgZG?editors=1010

In this example we have 2 inputs, one of them where we're setting a DOMString attribute and another for a IDREF one.

  • DOMString attribute:
    <x-input-domstring aria-label="My custom label">
      #shadow-root
        <input />
    </x-input-domstring>
  • IDREF:
    <label id="anotherLabel">Another label</label>
    <x-input-idref aria-labelledby="anotherLabel">
      #shadow-root
        <input />
    </x-input-idref>

Setting the attributes from JavaScript we manage to either set the aria-label attribute for the inner input properly, or setup the relationship between the inner input and the external label.

You can check with Chromium Dev Tools or a screen reader, how the inputs have the proper accessibility information.

@nolanlawson
Copy link
Collaborator

@mrego For the aria-label example, it seems to work in Chrome but not Firefox (in terms of the <input> correctly getting the label):

Screen Shot 2022-09-13 at 10 32 34 AM

Screen Shot 2022-09-13 at 10 32 24 AM

As for aria-labelledby, your example doesn't seem to correctly apply the label to the <input> in either Chrome or Firefox:

Screen Shot 2022-09-13 at 9 51 31 AM

Screen Shot 2022-09-13 at 9 51 36 AM

I quickly tested VoiceOver, and in Chrome it works for both inputs, but for Firefox it only works for the aria-label one.

Probably we would need an exhaustive survey of screenreaders + browsers to see what works and what doesn't, and we may need to look into the details of the accessible name spec to see how it should work.

@mrego
Copy link
Collaborator Author

mrego commented Sep 13, 2022

In Chromium it works for me if I enable experimental web platform features.

Firefox hasn't implemented element reflection yet, and string reflection is behind a flag.

@nolanlawson
Copy link
Collaborator

My mistake, I thought your example was just using attributes, not reflected properties. Please disregard my comment.

@nolanlawson
Copy link
Collaborator

After discussion with @mrego, I can summarize the issue as the following:

Do we absolutely need cross-root aria delegation if we have aria element reflection?

Rego had an example of using attributeChangedCallback to manually reflect the aria-labelledby from the host to the element inside of the host's shadow root. This works (codepen).

Screen Shot 2022-09-13 at 10 32 34 AM

(This is Chrome Canary with "experimental web platform features" flag on.)

The problem here is that the aria-labelledby element remains on the host, as well as on the <input>. So some screen readers will read this twice, e.g. VoiceOver:

Screen Shot 2022-09-13 at 2 28 41 PM

This was actually raised in a previous discussion (WICG/webcomponents#917 (comment)):

I thought the main advantage was the attribute not applying to the host element when it's delegated.

So this (along with developer ergonomics) is a main benefit we're getting from cross-root aria delegation.

@nolanlawson
Copy link
Collaborator

I think we can close this now @mrego @leobalter ^ .

ChromeVox on ChromeOS also seems to do the same thing as VoiceOver+Chrome on macOS.

@mrego
Copy link
Collaborator Author

mrego commented Sep 14, 2022

It looks like there are 2 reasons:

  • Having a declarative way to do this
  • The screen reader issues with duplicated attributes.

About the duplicated attributes, I wonder how that would work with delegation; I guess we'll need some kind of tweak to make them not a problem, e.g.:

<label id="anotherLabel">Another label</label>
<x-input-idref aria-labelledby="anotherLabel">
  #shadow-root delegatesAriaLabelledBy
    <input autoAriaLabelledBy />
</x-input-idref>

In this case the custom element still has the aria-labelledby attribute, so the screen readers might have problems with that.

So even if we close this, we should take this into account on the final proposal.

@mrego mrego closed this as completed Oct 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants