Accessible forms give every field a label a screen reader can announce, group related choices, explain errors in text next to the field, and let the browser fill in what it already knows. Underneath all of that is one rule that also applies to every link and button: each needs an accessible name, the words assistive technology announces for it. A field with no name is read as "edit text", an icon button as "button", and twelve "Read more" links as twelve identical mysteries.
This guide covers how the name is worked out, how to label fields without a redesign, and the fixes for icon buttons, cards and generic link text. Labels and names are two of the ten fixes in the web accessibility guide, and most sites are done in an hour because one template produces every broken instance.
Quick answer
- Every
<input>,<select>and<textarea>needs a label: best a visible<label for="id">, or a<label>wrapped around the field. - No room in the design? Keep the
<label>and hide it with a.visually-hiddenclass, or usearia-label. - A placeholder is not a label, and a
titleis a weak one. - Radio buttons and checkboxes that answer one question go in a
<fieldset>with a<legend>. - Errors: text next to the field, linked with
aria-describedby, saying what to do. - Every link and button needs a name that makes sense alone: "Add to cart", not "click here"; "Read more about winter tyres", not "Read more".
- Keep the visible words inside the name (WCAG 2.5.3 Label in Name) so voice-control users can say what they see.
What an accessible name is
The browser computes a name for every interactive element and passes it to assistive technology, following the W3C's Accessible Name and Description Computation. It takes the first of these it finds:
aria-labelledby: the text of other elements, referenced by id.aria-label: a string on the element itself.- The native source: a connected
<label>for a form field; the text content for a link or button, including thealtof an image inside and visually hidden text, but not text insidearia-hidden="true". - The
titleattribute. - For fields only, the
placeholder, as a last resort.
title shows only as a mouse tooltip, never on touch screens. A placeholder disappears when someone types and is usually too pale to meet the 4.5:1 contrast ratio. An <i class="icon-search"></i> or an SVG without a name contributes nothing, so a button containing only that has no name.
The rules are WCAG 2.2 criteria 1.3.1 Info and Relationships, 3.3.2 Labels or Instructions, 2.4.4 Link Purpose (In Context), 2.5.3 Label in Name and 4.1.2 Name, Role, Value, all level A.
How getReport checks it
The report renders the page in Chromium and runs axe-core. Two findings gather the name rules:
This one collects fields with no name (label), selects without a name (select-name), fields whose only name is a title (label-title-only) and custom ARIA inputs and toggles without a name. It appears only on pages with form fields.

