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

label-input association across Shadow DOM boundary #3219

Open
tomalec opened this issue Nov 9, 2017 · 15 comments
Open

label-input association across Shadow DOM boundary #3219

tomalec opened this issue Nov 9, 2017 · 15 comments
Labels
accessibility Affects accessibility topic: shadow Relates to shadow trees (as defined in DOM)

Comments

@tomalec
Copy link

tomalec commented Nov 9, 2017

moved from whatwg/dom#530

Currently, Shadow DOM make inputs and label elements inaccessible, if they are placed across the boundary.

Consider the cases:

  1. <input> element is in shadow root (for example in a <custom-input> custom element). It is composed there with some styles and divs around, but there is no label for such input. Then I would like to create one in the light DOM.
    jsbin demo
    <label for="ccard">Card No</label>
    <card-input id="ccard">
    #shadowroot
        <input id="number-input">
    #/shadowroot
    </card-input>
  2. <input> is in light DOM and we would like to add a label in its parent Shadow DOM
    jsbin demo
    <card-form>
    #shadowroot
        <label>Number<slot name="number"></slot></label>
        <label for="month">Month</label><slot name="month" id="month"></slot><label>Year</label><slot name="year"></slot>
    #/shadowroot
        <input slot="number">
        <input slot="month">
        <input slot="year">
    </card-form>

Now, the problem is to couple label with the input element, to get the native behavior of focus and other accessibility features.
Naturally without reimplementing it on my own.

I cannot do that as the assignment is done only via a string with an id of an element within the same tree.


For the first case, we could think of customized built-ins, but they are dead, we cannot assignShadow to an input element and it would be limited to custom elements only.

Even though I can (via inline script or custom element methods) find and match label and input, I'm still blocked, as control is read-only and there is no id of the element within the same tree I could give.

@robdodson made a nice a11y cast on how to hack it to make aria-label do the job, to make it work at least for screen readers. https://www.youtube.com/watch?v=rr3c62Wnaeo But:

  • it doesn't solve the problem of actual <label> element which I would like to use for visual features and not limit myself to just stringified attribute.
  • it requires a custom element and in fact, re-implementing label element feature in JS.

My naive solution to this would be to:

  • solve the imperative case: make control writable, so I could pass a reference to an element from shadow/separate tree,
  • solve the declarative case: let <slot> be a labelable element and delegate the search in distributed nodes, let shadow host be labelable, then it delegates the search in shadow root.

This one could be pretty tricky, as in fact it removes the need for labelable element, and opens a box of questions about cases like <label><shadow-host-with-control-and-label><input>.

@tomalec
Copy link
Author

tomalec commented Nov 9, 2017

@robdodson commented whatwg/dom#530 (comment)

For what it's worth, AOM should help with this as well wicg/aom. It'll let you pass an object reference to an element which can be used as its label.

@tomalec
Copy link
Author

tomalec commented Nov 9, 2017

@annevk commented whatwg/dom#530 (comment)

Thanks for filing this. A better place to file this would be at whatwg/html/issues/new as the DOM Standard doesn't really say how the label element needs to behave.

I suspect this would be problematic to fix as you want something that crosses the boundary (an extra complication might be that this also affects selectors). We generally decided that forms only work within the same tree. An input element inside a shadow tree also does not get submitted for instance. An alternative that might make sense and preserves the encapsulation boundary is that we allow a shadow host to be labeled, and that it can decide somehow who gets focus.

@tomalec
Copy link
Author

tomalec commented Nov 9, 2017

I suspect this would be problematic to fix as you want something that crosses the boundary

If the AOM would be able to cross the boundary (which I hope it will), with for example .labeledby = referenceToLabelingNode, it would be consistent to allow the same for <label> element as well. For a11y as well as for the cases w/o the need for AT.

Also, we are doing it imperatively and consciously. We are already crossing the boundary earlier, by getting the reference to the labelable in the (open) shadow:

const labelable = host.shadowRoot.querySelector('input'); // <- we are breaking boundary already here and that's nothing new.
label.control = labelable; // <- that would be something new, but there boundary was already crossed.

an extra complication might be that this also affects selectors

Could you give an example?

We generally decided that forms only work within the same tree. An input element inside a shadow tree also does not get submitted for instance.

But <label> element for given labelable, does not have to be in the same <form>, does it?
http://jsbin.com/nacihovumo/edit?html,output

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

An alternative that might make sense and preserves the encapsulation boundary is that we allow a shadow host to be labeled and that it can decide somehow who gets focus.

👍 I believe that would be a great to have such a primitive to build web components that are able to extend the platform in area of labelable elements

@robdodson
Copy link

robdodson commented Nov 14, 2017 via email

@annevk annevk added the accessibility Affects accessibility label Jan 21, 2018
@annevk
Copy link
Member

annevk commented Jan 21, 2018

cc @whatwg/a11y

@alice
Copy link
Contributor

alice commented Jan 21, 2018

solve the imperative case: make control writable, so I could pass a reference to an element from shadow/separate tree

