Form controls need accessible names

Why it matters

A form control with no accessible name leaves screen reader users guessing at its purpose. They hear “combo box” or “edit” with nothing to anchor it to — no field name, no instruction. The only way to recover is to read surrounding context, open the control and inspect its options, or give up. Sighted users get the label for free; screen reader users shouldn’t have to work harder for the same information.

This rule covers the WCAG 2.2 Success Criterion 4.1.2 Name, Role, Value (Level A).

How form controls get an accessible name

Prefer a visible <label>. In Simple Form, that means letting the helper render its default label or passing an explicit string: label: "Sort by". Visible labels help everyone — sighted users, screen reader users, voice control users speaking the field name — and they survive translation and browser autofill.

If the design genuinely calls for a control with no visible label — typically a control whose purpose is obvious from its surroundings, like a “Sort by” select on a search results page — the control still needs an accessible name. Provide it explicitly with one of:

In Simple Form, both go inside input_html:

<%= form.input :sort_by, collection: opts, label: false,
      input_html: { aria: { label: "Sort by" } } %>

A placeholder is not a label. It disappears as soon as the user types, browsers don’t reliably expose it as the accessible name, and it fails users who rely on the prompt staying visible while they fill the field.

SimpleFormInputMissingAccessibleName

Applies to: Simple Form form.input calls with label: false or label: "" — regardless of input type. as: :hidden is skipped, since hidden inputs don’t render a visible control.

The rule passes when input_html provides aria-label or aria-labelledby. Both hash and string keys are accepted (aria: { label: "..." } and "aria-label" => "...").

Bad

<%= form.input :name, label: false %>
<%= form.input :sort_by, collection: opts, label: false %>
<%= form.input :sort_by, as: :select, label: false %>

Good

Render a visible label by removing label: false and letting Simple Form generate one from the attribute name, or by passing an explicit string:

<%= form.input :name %>
<%= form.input :sort_by, collection: opts, label: "Sort by" %>

Or, when there’s no visible label, supply aria-label:

<%= form.input :name, label: false,
      input_html: { aria: { label: "Name" } } %>

Or aria-labelledby pointing to existing visible text:

<h2 id="sort-label">Sort by</h2>
<%= form.input :sort_by, collection: opts, label: false,
      input_html: { aria: { labelledby: "sort-label" } } %>

as: :hidden doesn’t render a visible control, so it’s exempt:

<%= form.input :secret, as: :hidden, label: false %>

What this rule doesn’t catch

This rule has a deliberately narrow scope. Things it doesn’t flag: