Form fields need a label
- Rule:
axe/label - Checks: Form elements must have labels
- WCAG: 4.1.2
- Fix class:
content: A fix needs authored text.
Who it affects
Section titled “Who it affects”- People who use a screen reader or a braille display, most of them blind.
- People who control the page by speaking.
How to fix it
Section titled “How to fix it”Visible text names the field but is not connected to it
Section titled “Visible text names the field but is not connected to it”Make the text a <label> whose for matches the field’s id exactly, and keep that id unique on the page. Clicking the label then also focuses the field.
<label for="city">City</label><input type="text" id="city" autocomplete="address-level2">The field has no label at all
Section titled “The field has no label at all”Add a visible <label> connected with for and id, in words that say what to enter. Take the words from the field’s purpose in the form; if that is unclear, ask the form’s owner rather than guess.
The <label> is hidden with display: none or visibility: hidden
Section titled “The <label> is hidden with display: none or visibility: hidden”Show the label. Hide it, with a class that clips it visually but keeps it in the accessibility tree, only where what people see already makes the purpose clear, such as a search box beside a Search button.
<label for="site-search" class="visually-hidden">Search this site</label><input type="search" id="site-search"><button type="submit">Search</button>Fixes that fail
Section titled “Fixes that fail”Using only a placeholder
Section titled “Using only a placeholder”axe accepts it as a name, but it disappears as soon as people type, is often faint, and leaves the field without a lasting visible label.
Naming the field only with aria-label where a visible label would fit
Section titled “Naming the field only with aria-label where a visible label would fit”Screen readers announce it, but sighted people see no label, and voice control users cannot tell what to say to reach the field.
Adding only a title attribute
Section titled “Adding only a title attribute”axe’s label rule accepts it, but it appears only on mouse hover, so most people never see it. axe’s best-practice rule label-title-only reports it.
In React
Section titled “In React”The part of the fix that differs in React, in its syntax. The rest of the guidance holds.
A field component hard-codes its input’s id
Section titled “A field component hard-codes its input’s id”Use the caller’s id if one is passed, or else one from useId, for both the label’s htmlFor and the input’s id, so each instance’s label reaches its own input. Set the id after spreading the other props, so the spread cannot replace it.
function TextField({ label, id: idProp, ...rest }) { const generatedId = useId(); const id = idProp ?? generatedId; return ( <> <label htmlFor={id}>{label}</label> <input {...rest} id={id} /> </> );}An input component does not pass id on to its <input>
Section titled “An input component does not pass id on to its <input>”Spread the remaining props onto the <input>, so the id a label points at, and any aria-* props, reach the element.
function Input({ className = '', ...rest }) { return <input className={`input ${className}`} {...rest} />;}Fixes that fail in React
Section titled “Fixes that fail in React”Using the field’s name as its id
Section titled “Using the field’s name as its id”Two instances of the form, such as shipping and billing addresses, then share IDs, and both labels point at the first input.
Generating the id with Math.random() or a module-level counter
Section titled “Generating the id with Math.random() or a module-level counter”The value differs between the server and the client render, so hydration reports a mismatch. useId gives an ID that is unique and the same on both.
More on the rule
Section titled “More on the rule”axe-core’s documentation of the rule
CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.