How to Label Buttons Accessibly: Complete Guide (2026)

Saar Twito9 min read
Saar Twito
Saar TwitoFounder & SEO Engineer

Hi, I'm Saar - a software engineer, SEO specialist, and lecturer who loves building tools and teaching tech.

View author profile →

What Is an Accessibly Labeled Button?

An accessibly labeled button is a <button> (or element with role="button") that has a clear, descriptive accessible name— the text a screen reader announces when the user lands on it. A correctly labeled submit button is announced as "Submit application, button" rather than just "button." Without that name, screen-reader, voice-control, and AI-agent users cannot tell what the control does.

Key Facts (TL;DR)

  • WCAG criterion: SC 4.1.2 Name, Role, Value (Level A) requires every interactive control to have a programmatically determinable name. SC 2.4.4 Link Purpose (Level A) applies when buttons act like links.
  • The four ways to give a button a name (in order of preference): visible text, aria-label, aria-labelledby, visually hidden text.
  • Most common failure: icon-only buttons (close × , trash, share) with no aria-label — they announce as "button" with no purpose.
  • Scale of the problem: the WebAIM Million 2025 report found that buttons with empty accessible names appear on roughly 27.7% of home pages tested — making it one of the most common WCAG failures on the web.
  • Conversion impact: A/B tests consistently show that specific button text ("Buy now", "Start free trial") outperforms generic text ("Submit", "Click here") — clarity isn't just an accessibility win, it's a revenue lever.
  • AI agents: Agentic browsers and LLM-powered assistants parse button text to plan actions. A button with no accessible name is effectively invisible to them.

Think of a button's accessible name the way you think of the label on a light switch in a server room. Sighted users can sometimes guess from context, but anyone navigating in the dark — by screen reader, by voice, or by automated agent — needs the label to know whether they're flipping "Save" or "Delete account."

Why Button Labels Matter for Real Users

Every assistive-tech path through a page touches the button's accessible name. When that name is missing or vague, the failure mode is immediate and total:

  • Screen readers (NVDA, JAWS, VoiceOver):Without a name, the user hears only "button." They have no way to know which control deletes their account and which one cancels the dialog.
  • Voice control (Voice Control on macOS/iOS, Dragon, Voice Access):Users say "click [button name]" to activate controls. A button with no name is unreachable by voice.
  • Switch and keyboard users: Tab focus lands on the button, but speech output gives no clue what activating it will do — many users simply skip it.
  • AI agents and scrapers:AI-driven browsing agents (and Google's rendering pipeline) parse button text to plan actions. An anonymous button cannot be invoked, summarized, or indexed.
  • SEO:Buttons with descriptive text contribute to page intent signals. Generic "Click here" buttons add nothing semantic; specific text ("Download pricing PDF") reinforces topical relevance.
  • Conversion:Specific button copy consistently beats vague copy in A/B tests. "Start free trial" outperforms "Submit"; "Add to cart" outperforms "Add."

The Four Ways to Give a Button an Accessible Name

WCAG and the WAI-ARIA Authoring Practices recognize four techniques for naming a button. Use them in this order of preference — visible text first, hidden text last.

<!-- 1. Visible text inside the button (best) -->
<button type="submit">Save changes</button>

<!-- 2. aria-label for icon-only buttons -->
<button type="button" aria-label="Close dialog">
  <svg aria-hidden="true" focusable="false" width="16" height="16">
    <path d="M2 2 L14 14 M14 2 L2 14" stroke="currentColor" />
  </svg>
</button>

<!-- 3. aria-labelledby when the label exists elsewhere -->
<span id="cart-label">Add wireless headphones to cart</span>
<button type="button" aria-labelledby="cart-label">
  <svg aria-hidden="true" focusable="false">...</svg>
</button>

<!-- 4. Visually hidden text inside the button -->
<button type="button">
  <svg aria-hidden="true" focusable="false">...</svg>
  <span class="sr-only">Search products</span>
</button>

<!-- The .sr-only utility class -->
<style>
.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;
}
</style>

Why Visible Text Wins

Visible text is announced by every screen reader, indexed by search engines, parseable by AI agents, and targetable by voice control with no extra attributes. aria-label works for screen readers and voice control but is invisible to sighted users and to most search-engine text extraction. Reach for ARIA only when there is genuinely no room for visible text.

Don't Double-Announce the Icon

