eslint-plugin-jsx-a11y
eslint-plugin-jsx-a11y copied to clipboard
My `<label>` has `htmlFor` matching an input's `id`, but `jsx-a11y/label-has-associated-control` still warns?

The input needs to also be inside the label to satisfy the stricter form of the rule (which the airbnb config configures, for example).
Is that part of an a11y spec? That is... <label>s that nest text + <input>s are as equally valid as doing <label for="email">Email</label> and <input id="email" /> as siblings.
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
- https://www.w3.org/TR/html52/sec-forms.html#the-label-element
Not trying to be nitpicky, just seems strange that an a11y spec would run contrary to the HTML spec.
The purpose of the “both” requirement is to reflect actual devices in use. There are some that only support linking, some that only support nesting, and some that support either.
The only way to reach 100% of people - which is what a11y is all about - is by doing both, regardless of what some spec says.
some that only support nesting
Ah okay I didn't realize. Thanks for educating me @ljharb :)
@adamwathan A lot of TailwindUI examples use this pattern. It's not a11y-friendly. ☝️ I think TailwindUI should default to nesting elements within <label>s.
Can you share some examples of devices that don’t properly relate labels and inputs without nesting?
@adamwathan If I recall (i might be getting this backwards) Dragon NaturallySpeaking, for example, only supports for-ID linking, even when labels are nested; and there's a number of assistive devices (not browsers, and probably older ones) that don't support for-ID linking at all.
Asked about this on Twitter and can’t find any evidence to support this is an issue. Only found the opposite in fact — that using both approaches at once causes new problems.
https://twitter.com/adamwathan/status/1277760447404560384?s=21
@adamwathan Thanks for pinging your community about it.
Adrian Roselli seems to be the only person, so far, who has actually studied this in-depth:
- https://adrianroselli.com/2020/01/my-priority-of-methods-for-labeling-a-control.html#HTML
- https://twitter.com/aardrian/status/1188470905502130176
He seems to think Dragon has problems with the nested version.
Anyone have any friends who own Dragon and could test this for us? I don't have an extra $100 lying around to buy Dragon (especially for this one-off thing).
If this widely used rule is getting this wrong, and Dragon is the most popular screenreading software, this would be important to fix.
Dragon is the only implementation I'm aware of that can't handle simple nesting.
I have seen zero evidence that using both causes problems; one vague tweet in that thread doesn't constitute evidence.
Regardless of what you do, the other benefit of nesting is that it ensures the click/tap area is larger and intuitively encloses both the label and the input.
one vague tweet in that thread doesn't constitute evidence.
Fair enough. I don't know his credentials. His website being almost exclusively accessibility content makes me lean towards trusting him.
So far we have your sketchy memory claim vs some vague tweet.
I'd really like for someone to get in touch with anyone who has a copy of Dragon to confirm this. Anyone famous want to ping their Twitter followers to help? Or any company looking at this issue want to sponsor this project [that's used on trillions of websites] a Dragon license so @ljharb can 100% confirm/deny these things?
the other benefit of nesting is that it ensures the click/tap area is larger and intuitively encloses both the label and the input.
To be pedantic, <label for="..."> also handles clicking to focus the input, albeit <label> is inline by default so the click area is smaller (setting label { display: block; } in a base.css or reset would fix that).
when they're not nested, you have two clickable rectangles, often with confusing gaps in between, instead of one larger one with no gaps.
Purchasing a Dragon license wouldn't answer this question, Dragon has issues with nesting, we are looking for evidence of a browser/device that has issues with linking.
There's some confusion.
If I recall (i might be getting this backwards) Dragon NaturallySpeaking, for example, only supports for-ID linking... — @ljharb
Dragon is the only implementation I'm aware of that can't handle simple nesting. — @ljharb
- Jordan isn't sure which devices care about nesting.
- I agree with Jordan that some random/vague Tweet probably isn't enough to base this on.
- I think someone actually loading up Dragon and testing it out would let us solve this, and probably lots of other guesses that are sprinkled through eslint-plugin-jsx-a11y.
- If eslint-plugin-jsx-a11y is used on tons of sites, and Dragon (the most popular accessibility software) can't read labels, then it's potentially screwing with lots of poor-vision people (particularly with forms). Seems worth investigating.
But I'm tired now and I have to go home.
To be clear, while the advice in the past was unarguably to use both, if the set of devices/software that are conceivably in use today forces a different best practice, I'm happy to update the recommendation in the airbnb config. However, I don't think there'd be anything actionable in this repo except perhaps updating the docs (so, I'll leave this open until we have a resolution).
In the meantime, and regardless of the outcome, this is an eslint rule - you can configure it to whatever you like :-)
@corysimmons The information in this thread says that Dragon only supports for-ID linking, and that is what Tailwind UI uses. We haven't seen any evidence of a browser/device yet that does not support for-ID linking.
You made the claim that the Tailwind UI markup is not "a11y-friendly" because we use for-ID linking instead of nesting. We know that Dragon does support for-ID linking, in fact it only supports for-ID linking. Testing in Dragon will not validate what you're worried about because we already know the Tailwind UI mark up is perfectly fine in Dragon. It's the mystery "only supports nesting" devices we need to test in, and so far we have no idea what those devices are.
You said:
I think TailwindUI should default to nesting elements within labels.
We have zero evidence that this is required because so far no one has named any device that requires nesting. In fact so far we have only been able to name a tool (Dragon) that requires linking, which is what Tailwind UI does.
Folks can override the default AirBnB config for this rule in their local project, no?
Indeed they can, that's the beauty of eslint :-)
Hey, I am the author of that vague/random tweet (and the blog post) mentioned above.
Dragon is voice dictation software, not a screen reader. For voice-only users, Dragon has been the de facto choice for years, even though its makers adamantly stress it is not AT (assistive technology).
I have worked with folks with motor impairments who rely on it, and it is a point of frustration for them as users. I have tested it, and it is still not recognized.
That being said, Dragon abandoned macOS, forcing Apple to build its own voice control support, and Windows has had it for years. More users are moving to the native options and, so far in testing, their support is... better? Factors which confuse testing include other issues on the page, and the mood of the winds (it really can be tough to nail down in testing), and how clearly the user speaks.
The good news is that as long as you are using native HTML controls (for the text fields, select menus, etc) then this is not a true barrier because a voice control user has other ways to get to them. But be aware that wrapping labels around fields can create a frustrating experience for some users in a very specific context.
My credentials are on my site in bio, but to save you the hassle I do accessibility consulting professionally and am happy to answer questions related to credibility, experience, favorite type of coffee, testing tools, and so on.
@aardrian Thank you! So reading between the lines a bit just to be absolutely clear, the recommendation is:
Use
forandidlinking, not wrapping
...correct? And are there any negative consequences you know of to using linking instead of wrapping? Any negative consequences to using both at the same time?
Yeah, in short this construct:
<label for="FOO">Hey</label>
<input type="text" id="FOO">
Is safer than this construct:
<label>
Hey
<input type="text">
</label>
If your goal is to support the broadest possible number of users.
I would have to go back and test both at the same time as I recall there may have been a bug somewhere, but I think that is safe? Frankly, my memory goes back far enough I could be confusing it with the IE6 bug around wrapped labels as well.
For some confirmation, Accessibility Support is a handy resource and it talks about voice control (though the results are a year or more old). https://a11ysupport.io/tech/html/label_element
I probably should have just linked that from the start instead of squeezing my ego.
@aardrian I'd be very interested to hear about any current downsides to using both.
@ljharb I would as well. You can file a PR with https://a11ysupport.io/tech/html/label_element#related-tests and add a new test for when both are used together. Then if in the scope of testing you find something, we will all have it handy as a reference.
As per https://a11ysupport.io/tech/html/label_element#support-table-0, explicit labelling with for (i.e. without nesting) is supported across the board -- or at least everywhere implicit labelling (i.e. with nesting) is supported (assuming Voice Access on Android support hasn't changed).
So do we agree that jsx-a11y/label-has-associated-control should not warn when encountering an explicitly labelled input element (or other "labelable" element)?
If so, how can this be detected? Just checking if an id is present would lead to false negatives, for sure, but it'd be an improvement over the false positives we get now. 😅
The rule can be configured however you like. The Airbnb config chooses to configure it with “both”, and the table you linked still has some gaps in the “Voice Control support by expectation” section.
I’m not actually sure why this issue is still open, since the rule is not configured with “both” in the plugin’s recommended config.
My bad, your reply made me realise that the rule that's causing trouble for me is actually control-has-associated-label! Looks like the issue I'm after is #566. Apologies.
This construct triggers a bug in Chrome:
<label>
<input type="radio" […]>
<span>Foo</span>
</label>
Ref: https://github.com/FreedomScientific/VFO-standards-support/issues/575
Chrome bug (fixed, not deployed): https://bugs.chromium.org/p/chromium/issues/detail?id=1254723
@aardrian with the Chrome bug, what happens when the label also uses htmlFor and is linked to the input's ID?
@ljharb I did not discover, report, nor test the bug. You could try the test case (adding the for attribute) and see what you get.
For ESLint configuration: jsx-a11y/label-has-associated-control: ["error", { assert: "either" } ]