Icons Accessibility for Icon-Only Buttons

Icon-only buttons look clean, but without an accessible name they are invisible to screen reader users, so here is how to label them correctly and avoid announcing decorative icons twice.

The Problem with Icon-Only Buttons

A magnifying-glass icon reads as "search" at a glance, and an X reads as "close", but only if you can see it. Screen readers don't interpret the shape of an SVG path or an icon-font glyph; they announce whatever accessible name is attached to the element. When a button contains only an icon and no text, and that icon carries no label, the button has no name at all. A screen reader user tabbing through the page hears nothing more useful than "button", with no clue what pressing it will do.

  • A close button showing only an X icon
  • A search button showing only a magnifying glass
  • A hamburger menu button showing only three lines
  • A trash-can icon used to delete a row in a table

Example

<!DOCTYPE html>
<html>
<head>
</head>
<body>

<!-- This button has no accessible name -->
<button class="icon-btn">
  <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
    <path d="M18 6 6 18M6 6l12 12" stroke="currentColor" stroke-width="2" fill="none"/>
  </svg>
</button>

<style>
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 6px;
  background: #f1f1f1;
  cursor: pointer;
}
</style>

</body>
</html>
Note: Turn on a screen reader (VoiceOver on Mac, NVDA on Windows) and tab to a button like the one above. You'll hear only "button", no hint that it closes anything. That gap is exactly what an accessible name fixes.

Giving the Button an Accessible Name

Example

<!DOCTYPE html>
<html>
<head>
</head>
<body>

<!-- Option A: aria-label directly on the button -->
<button class="icon-btn" aria-label="Close">
  <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
    <path d="M18 6 6 18M6 6l12 12" stroke="currentColor" stroke-width="2" fill="none"/>
  </svg>
</button>

<!-- Option B: visually hidden text, kept in the DOM for assistive tech -->
<button class="icon-btn">
  <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
    <path d="M18 6 6 18M6 6l12 12" stroke="currentColor" stroke-width="2" fill="none"/>
  </svg>
  <span class="visually-hidden">Close</span>
</button>

<style>
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
</style>

</body>
</html>
TechniqueWhen to reach for it
aria-label="Close" on the buttonFastest option, great for simple, static labels. It overrides any visible text, so don't use it alongside text you want announced as-is.
Visually-hidden <span> inside the buttonKeeps real text in the accessible name and in the DOM, which helps translation tools and in-page search; slightly more markup.
title attributeShows a mouse-hover tooltip but is read inconsistently by screen readers and unusable on touch devices, so don't rely on it alone.

Decorative Icons Beside Visible Text

Not every icon needs a label. When a button already shows visible text, like a "Delete" button with a trash-can icon next to the word, the icon is purely decorative. The text already gives screen reader users everything they need, so the icon should not be announced too. Add aria-hidden="true" to the icon in that case. Without it, some screen readers announce the icon's role or an embedded title, so users hear something like "trash icon, Delete, button" instead of the clean "Delete, button".

Example

<!DOCTYPE html>
<html>
<head>
</head>
<body>

<!-- The icon is decorative here, the word "Delete" is the accessible name -->
<button class="text-icon-btn">
  <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false">
    <path d="M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2m-9 0 1 12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1l1-12"
      stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
  </svg>
  Delete
</button>

</body>
</html>
Note: Quick self-check for every icon: if you covered it with your hand, would the button still make sense? If yes, because visible text remains, hide the icon with aria-hidden="true". If no, because the icon is the only content, give the button an accessible name with aria-label or visually-hidden text.

Exercise: Icons Accessibility

A decorative icon sits right next to visible text that already reads "Delete". What should be done for screen reader users?