Some questions we have encountered in figuring out the story in AOM:

  • Currently control is a computed value reflecting the result of a process including htmlFor and tree relationships. Does it make sense to make it writeable, or to introduce a new writable property?
  • If no new property is introduced:
    • if you set control to null, but the label element had a labelable element descendant, should the label be associated with the descendant or not?
  • If a new property (htmlForElement for argument's sake) is introduced:
    • should it reflect the value of @for, or be separate from it?
      • if it reflects, what should @for be set to if an htmlForElement refers to an element with no ID, or is in a different shadow tree?
      • if it does not reflect and htmlForElement and @for disagreed, which should take precedence?

@alice
Copy link
Contributor

alice commented Jan 21, 2018

For what it's worth, AOM should help with this as well wicg/aom. It'll let you pass an object reference to an element which can be used as its label.

n.b.: as currently specced, you would not pass a reference to an element, but to an AccessibleNodeList:

attribute AccessibleNodeList? labelledBy;

This also wouldn't create the association between the label element and its labelled control for activation behaviour.

@annevk annevk added the topic: shadow Relates to shadow trees (as defined in DOM) label Jan 22, 2018
@tomalec
Copy link
Author

tomalec commented Jan 22, 2018

Does it make sense to make it writeable, or to introduce a new writable property?

In my opinion, there are already too many ways to affect label of a control (for a11y and activation), introducing another one could make it even more confusing.

if you set control to null, but the label element had a labelable element descendant, should the label be associated with the descendant or not?

It looks consistent to me to no longer associate it. Similarly to when you set a for attribute on a label that has labelable element descendant. When you set it explicitly, the "default" should no longer apply.
http://jsbin.com/qesiqowuqi/edit?html,output

if it reflects, what should @for be set to if an htmlForElement refers to an element with no ID, or is in a different shadow tree?

I'd say null. @for is to reflect the ID of a labelable element, htmlForElement is to reflect the element, element.getAttribute('id') === null

@alice
Copy link
Contributor

alice commented Jan 22, 2018

element.getAttribute('id') === null

So the attribute would be deleted from the node? i.e.

<label for="some-id">Banana</label> <!-- before -->
<label>Banana</label>               <!-- after -->

@tomalec
Copy link
Author

tomalec commented Jan 23, 2018

The way I imagine it:

<label for="foo">Label</label>
<script>
  const label = document.querySelector('label');

  label.control = elementWithoutID;
  // label.htmlForElement = elementWithoutID;
  assert(label.getAttribute('for') === elementWithoutID.getAttribute('id'));
  assert(label.getAttribute('for') === null);  
  // <label>Label</label>
  
  label.control = elementInSameTreeWithID;
  // label.htmlForElement = elementInSameTreeWithID;
  assert(label.getAttribute('for') === elementInSameTreeWithID.getAttribute('id'));
  assert(label.getAttribute('for') === 'bar');  
  // <label for="bar">Label</label>
  
  label.control = elementFromDifferentTree;
  // label.htmlForElement = elementFromDifferentTree;
  assert(label.getAttribute('for') === null);  
  // <label>Label</label>
</script>

@rniwa
Copy link

rniwa commented Oct 25, 2018

TPAC F2F note: This will be discussed along with WICG/webcomponents#179.

@rniwa
Copy link

rniwa commented Oct 26, 2018

Rough consensus at TPAC F2F: This can be spec'ed using a delegation mechanism for a label or as a part of making custom element a form control.

@domenic
Copy link
Member

domenic commented Oct 26, 2018

Various ideas that were thrown out:

  • attachShadow({ delegatesLabel: true })---delegates to first labelable thing within the shadow tree?
  • attachShadow({ delegatesLabel: "foo" })---delegates to labelable thing with ID "foo" within the shadow tree?
  • Neither of the above is very great if you want to dynamically change what you delegate to; a different API shape may be better.
  • Maybe it's not delegatesLabel, but delegatesActivation. This would at least impact accesskey="" behavior. Maybe other things that do activation. (Even click events??)
  • Maybe a better API shape is firing an event on the shadow root which can be listened for and forwarded appropriately, somehow.
  • Nobody has sufficiently thought through how this relates to @tkent-google's custom element form submission proposal, which has some related capabilities. This use case is focused around a shadow root that wants to delegate to a real-input inside, which is different from a custom element that wants to act like an input. But they are very related so we should think harder.

@tomalec
Copy link
Author

tomalec commented Oct 26, 2018

@rniwa
I think there still will be a problem with cross shadow tree references with such approach:

<label for="lightId">Foo</label>
<div id="shadowHost">  
</div>
<script>
  shadowHost.attachShadow({delagateLabelable: 'shadowId'})
            .innerHTML = '<input id="shadowId">';
</script>

What should in such case be the value of inputs .labels property?

  • empty, as if it would not have label assigned at all,
  • shadow root/shadow host, as if it would be a label,
  • actual label element from enclosing tree

@rniwa
Copy link

rniwa commented Nov 6, 2018

I think we can spec it to be either the actual label element or empty. I don't think making it the shadow host makes much sense since that's not all what's labeling the input element.

ITenthusiasm added a commit to ITenthusiasm/custom-element-discoveries that referenced this issue Jun 30, 2023
Originally, when we started writing this component,
we wanted to give an example of making the component
_accessible_ and using semantic HTML. However, we've
kind of been brought to a halt on this because
apparenty a11y gets VERY complicated when it comes
to ARIA attributes that try to reach for elements
across the ShadowBoundary. We don't really want to
present people with something that's inaccessible,
because it won't be good for their users and it won't
give them good solutions or best practices. So...
for now we're stashing our work. If we can come up
with something that makes the Custom Select Component
accessible, we'll return to our efforts here. Until
then, we'll probably do some cleanup in the SuperTokens
examples or in the package that we have not yet released.

For anyone looking at this code, please bear in mind
that it is incomplete. Feature-wise, it's probably
at least 75% done if you wanted to take it for a spin.
If you don't care about a11y, it is still very possible
and easy to finishe building out this component.

Future Note: Can't use `onblur` for the combobox. We need
to use a different kind of listener so that users can
select options by clicking them.

For A11y Issues, See:
- https://nolanlawson.com/2022/11/28/shadow-dom-and-accessibility-the-trouble-with-aria/
- whatwg/html#3219
ITenthusiasm added a commit to ITenthusiasm/custom-element-discoveries that referenced this issue Jun 30, 2023
Originally, when we started writing this component,
we wanted to give an example of making the component
_accessible_ and using semantic HTML. However, we've
kind of been brought to a halt on this because
apparenty a11y gets VERY complicated when it comes
to ARIA attributes that try to reach for elements
across the ShadowBoundary. We don't really want to
present people with something that's inaccessible,
because it won't be good for their users and it won't
give them good solutions or best practices. So...
for now we're stashing our work. If we can come up
with something that makes the Custom Select Component
accessible, we'll return to our efforts here. Until
then, we'll probably do some cleanup in the SuperTokens
examples or in the package that we have not yet released.

For anyone looking at this code, please bear in mind
that it is incomplete. Feature-wise, it's probably
at least 75% done if you wanted to take it for a spin.
If you don't care about a11y, it is still very possible
and easy to finish building out this component.

Future Note: Can't use `onblur` for the combobox. We need
to use a different kind of listener so that users can
select options by clicking them.

For A11y Issues, See:
- https://nolanlawson.com/2022/11/28/shadow-dom-and-accessibility-the-trouble-with-aria/
- whatwg/html#3219
ITenthusiasm added a commit to ITenthusiasm/custom-element-discoveries that referenced this issue Sep 25, 2023
Originally, when we started writing this component,
we wanted to give an example of making the component
_accessible_ and using semantic HTML. However, we've
kind of been brought to a halt on this because
apparenty a11y gets VERY complicated when it comes
to ARIA attributes that try to reach for elements
across the ShadowBoundary. We don't really want to
present people with something that's inaccessible,
because it won't be good for their users and it won't
give them good solutions or best practices. So...
for now we're stashing our work. If we can come up
with something that makes the Custom Select Component
accessible, we'll return to our efforts here. Until
then, we'll probably do some cleanup in the SuperTokens
examples or in the package that we have not yet released.

For anyone looking at this code, please bear in mind
that it is incomplete. Feature-wise, it's probably
at least 75% done if you wanted to take it for a spin.
If you don't care about a11y, it is still very possible
and easy to finish building out this component.

Future Note: Can't use `onblur` for the combobox. We need
to use a different kind of listener so that users can
select options by clicking them.

For A11y Issues, See:
- https://nolanlawson.com/2022/11/28/shadow-dom-and-accessibility-the-trouble-with-aria/
- whatwg/html#3219
ITenthusiasm added a commit to ITenthusiasm/custom-element-discoveries that referenced this issue Sep 25, 2023
Originally, when we started writing this component,
we wanted to give an example of making the component
_accessible_ and using semantic HTML. However, we've
kind of been brought to a halt on this because
apparenty a11y gets VERY complicated when it comes
to ARIA attributes that try to reach for elements
across the ShadowBoundary. We don't really want to
present people with something that's inaccessible,
because it won't be good for their users and it won't
give them good solutions or best practices. So...
for now we're stashing our work. If we can come up
with something that makes the Custom Select Component
accessible, we'll return to our efforts here. Until
then, we'll probably do some cleanup in the SuperTokens
examples or in the package that we have not yet released.

For anyone looking at this code, please bear in mind
that it is incomplete. Feature-wise, it's probably
at least 75% done if you wanted to take it for a spin.
If you don't care about a11y, it is still very possible
and easy to finish building out this component.

Future Note: Can't use `onblur` for the combobox. We need
to use a different kind of listener so that users can
select options by clicking them.

For A11y Issues, See:
- https://nolanlawson.com/2022/11/28/shadow-dom-and-accessibility-the-trouble-with-aria/
- whatwg/html#3219
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accessibility Affects accessibility topic: shadow Relates to shadow trees (as defined in DOM)
Development

No branches or pull requests

6 participants