Custom input fields need a name
- Rule:
axe/aria-input-field-name - Checks: ARIA input fields must have an accessible name
- 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”A visible label sits next to the field but is not connected to it
Section titled “A visible label sits next to the field but is not connected to it”Give the label text an id and point the field’s aria-labelledby at it. A <label> element’s for reaches only labelable elements, such as <input> and <select>; a <div> or <span> with a role is not one.
<span id="notes-label">Delivery notes</span><div role="textbox" contenteditable="true" aria-multiline="true" aria-labelledby="notes-label"></div>The field has no visible label
Section titled “The field has no visible label”Add a visible label and connect it with aria-labelledby. Use aria-label instead only where what people see already makes the purpose clear, such as a search box beside a Search button, and take its words from that purpose.
The custom field rebuilds a native one, such as a slider or a text box
Section titled “The custom field rebuilds a native one, such as a slider or a text box”Use the native element with a <label>. It comes named, focusable and operable by keyboard, with no script to maintain.
<label for="volume">Volume</label><input type="range" id="volume" min="0" max="10" value="5">Fixes that fail
Section titled “Fixes that fail”Pointing a <label for> at the element with the role
Section titled “Pointing a <label for> at the element with the role”for connects only to labelable elements such as <input>, so a <div> or <span> with a role stays unnamed and axe still reports it. Clicking the label does nothing either.
Using aria-placeholder as the name
Section titled “Using aria-placeholder as the name”A placeholder is a hint, not a name, and it goes away once the field has a value. axe still reports the field.
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 custom field component takes a label but never connects it to the element with the role
Section titled “A custom field component takes a label but never connects it to the element with the role”Render the label with an id from useId, and point the role element’s aria-labelledby at it.
function NotesEditor({ label }) { const labelId = useId(); return ( <> <span id={labelId}>{label}</span> <div role="textbox" contentEditable aria-multiline="true" aria-labelledby={labelId} /> </> );}Fixes that fail in React
Section titled “Fixes that fail in React”Spreading the caller’s aria-label onto the component’s outer wrapper
Section titled “Spreading the caller’s aria-label onto the component’s outer wrapper”The wrapper is not the field, so the element with the role stays unnamed and axe still reports it. Pass aria-label and aria-labelledby through to the element with the role.
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.