usability.cat
Issue Wiki

Missing ARIA Labels

Your buttons and links have no accessible names — screen readers just say 'button' with no context. Time to label things.

What is this?

Picture an elevator with a row of identical, unlabeled buttons. No numbers, no symbols, just smooth metal circles. You press one and hope for the best. That is what interactive elements without accessible names feel like to screen reader users — they encounter a button, a link, or a toggle that has no label, so they hear "button" or "link" with zero context about what it does.

ARIA labels (ARIA stands for "Accessible Rich Internet Applications") are attributes you add to HTML elements to give them names that assistive technology can read aloud.

Why it matters

  • For your visitors: When a screen reader encounters a button with only an icon and no label, it might read "button" or, worse, "SVG graphic button." Your visitor has no idea if that button submits a form, opens a menu, or deletes their account. This is not a minor inconvenience — it makes your site unusable.
  • For your business: Icon-only buttons are trendy, but without labels they exclude a significant portion of your audience. Unlabeled controls also confuse voice control users who need to say the name of a button to activate it.
  • The standard: WCAG 4.1.2 (Name, Role, Value) requires that all interactive elements have an accessible name. This means every button, link, and form control must be identifiable.
Labeled interactive elements
<!-- Icon button with aria-label -->
<button aria-label="Close dialog">
  <svg><!-- X icon --></svg>
</button>

<!-- Link with visible text — no extra label needed -->
<a href="/pricing">View pricing</a>
Mystery buttons
<!-- Screen reader says: "button" -->
<button>
  <svg><!-- X icon --></svg>
</button>

<!-- Screen reader says: "link" -->
<a href="/pricing">
  <svg><!-- arrow icon --></svg>
</a>

How to fix it

React / Next.js

The rule is simple: if an element has no visible text, it needs an aria-label. If it already has visible text, you usually do not need one.

// src/components/toolbar.tsx
import { X, Menu, Search, ChevronLeft } from "lucide-react";

export function Toolbar() {
  return (
    <nav aria-label="Main toolbar">
      {/* Icon-only buttons MUST have aria-label */}
      <button aria-label="Open menu">
        <Menu className="h-5 w-5" />
      </button>

      <button aria-label="Search">
        <Search className="h-5 w-5" />
      </button>

      <button aria-label="Close panel">
        <X className="h-5 w-5" />
      </button>

      {/* Button with text — no aria-label needed */}
      <button>
        <ChevronLeft className="h-4 w-4" />
        <span>Back</span>
      </button>

      {/* Multiple navs on a page? Label them to distinguish */}
    </nav>
  );
}

When you have multiple navigations, label each one so screen reader users can tell them apart:

<nav aria-label="Main navigation">{/* primary links */}</nav>
<nav aria-label="Footer navigation">{/* footer links */}</nav>

Plain HTML

<!-- Icon-only button: needs aria-label -->
<button aria-label="Delete item">
  <svg aria-hidden="true"><!-- trash icon --></svg>
</button>

<!-- Icon-only link: needs aria-label -->
<a href="https://twitter.com/yourhandle" aria-label="Follow us on Twitter">
  <svg aria-hidden="true"><!-- twitter icon --></svg>
</a>

<!-- Using aria-labelledby when a visible label exists elsewhere -->
<h2 id="cart-heading">Shopping Cart</h2>
<section aria-labelledby="cart-heading">
  <!-- cart contents -->
</section>

<!-- Social links with only icons -->
<ul aria-label="Social media links">
  <li>
    <a href="https://github.com/you" aria-label="GitHub">
      <svg aria-hidden="true"><!-- github icon --></svg>
    </a>
  </li>
</ul>

When to use which:

  • aria-label — when there is no visible text and you need to provide a name
  • aria-labelledby — when a visible label exists elsewhere on the page (reference it by ID)
  • Neither — when the element already has clear visible text content
High impactaccessibility~2 paws

Unlabeled interactive elements are like unmarked doors — your visitors do not know what is behind them. The cat flags every single one and expects you to fix them all.

How the cat scores this

The cat inspects every interactive element (buttons, links, inputs, selects) and checks for an accessible name. The name can come from text content, aria-label, aria-labelledby, a <label> element, a title attribute, or alt text on a child image. If no accessible name is found, the cat flags it. Elements with only whitespace or only SVGs/icons with no text alternative are caught.

Further reading

On this page