When a button uses an SVG or icon font, set aria-hidden="true" on the icon and (for SVG) focusable="false". Otherwise screen readers may announce the icon's tooltip plusthe button's accessible name, producing "trash icon, Delete photo, button."

How to Check Your Buttons

Accessible-name failures are easy to introduce and easy to detect. A combination of automated and manual checks catches almost all of them.

  • Greadme deep scan — flags every button with an empty or generic accessible name, points to the offending element, and pairs each issue with an AI-generated fix or one-click GitHub PR.
  • Greadme crawler scan — runs the same checks across every indexable page on your site, so you can find the button-naming failure that's repeated on every product card or every modal template.
  • Greadme AI visibility analyzer — checks whether AI agents can identify and reason about your CTAs based on their accessible names, not just their visual design.
  • axe-core (browser extension or CI) — open-source rules engine that flags button-name violations directly. Free and reliable for the "empty name" case.
  • Screen reader walk-through — tab through your page with NVDA (Windows), JAWS (Windows), or VoiceOver (macOS/iOS). Every button should announce a name plus the word "button."
  • Chrome DevTools > Accessibility pane — selects any element and shows its computed accessible name, role, and ARIA properties in real time.

8 Patterns for Specific Button Types

1. Text Buttons — Be Specific

Generic verbs like "Submit" and "OK" technically pass automated audits but still fail users. Replace them with action + object: <button>Submit application</button>, <button>Save changes</button>, <button>Buy now</button>.

2. Icon-Only Buttons — Always aria-label

The most common WCAG failure on the web. Every icon-only button needs an aria-labelthat says what it does, not what it looks like. "Delete item" — not "Trash can."

<!-- Wrong: icon with no name -->
<button><svg>...</svg></button>

<!-- Right: aria-label describes the action -->
<button type="button" aria-label="Delete item">
  <svg aria-hidden="true" focusable="false">...</svg>
</button>

3. Toggle Buttons — Use aria-pressed

For buttons that switch a binary state (mute, follow, bookmark), keep the label stable and let aria-pressedcarry the state. Screen readers announce "Mute, toggle button, pressed" vs "not pressed." Don't flip the label between "Mute" and "Unmute" — that confuses users who rely on consistent landmarks.

<button type="button" aria-pressed="false" onclick="toggleMute(this)">
  Mute
</button>

4. Disclosure Buttons — Use aria-expanded

For buttons that show or hide content (accordions, menus, "Read more"), use aria-expanded="true|false" and keep the label describing the target, not the state.

<button type="button" aria-expanded="false" aria-controls="details">
  Show shipping details
</button>
<div id="details" hidden>...</div>

5. Close Buttons — "×" Is Not a Name

The character "×" (multiplication sign) has no consistent accessible name across screen readers. Always pair it with an aria-label: <button aria-label="Close dialog">×</button>. Bonus: set aria-hidden="true"on the visible "×" if you also include hidden text, so it isn't double-announced.

6. Image Buttons — alt Is the Name

When a button contains only an <img>, the image's alt attribute becomes the button's accessible name. An empty alt="" means an unlabeled button.

<!-- Wrong: empty alt = no name -->
<button><img src="/print.png" alt=""></button>

<!-- Right: alt describes the action -->
<button><img src="/print.png" alt="Print invoice"></button>

7. Buttons With Visible Text + an Icon

When the button has both, hide the icon from assistive tech (aria-hidden="true") and let the text serve as the name. Don't add a redundant aria-label— if you do, it overrides the visible text and creates the "label mismatch" problem (visible says "Save", screen reader says "Submit form"), which fails WCAG SC 2.5.3 Label in Name.

8. Custom Buttons — Use <button>, Not <div>

<div onclick> styled like a button has no role, no keyboard support, no name, and no focus ring. If you absolutely must build a custom control, you need role="button", tabindex="0", keyboard handlers for Enter and Space, and an accessible name. Or just use <button>and reset its styles — it's 95% less work.

Common Button-Labeling Failures and Fixes

Problem: Icon-Only Button With No aria-label

What's happening: The button contains only an SVG, icon font, or image with empty alt. Screen readers announce just "button." Voice control users cannot target it.

Fix: Add aria-label="Action description" to the button and aria-hidden="true" to the icon. The label should describe the action ("Delete item"), not the icon ("Trash can").

Problem: Generic Text ("Click Here", "Submit", "OK")

What's happening: The button passes automated audits because it has a name, but the name carries no information out of context.

Fix: Use action + object. Replace "Submit" with "Submit application," "Click here" with "Download pricing PDF," "OK" with "Confirm deletion."

