Getting Text Alternatives for Icon-only Buttons Right
AI generated
A11Y
WCAG
Accessibility · Icon Buttons
Getting Text Alternatives for Icon-only Buttons Right
Visually hidden text, aria-label, and the title attribute compared head to head, with practical examples from Hyva

A cart icon, a magnifying glass for search, or a plain icon for social share buttons looks self-explanatory to sighted users, but to a screen reader such a button without extra markup is simply a nameless button element. This article compares the three common patterns, visually hidden text, aria-label, and the title attribute, on solid technical grounds, and shows using cart, search, and social share icons in Hyva which pattern actually works reliably and when.

12 min read Icon Buttons aria-label

1. The problem: icon-only buttons without visible text

A button consisting purely of an SVG icon, say a cart symbol or a magnifying glass, has no accessible name by default from the accessibility tree's point of view, unless the icon itself carries an embedded, visible text element. A screen reader then announces such a button merely as a button, with no information whatsoever about what function is hiding behind it, which effectively turns operation into guesswork for blind users.

The problem sits purely at the programmatic level, not the visual one: sighted users usually recognize the meaning of a cart icon reliably, since it's a largely standardized visual convention. But that implicit, purely visual convention doesn't automatically carry over into a programmatically accessible name, which is why an explicit text alternative needs to be added technically.

2. The visually hidden text pattern: the most robust solution

The visually hidden text pattern places real, actual text in the DOM but hides it purely visually via a CSS class that removes the text from the layout without deleting it from the accessibility tree. Unlike display none or visibility hidden, which also make an element invisible to screen readers, text hidden through off-screen positioning stays fully readable.

The decisive advantage of this pattern over aria-label is that the text exists as real, DOM-present content and thus also gets picked up by tooling that doesn't reliably process pure ARIA attribute content, say certain translation tools, browser extensions, or older screen reader versions. The text can also be made visible again on demand via JavaScript or CSS, say at a responsive breakpoint where there's enough room for visible text.


.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

3. aria-label: pros and cons per screen reader

aria-label sets an element's accessible name directly as an attribute value, with no additional DOM-visible text needed, which makes the pattern especially compact and simple to apply. In practice, aria-label is reliably supported by all relevant screen readers, NVDA, JAWS, and VoiceOver, making it technically equivalent to the visually hidden text pattern for pure read-out purposes.

The practical downside of aria-label shows up with tooling that doesn't hook into the accessibility API but reads visible or DOM-present text directly instead, say browser translation features, in-browser text search via Ctrl-F, or certain test automation tools that primarily target visible text rather than ARIA attributes. An aria-label is also easy to forget updating when a button's function changes, since it leaves no visible trace in the markup that would jump out during a code review.

4. The title attribute: why it isn't a reliable solution

Many developers mistakenly use the title attribute as a simple text alternative for icon buttons, since it additionally produces a visual tooltip on mouse hover. In reality, though, screen reader support for title as an accessible name is inconsistent: some browser and screen reader combinations read title out, others ignore it entirely, especially once an aria-label or visible text is also present, which then takes precedence.

A further practical problem concerns keyboard and touch users: a title tooltip only appears on mouse hover, meaning it's fundamentally unreachable for pure keyboard users and for touch devices without hover capability, regardless of screen reader support. The title attribute should therefore be understood at most as visual supplementary information for mouse users, never as the sole, reliable text alternative for the accessible name.

5. Tooltip compatibility: pairing a visible tooltip with a programmatic name

For icon buttons meant to give both a sighted mouse user and a screen reader user the same information, the recommended combination is a custom, JavaScript-driven tooltip element paired with the visually hidden text pattern, rather than relying on the unreliable title attribute. The tooltip delivers the visible information on hover or focus, while the button's accessible name stays fixed independently via the hidden text.

It's important to make the tooltip appear on keyboard focus as well, not just mouse hover, so sighted keyboard users, say people with motor impairments, get the same visual information as mouse users. Such a tooltip should also be wired up via role tooltip and an aria-describedby link, not via aria-label, since aria-label would otherwise overwrite or duplicate the hidden text already present.

6. Practical example: the cart icon in Hyva

In a Hyva theme, the cart button in the header usually consists of an SVG icon plus a dynamically updated item-count badge. The accessible name needs to reflect both the base function, open cart, and the dynamic item count, since otherwise a plain icon text won't update after a product gets added, and screen reader users end up hearing a stale number.

The most robust implementation combines a visually hidden base text with a reactively updated, likewise hidden count text driven by Alpine.js, so the entire accessible name gets read out correctly on every cart change automatically, without needing a separate aria-live update, since the name gets re-read anyway the next time the button receives focus.


<button
  type="button"
  class="relative"
  x-data="{ count: 3 }"
  aria-label="Open cart"
