Material-ui: Label for inputs should be associated via wrapping, not "for" attribute

Created on 5 Jul 2019  路  11Comments  路  Source: mui-org/material-ui

Not sure if this has been asked before (I wasn't able to find an issue for it).
HTML5 allows two ways to associate a label with an input field. The first one is via the "for" attribute and an id, like

<div>
 <label for="myInput">My Input</label>
 <input type="text" id="myInput" />
</div>

The other is to use the label as a wrapper, like

<label>
 My Input
 <input type="text" />
</label>

When using the first method if no id is set the browser does not know about the relation between the label and the input. This isn't ideal for accessibility reasons. It's also not ideal when using "Testing Library" with the getByLabelText method to get the input - which simply won't return the correct input as there is no obvious relation.

Current versions of material ui input components use the first method. I propose to switch to the label wrapping method. I see two major benefits from this:

  1. Using hierarchy alone, the browser knows which labels refer to which input, which improves accessibility.
  2. Given the first reason there is no reason to set Ids anymore. This is great because the dom tree should never contain the same id twice. Since react components are reusable this is not guaranteed when using the same component (one that has a TextField with an Id set) twice.

Possible disadvantages:

  • dom diverges from material design (see: https://material.io/develop/web/components/input-controls/form-fields/). Interestingly Floating Labels in material design allow labels as wrapper (https://material.io/develop/web/components/input-controls/floating-label/, see "Avoid Dynamic ID Generation")
  • component styling could become more complex, at the very least it has to be adjusted
accessibility TextField discussion

Most helpful comment

Having dived more into how accessible names are computed I can only see a benefit with not having to provide ids which would be quite nice since it gives less opportunity to make mistakes.

If somebody wants to work on this I would suggest making this work for

<FormControl>
  <InputLabel>
    label
    <Input variant="outlined" />
  </InputLabel>
</FormControl>

-- https://material-ui.com/components/text-fields/#components

Then we'll see how we can incorporate this approach into TextField and how backwards compatibility looks.

All 11 comments

Yeah that would be nice. Styling is quite tricky though so it might not be viable or require a lot of effort. Adding an id prop to the TextField does not though which is all that is required to make it accessible.

The getByLabelText from @testing-library/react might be problematic because it doesn't use textContent but joins text nodes.

If you have problems with accessibility either check out our demos (94% lighthouse score; only contrast issues) or our tests for TextField which are written with @testing-library/react (especially the test with labels).

@konqi I believe It's in the public API landscape, I'm not sure we can change anything before v5 without introducing a breaking change. However, it's worth exploring.
I have done a quick benchmark, Bootstrap and Reakit use the same approach we do. I would suspect the label wrapping approach has customization issues, to explore.

I have done a quick benchmark, Bootstrap and Reakit, uses the same approach we do. I would suspect the label wrapping approach has customization issues.

Or they don't know about it?

Or they don't know about it?

I don't know, let's ask.

Hey @diegohaz, I was looking at your experimental API for solving the form problem in https://reakit.io/docs/form/, it's interesting! :). I was wondering, what's the tradeoff behind doing:

<Form>
  <FormLabel name="name">
    Name
  </FormLabel>
  <FormInput name="name" placeholder="John Doe" />
  <FormMessage name="name" />
  <FormSubmitButton>Submit</FormSubmitButton>
</Form>

and not?

<Form>
  <FormLabel>
    Name
    <FormInput name="name" placeholder="John Doe" />
  </FormLabel>
  <FormMessage name="name" />
  <FormSubmitButton>Submit</FormSubmitButton>
</Form>

Thanks.

94% lighthouse score; only contrast issues

@eps1lon Do we still have a few contrast issues?

@eps1lon to be honest, accessibility is not really my main concern here. I believe it's a side-effect of me having an id-allergy. So being forced to use Ids is my greatest pain. Reusability of components using Inputs is also reduced. e.g. when I render the same component twice, the same id exists twice on the dom - that simply shouldn't happen and is the reason why I avoid ids if possible.
I'd like to contribute to this (if I can) but wanted to give the idea a spin before I oversee some obvious reasons why the id-version makes perfect sense and/or is the better solution.

Yeah I totally get that. Creating ids is a common pain point currently. The upside of using ids is that you get to use aria-describedby or aria-controls. So even if we can switch to by default we would still advice our users to pass an id for the helper text.

Of the top of my head I can't find any strong concern against it. Only issue might be stacking context. Somebody would just have to look into it.

Just remember a11y isn't just for visually impaired. Label association helps mouse users as well (label clicking focuses inputs) and I believe password managers might as well. In the end it's about making your site more semantic which helps everyone to understand your page. Be it human or machine.

Reakit supports wrapping inputs with the native <label> (not <FormLabel>) just like one would do with HTML. The documentation uses this method specifically for checkboxes and radio buttons, but nothing prevents someone from using this for other input types.

FormLabel just exists so you can use the separate label approach without worrying about generating IDs. Reakit does this already.

But, as you noted, this is all experimental right now and may change in the future.

@diegohaz Thanks for the feedback!

I would like to change the labeling for the textField component, if you'd like me to do.

Having dived more into how accessible names are computed I can only see a benefit with not having to provide ids which would be quite nice since it gives less opportunity to make mistakes.

If somebody wants to work on this I would suggest making this work for

<FormControl>
  <InputLabel>
    label
    <Input variant="outlined" />
  </InputLabel>
</FormControl>

-- https://material-ui.com/components/text-fields/#components

Then we'll see how we can incorporate this approach into TextField and how backwards compatibility looks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

newoga picture newoga  路  3Comments

FranBran picture FranBran  路  3Comments

mattmiddlesworth picture mattmiddlesworth  路  3Comments

sys13 picture sys13  路  3Comments

ghost picture ghost  路  3Comments