This one is fed by link-name and button-name: elements with an empty name, which read as "link" or "button". Three things neither card tells you:
- axe accepts a placeholder as a name. A field labelled only by
placeholder="Email"passes but still fails people; look for grey text inside a field with no words beside it. - "Read more" has a name, so axe does not flag it. Generic link text is your job in step 6.
- The same elements count twice. Unnamed fields are critical violations and unnamed links and buttons serious ones, so fixing names clears those counts too.
Labelling form fields, step by step
1. Find the fields
Open the form labels finding and read the selectors, such as #mc-embedded-subscribe-form input[type="email"] or .search-form > input. Most sites have a search box in the header, a newsletter field in the footer and one real form. The header and footer ones repeat on every page, so fix them first.
2. Add a visible label where there is room
<label for="contact-email">Email address</label>
<input type="email" id="contact-email" name="email" autocomplete="email" required>The for must match an id that is unique on the page. Wrapping the field in the <label> needs no ids, which helps when a form appears twice. A connected label also makes the field easier to hit: clicking it focuses the field.
3. Where there is no room, hide the label visually only
A one-field newsletter under a heading that says "Get our newsletter" does not need visible label text, but it still needs a label. Hide it from the screen, not from the accessibility tree:
.visually-hidden {
position: absolute !important;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}<form role="search" action="/search" method="get">
<label for="site-search" class="visually-hidden">Search the shop</label>
<input type="search" id="site-search" name="q">
<button type="submit">
<svg aria-hidden="true" focusable="false" width="20" height="20"><use href="#icon-search"></use></svg>
<span class="visually-hidden">Search</span>
</button>
</form>display: none and visibility: hidden remove the text for screen readers too. WordPress themes ship this class as .screen-reader-text, Dawn-based Shopify themes as .visually-hidden.
When you cannot add an element, aria-label="Email address for the newsletter" names the field directly; when the words are already on the page, aria-labelledby="col-qty item-42" combines them. Prefer a visible label: voice-control users cannot say words they cannot see.
4. Group choices with fieldset and legend
<fieldset>
<legend>Delivery method</legend>
<input type="radio" id="ship-standard" name="shipping" value="standard" checked>
<label for="ship-standard">Standard, 3-5 working days, €4.90</label>
<input type="radio" id="ship-express" name="shipping" value="express">
<label for="ship-express">Express, next working day, €9.90</label>
</fieldset>Screen readers announce the legend when focus enters the group. The same pattern fixes "Date of birth" above three selects: a legend for the question, a label (visible or hidden) for day, month and year.
5. Hints, errors, autocomplete and sign-in
<label for="password">Password</label>
<input type="password" id="password" name="password" autocomplete="new-password"
aria-describedby="password-hint password-error" aria-invalid="true">
<p id="password-hint">At least 12 characters.</p>
<p id="password-error">This password has 8 characters. Add 4 more.</p>- Errors say what to do, sit next to the field and never rely on a red border alone.
- Required fields: the
requiredattribute is announced; if you use an asterisk, explain it once at the top of the form. autocompleteon fields about the user (name,email,tel,street-address,postal-code,current-password,one-time-code…) is WCAG 1.3.5, level AA. axe reports invalid values such asautocomplete="e-mail".- Two WCAG 2.2 criteria apply to longer forms: 3.3.7 Redundant Entry (do not ask for the same information twice in one process; offer "same as billing address") and 3.3.8 Accessible Authentication (allow paste and password managers in login fields, no puzzle as the only way in).
Naming links and buttons
6. Fix the empty names first, then the generic ones
Icon-only buttons and social icons. Hide the icon, add the name as text:
<a href="https://www.instagram.com/exampleshop/">
<svg aria-hidden="true" focusable="false">…</svg>
<span class="visually-hidden">Instagram</span>
</a>aria-label="Instagram" on the link works too; hidden text is translated with the page more reliably.
Image links. The image's alt is the link's name, so it says where the link goes: alt="Example Shop home". Alt text covers functional images in full.
"Read more", "Learn more", "Click here". They pass the automated check and fail people, because screen readers offer a list of all links out of context. Better: say the target ("Read the winter tyre guide"). When the design must show "Read more", add the target in hidden text, after the visible words:
<a href="/blog/winter-tyres/">Read more<span class="visually-hidden"> about choosing winter tyres</span></a>Avoid aria-label="Winter tyres" on a link that shows "Read more": the visible words are no longer in the name, and 2.5.3 fails. Descriptive anchor text also tells search engines what the target page is about.
7. Cards: one link, on the heading
A blog card often has three links to the same post: the image (no name), the title and "Read more" (generic). Use one link on the heading, stretched over the card:
<article class="card">
<img src="/img/winter-tyres.jpg" alt="" width="640" height="400">
<h3><a href="/blog/winter-tyres/">Choosing winter tyres: a 10-minute guide</a></h3>
</article>.card { position: relative; }
.card h3 a::after { content: ""; position: absolute; inset: 0; }8. Buttons that say what they do
"Submit" and "OK" are the button version of "click here"; use "Send message", "Apply filters". For toggles, keep the name fixed and announce state with aria-expanded (menus) or aria-pressed: a "Menu" button with aria-expanded="false" is read as "Menu, collapsed".
Platform notes
WordPress. Contact Form 7 labels live in the form template (Contact → Contact Forms); wrap each tag, as in <label> Your email [email* your-email autocomplete:email] </label>, and add use_label_element to checkbox and radio tags. Gravity Forms and WPForms have a label per field; if you hide one, re-run the checker. Classic themes' "Continue reading" link comes from the excerpt_more and the_content_more_link filters; in a child theme, add <span class="screen-reader-text"> about {post title}</span> inside the link. In block themes, the Query Loop links the post title by default, and the Read More block's text is editable. Newsletter embeds trimmed to fit a footer often lose their label; add it back in the embed code.
Shopify. Header icons (cart, search, account) live in sections/header.liquid; current themes include a translatable visually-hidden span. Unlabelled fields and nameless buttons usually come from pop-up, review and newsletter apps; check each app's label settings.
Component libraries. Fix the input and icon-button components once: a required label prop is a good guard.
Verify
- The form labels finding reads "Every form field has a label" and link names "Every link and button has an accessible name".
- Clicking each label focuses its field; browser autofill fills the right values.
- A screen reader's links list (NVDA: Insert+F7; VoiceOver: VO+U) has no entry called "link" and no repeated "Read more". The script is in testing with a screen reader.
- Voice control: "click" plus a button's visible label does the right thing.
Common mistakes
- A placeholder as the only label. axe lets it pass; people do not.
- A
forthat points nowhere.for="email"next toid="email-1"names nothing. aria-labelthat differs from the visible text. "Subscribe" in code, "Email" on screen: voice control fails.- Three links per card. Every article appears three times in the links list.
- Fixing the page instead of the template. The finding comes back on every other page.
Questions people ask
What makes a form accessible?
An accessible form has a label for every field, groups related choices in a fieldset with a legend, marks required fields in text, and shows errors next to the field in words that say how to fix them. It works with the keyboard alone, supports autocomplete and password managers, and has submit buttons named for what they do.
What is an accessible name?
An accessible name is the text a browser gives an element for assistive technology, such as "Search, button" or "Email address, edit text". It comes from aria-labelledby, aria-label, a connected label or the element's own text, in that order. Every link, button and form field needs one, and it should contain the visible words.
Is a placeholder enough as a form label?
No. A placeholder vanishes as soon as someone types, is often too pale to read, and is not announced reliably by every screen reader. Automated checks may still pass it because browsers fall back to it. Use a <label>, visible or visually hidden, and keep the placeholder for an example value if at all.
Why are "read more" links bad for accessibility?
Screen reader users often navigate with a list of all links on the page, where "Read more" appears many times with no context. WCAG 2.4.4 asks that a link's purpose be clear from its text or its context. Name the target in the link, or add it in visually hidden text after the visible words.
Do icon buttons need text?
Yes. An icon-only button, like a cart, menu or close icon, has no accessible name unless you add one, so it is announced as just "button". Add visually hidden text inside it or an aria-label, and hide the icon itself with aria-hidden="true". Visible text next to the icon helps everyone and is better still.