>
  <svg class="w-6 h-6" aria-hidden="true"><!-- icon path --></svg>
  <span
    class="sr-only"
    x-text="count + ' items in cart'"
  ></span>
  <span
    class="absolute -top-1 -right-1 bg-red-600 text-white text-xs rounded-full px-1.5"
    aria-hidden="true"
    x-text="count"
  ></span>
</button>

7. Practical example: the search icon in the header

The magnifying glass icon in the header carries a dual function in many Hyva themes: in the closed state it opens the search field, in the open state the very same button closes the search again. A static aria-label like open search becomes semantically wrong after the first click in this case, since the button then actually closes rather than opens, even though the label stays unchanged.

The correct solution binds the aria-label reactively to the open state, so it delivers either open search or close search depending on the state, combined with aria-expanded, which additionally communicates the current state structurally to assistive technology, independent of the label's pure text content.


<button
  type="button"
  x-data="{ open: false }"
  x-on:click="open = !open"
  :aria-label="open ? 'Close search' : 'Open search'"
  :aria-expanded="open"
>
  <svg class="w-6 h-6" aria-hidden="true"><!-- magnifier or X icon --></svg>
</button>

8. Practical example: social share icons

For a row of social share icons, say for Facebook, X, and Pinterest, the same basic problem repeats several times in a row: every icon needs to name both the channel and the action in its accessible name, say share on Facebook rather than just Facebook, since the plain brand name without an action verb doesn't provide enough information about the button's actual function.

For a group of related share buttons, an enclosing nav with a fitting aria-label like share on social networks should also bundle the individual buttons semantically, so screen reader users recognize the group as a coherent section instead of hearing five individual, context-free buttons read out one after another.

9. Decision guide: which pattern when

For static icon buttons with no dynamic content, visually hidden text and aria-label are technically nearly equivalent, though visually hidden text remains the more robust default choice because of its better compatibility with tooling outside the pure accessibility API. Visually hidden text also suits dynamic content like the cart counter particularly well, since it can be updated with the same reactivity mechanisms as the rest of the Alpine.js component.

aria-label remains a sensible choice when the accessible name can be derived simply and purely reactively from a state, as with the search icon's two states of open and closed, while the title attribute isn't advisable as the sole solution in any of the cases described here, and should at most be added as a supplementary visual tooltip for mouse users. The table below summarizes support for the three patterns across common screen readers.

Pattern NVDA JAWS VoiceOver
Visually hidden text Reliably supported Reliably supported Reliably supported
aria-label Reliably supported Reliably supported Reliably supported
title attribute Inconsistent, often ignored Inconsistent, often ignored Usually not read out
No text pattern Announces only button Announces only button Announces only button

Mironsoft

WCAG audits, accessible Magento shops, and training

Not sure whether the shop is actually accessible?

We audit existing Magento shops against WCAG 2.2, fix concrete barriers in the Hyvä frontend, and train teams so accessibility stays anchored in the development process for good.

WCAG Audit

Systematically review the shop against WCAG 2.2 AA, with a prioritized issue list.

Fixing Barriers

Concrete implementation: keyboard operability, screen reader support, contrast, forms.

Team Training

Raise developer and editor awareness for accessible implementation day to day.

10. Summary

Text Alternatives for Icon Buttons: The Essentials at a Glance

Core problem

An icon-only button has no accessible name for screen readers without extra work.

Most robust choice

Visually hidden text, since it stays real DOM content and is more broadly compatible than aria-label.

Unreliable

The title attribute is handled inconsistently by screen readers and is unreachable for touch.

Dynamic buttons

State-dependent buttons like search need a reactive aria-label plus aria-expanded.

11. FAQ: Text Alternatives for Icon Buttons: The Essentials at a Glance

1Why does a plain icon with no text have no accessible name?
The accessibility tree derives the name from text content, and an SVG icon provides none by default.
2What's the main advantage of the visually hidden text pattern?
The text stays real DOM content and also gets picked up by tooling that doesn't process pure ARIA attributes.
3Is aria-label supported by all relevant screen readers?
Yes, NVDA, JAWS, and VoiceOver reliably support aria-label as an accessible name.
4Why isn't the title attribute a reliable solution?
Support as an accessible name is inconsistent, and the tooltip only appears on mouse hover.
5How do you pair a visible tooltip with an accessible name?
Via a custom tooltip element with role tooltip and aria-describedby, not via title or a duplicate aria-label.
6How should the cart button communicate its content?
Via visually hidden text that names both the base function and the current item count together.
7Why does the search icon need a reactive aria-label?
Because its function changes between open and close depending on state, and the label needs to follow.
8What does aria-expanded add on the search icon?
It communicates the current state structurally, independent of the label's pure text content.
9How should social share icons be named?
With channel and action verb, say share on Facebook, not just the brand name.
10Should a group of share buttons be structured further?
Yes, via an enclosing nav with a fitting aria-label, so the group is recognizable as a unit.