Links and buttons Rule

Buttons must have a name that says what they do button-name

A button needs a name that assistive technology can announce. A button with words inside it has one. A button that only holds an icon, an SVG or a background image does not, so a screen reader says 'button' and nothing else. Add the name with visually hidden text, an aria-label, or alt text on the image inside.

Websight reports this as Buttons must have discernible text

Who it affects

Someone who is blind, booking a GP appointment online tabs to a button and hears only 'button'. It could be Confirm, Cancel or Close, and pressing the wrong one loses the slot.

Someone using voice control says 'click close' and nothing happens, because the close button's name is empty.

Someone reading with a Braille display feels 'btn' with nothing after it and has to guess from whatever is around it.

Broken versus fixed

Both examples are live and rendered in a sandboxed frame. The broken one is deliberately broken.

Broken

A screen reader announces: button. Then: button. The person cannot tell the close button from the search button.

Fixed

Announced: Close, button. Then: Search appointments, button.

Show the markup behind both examples
Broken
<style>body{font-family:system-ui;margin:0;padding:16px}.panel{border:1px solid #cfd6dd;border-radius:8px;padding:12px;max-width:360px}.row{display:flex;gap:8px;align-items:center;justify-content:space-between}button{border:1px solid #9aa5b1;background:#fff;border-radius:6px;width:36px;height:36px;cursor:pointer}svg{width:18px;height:18px;stroke:#1f2937;stroke-width:2;fill:none}</style>
<div class="panel">
  <div class="row"><strong>Book an appointment</strong>
    <button><svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18"/></svg></button></div>
  <div class="row" style="margin-top:10px"><input type="text" aria-label="Search appointments" placeholder="GP name">
    <button><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M16 16l5 5"/></svg></button></div>
</div>
Fixed
<style>body{font-family:system-ui;margin:0;padding:16px}.panel{border:1px solid #cfd6dd;border-radius:8px;padding:12px;max-width:360px}.row{display:flex;gap:8px;align-items:center;justify-content:space-between}button{border:1px solid #9aa5b1;background:#fff;border-radius:6px;width:36px;height:36px;cursor:pointer}svg{width:18px;height:18px;stroke:#1f2937;stroke-width:2;fill:none}</style>
<div class="panel">
  <div class="row"><strong>Book an appointment</strong>
    <button aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6L6 18"/></svg></button></div>
  <div class="row" style="margin-top:10px"><input type="text" aria-label="Search appointments" placeholder="GP name">
    <button aria-label="Search appointments"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="7"/><path d="M16 16l5 5"/></svg></button></div>
</div>

Test it by hand in two minutes

  1. Press Tab to each button on the page. For every one that shows only an icon, right click it, choose Inspect, and look for text inside the button, an aria-label, or an img with alt.
  2. Start NVDA and press Insert+F7, then choose Buttons; or open VoiceOver's rotor with Ctrl+Option+U. Any button listed without a name fails.
  3. If you used aria-label on a button that also shows words, check the label contains those words, otherwise voice control users cannot say what they see.
  4. Shrink the browser until the mobile layout appears. The hamburger menu button that shows up there is the one most often missed.

The fix

Before
<button class="close"><svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
After
<button class="close" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6L6 18"/></svg></button>

Name the action, not the picture: Close, not Cross. Mark the SVG aria-hidden so its paths are not read out as noise before the name.

Visually hidden text

Before
<button class="menu"><span class="bars"></span></button>
After
<button class="menu"><span class="bars" aria-hidden="true"></span><span class="visually-hidden">Menu</span></button>

Hidden text is the most robust option: it is translated, it survives CSS failures, and it needs no ARIA.

Image inside the button

Before
<button><img src="search.svg"></button>
After
<button><img src="search.svg" alt="Search appointments"></button>

The image's alt text becomes the button's name. Do not also add aria-label, or you will have two names to keep in step.

Icon font

Before
<button><i class="fa fa-print"></i></button>
After
<button><i class="fa fa-print" aria-hidden="true"></i> Print</button>

Icon fonts are read as a stray private-use character by some screen readers. Hide the icon and show the word, or hide it and add hidden text.

Common mistakes

  • Using the title attribute as the only name; it is unreliable and never shown on a touch screen.
  • Setting aria-label to something different from the visible text, so voice control users cannot say what they see.
  • Leaving the SVG exposed, so the screen reader reads 'graphic' or 'image' before, or instead of, the name.
  • Naming the button after the icon ('magnifying glass') instead of the action ('Search').
  • Hiding the label text with display:none, which hides it from screen readers as well.

WCAG criteria this rule maps to

axe-core tags this rule as WCAG 2.2 level A. It is one of the checks for:

The scanner can see a button whose text, aria-label, aria-labelledby, image alt and title all amount to nothing. It cannot judge whether the name describes the action, and it will not notice ten buttons on one page all called 'Edit'. Automated tools find only part of the accessibility problems on a page; a pass on this rule is not a pass on the criterion.

Scan your own page for this and 63 other checks

Open any page in Chrome, click Websight, and get a score out of 100 with every issue explained and a draft fix snippet per element.

Add Websight to Chrome

Free scans, no account, nothing leaves your computer. Automated tools find only part of the problems; the extension says so on every report.