Skip to content

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.
  • People who use a screen reader or a braille display, most of them blind.
  • People who control the page by speaking.

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>

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">

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.

A placeholder is a hint, not a name, and it goes away once the field has a value. axe still reports the field.

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} />
</>
);
}

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.

axe-core’s documentation of the rule

CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.