Problem: aria-label Contradicts Visible Text

What's happening: The button shows "Save" but aria-label="Submit form" overrides it. Voice-control users say "click Save" and nothing happens — the button's accessible name is "Submit form." This fails WCAG SC 2.5.3 Label in Name.

Fix: Remove the aria-label. The visible text is already the accessible name. Only override visible text if the visible text is genuinely insufficient (rare).

Problem: <div onclick> Styled Like a Button

What's happening: The element looks like a button but has no role, no keyboard activation, no focus ring, and no accessible name.

Fix: Replace it with <button type="button">. If a refactor is too disruptive, add role="button", tabindex="0", an accessible name, and keyboard handlers for Enter and Space — but the native element is almost always less code.

aria-label vs Visible Text vs Hidden Text — Which to Use

All three give a button an accessible name, but they differ in how they affect sighted users, search engines, and AI extraction. Pick the technique that matches the design constraint.

TechniqueVisible to Sighted UsersRead by Screen ReadersIndexed for SEO/AIBest For
Visible text inside <button>YesYesYesDefault — use whenever there is room for a label.
aria-labelNoYesLimited (some crawlers ignore it)Icon-only buttons, close (×) buttons.
aria-labelledbyNo (but references visible text)YesLimitedWhen the label already exists elsewhere on the page (e.g. card titles).
Visually hidden <span>NoYesYes (it's real DOM text)Icon buttons where you also want the name in the DOM for indexing.

FAQ

What WCAG criterion covers button labels?

The primary criterion is WCAG 2.2 SC 4.1.2 Name, Role, Value (Level A), which requires every interactive control to have a programmatically determinable name and role. SC 2.4.4 Link Purpose (Level A) applies when buttons act like links, and SC 2.5.3 Label in Name (Level A) applies when visible text and the accessible name disagree. Failing any of these is a Level A violation — the strictest tier.

Is aria-label or visible text better for buttons?

Visible text. It is announced by every screen reader, indexed by search engines, parseable by AI agents, targetable by voice control without extra attributes, and useful to sighted users. aria-labelis appropriate only when visible text genuinely won't fit — the canonical case is an icon-only button.

Why does my icon button fail axe-core or the Greadme deep scan?

Almost always because the button has no accessible name. The <svg> or icon font inside it is not text, and without an aria-label, hidden text, or alt attribute on a contained image, the computed accessible name is empty. Add aria-label="Action description" to the button and aria-hidden="true" to the icon and re-run the scan.

Should I use aria-pressed or change the button text for toggles?

Use aria-pressed. Keeping a stable label ("Mute") and toggling aria-pressed="true|false"means screen readers announce the state explicitly ("Mute, toggle button, pressed") and sighted users always see the same control in the same place. Swapping text between "Mute" and "Unmute" on every click moves the visual landmark and confuses keyboard users.

Does the "×" character in a close button count as a name?

Inconsistently. The multiplication sign (×) is announced as "multiplication" by some screen readers, "times" by others, and skipped by some. Always pair it with aria-label="Close dialog" (or similar) on the button. If you want the visible × to remain decorative, set aria-hidden="true" on it and let the aria-label carry the name.

Do AI search engines like ChatGPT, Perplexity, and Google AI Overviews use button labels?

Yes. Agentic AI systems and rendering pipelines parse button text to understand page intent and (for agents) to plan actions. A button with no accessible name cannot be invoked by an AI agent and contributes no semantic signal to indexing. Specific, descriptive button text helps both human users and AI systems understand what your CTAs do.

How does button labeling affect SEO and conversion?

SEO: descriptive button text contributes to topical relevance signals and feeds in-page interaction crawls. "Download pricing PDF" tells Google what the page is about; "Click here" tells it nothing. Conversion: A/B tests consistently show specific CTAs ("Start free trial", "Buy now") outperform generic ones ("Submit", "Continue") — clear button copy is one of the highest-ROI accessibility improvements you can ship.

Conclusion

Button labeling is the lowest-effort, highest-impact accessibility fix on most sites. Visible text wherever possible; aria-label for icon-only buttons; aria-pressed for toggles; aria-expanded for disclosures; never <div onclick>. Get those five right and you've eliminated the most common WCAG Level A failure on the web — the same one that appears on roughly 28% of home pages tested.

Run a Greadme deep scan to see exactly which buttons on your site have empty or generic accessible names, then fix them in order of impact.