Missing or Incorrect ARIA Labels | Blue Frog Docs

Missing or Incorrect ARIA Labels

Diagnose and fix ARIA labeling issues to make interactive elements accessible to screen reader users

Missing or Incorrect ARIA Labels

What This Means

ARIA (Accessible Rich Internet Applications) labels provide accessible names and descriptions for interactive elements that screen readers announce to users. Missing or incorrect ARIA labels prevent users with disabilities from understanding and interacting with buttons, links, form controls, and custom widgets.

Impact on Your Business

Legal Compliance:

  • ARIA is part of WCAG 2.1 accessibility requirements
  • Missing labels can trigger ADA and Section 508 violations
  • Interactive elements without accessible names are Level A failures
  • Can result in legal action and financial penalties

User Experience:

  • 15% of the global population has some form of disability
  • Screen reader users cannot identify unlabeled buttons and controls
  • Custom widgets become completely unusable without proper ARIA
  • Creates frustrating, broken experiences for keyboard users

SEO and Quality:

  • Accessibility issues can affect search rankings
  • Poor accessibility signals low-quality user experience
  • Reduces potential audience and customer base

How to Diagnose

  1. Install axe DevTools Extension
  2. Open Chrome DevTools (F12)
  3. Navigate to "axe DevTools" tab
  4. Click "Scan ALL of my page"
  5. Review violations:
    • "Buttons must have discernible text"
    • "Links must have discernible text"
    • "Form elements must have labels"
    • "ARIA attributes must be valid"

What to Look For:

  • Elements flagged for missing accessible names
  • Invalid or incorrect ARIA attribute usage
  • Custom widgets without proper roles

Method 2: WAVE Browser Extension

  1. Install WAVE Extension
  2. Navigate to your webpage
  3. Click the WAVE icon
  4. Review the sidebar:
    • Red errors for missing labels
    • Alerts for suspicious ARIA usage
    • Click icons to see specific elements

Method 3: Chrome DevTools Accessibility Tree

  1. Open Chrome DevTools (F12)
  2. Navigate to Elements tab
  3. Click Accessibility in the sidebar
  4. Inspect each element:
    • Check "Computed Properties"
    • Verify "Name" has a value
    • Check "Role" is appropriate

Method 4: Screen Reader Testing

  1. Install a screen reader:

    • NVDA (Windows, free)
    • VoiceOver (Mac, built-in: Cmd+F5)
    • ChromeVox (Chrome extension)
  2. Navigate to your page

  3. Tab through interactive elements

  4. Listen to what's announced

What to Look For:

  • "Button" announced without a name
  • "Link" with no destination description
  • Custom controls with no role/name
  • Confusing or redundant announcements

Method 5: Browser Console Check

// Find buttons without accessible names
document.querySelectorAll('button').forEach(btn => {
  if (!btn.textContent.trim() && !btn.getAttribute('aria-label') && !btn.getAttribute('aria-labelledby')) {
    console.log('Button missing label:', btn);
  }
});

// Find links without accessible names
document.querySelectorAll('a').forEach(link => {
  if (!link.textContent.trim() && !link.getAttribute('aria-label')) {
    console.log('Link missing label:', link);
  }
});

// Find form inputs without labels
document.querySelectorAll('input, select, textarea').forEach(input => {
  const id = input.id;
  const hasLabel = id && document.querySelector(`label[for="${id}"]`);
  const hasAriaLabel = input.getAttribute('aria-label') || input.getAttribute('aria-labelledby');
  if (!hasLabel && !hasAriaLabel) {
    console.log('Input missing label:', input);
  }
});

General Fixes

Fix 1: Add aria-label to Icon Buttons

For buttons with only icons:

<!-- Bad - no accessible name -->
<button>
  <svg><!-- icon --></svg>
</button>

<!-- Good - aria-label provides name -->
<button aria-label="Close dialog">
  <svg aria-hidden="true"><!-- icon --></svg>
</button>

<!-- Also good - visually hidden text -->
<button>
  <svg aria-hidden="true"><!-- icon --></svg>
  <span class="sr-only">Close dialog</span>
</button>

CSS for screen reader only 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;
}

Fix 2: Label Form Controls

For inputs, selects, and textareas:

<!-- Bad - input with no label -->
<input type="email" placeholder="Email">

<!-- Good - explicit label -->
<label for="email">Email address</label>
<input type="email" id="email" placeholder="you@example.com">

<!-- Good - aria-label when no visible label -->
<input type="search" aria-label="Search products" placeholder="Search...">

<!-- Good - aria-labelledby for complex labels -->
<span id="email-label">Email</span>
<span id="email-hint">(We'll never share)</span>
<input type="email" aria-labelledby="email-label email-hint">

For links containing only icons:

<!-- Bad - meaningless link -->
<a href="/cart">
  <svg><!-- cart icon --></svg>
</a>

<!-- Good - aria-label -->
<a href="/cart" aria-label="View shopping cart (3 items)">
  <svg aria-hidden="true"><!-- cart icon --></svg>
</a>

<!-- Good - visually hidden text -->
<a href="/cart">
  <svg aria-hidden="true"><!-- cart icon --></svg>
  <span class="sr-only">View shopping cart (3 items)</span>
</a>

Fix 4: Label Custom Widgets

For custom interactive components:

<!-- Bad - custom dropdown with no role or label -->
<div class="dropdown" onclick="toggle()">
  <span>Select option</span>
  <ul class="options">...</ul>
</div>

<!-- Good - proper ARIA attributes -->
<div
  role="combobox"
  aria-label="Select country"
  aria-expanded="false"
  aria-haspopup="listbox"
  aria-controls="country-options"
  tabindex="0"
>
  <span>Select country</span>
</div>
<ul id="country-options" role="listbox" aria-label="Countries">
  <li role="option" aria-selected="false">United States</li>
  <li role="option" aria-selected="false">Canada</li>
</ul>

Fix 5: Use aria-describedby for Additional Context

For elements needing more description:

<!-- Password field with requirements -->
<label for="password">Password</label>
<input
  type="password"
  id="password"
  aria-describedby="password-requirements"
>
<p id="password-requirements">
  Must be at least 8 characters with one number
</p>

<!-- Button with confirmation context -->
<button aria-describedby="delete-warning">Delete Account</button>
<p id="delete-warning">This action cannot be undone</p>

Fix 6: Fix Redundant or Conflicting ARIA

Avoid common ARIA mistakes:

<!-- Bad - redundant role (button already has button role) -->
<button role="button">Submit</button>

<!-- Good - no redundant role needed -->
<button>Submit</button>

<!-- Bad - conflicting/invalid ARIA -->
<button aria-hidden="true">Submit</button>

<!-- Bad - using ARIA instead of semantic HTML -->
<div role="button" tabindex="0" onclick="submit()">Submit</div>

<!-- Good - use semantic HTML -->
<button onclick="submit()">Submit</button>

<!-- Bad - aria-label overrides visible text unnecessarily -->
<button aria-label="Click here">Submit Form</button>

<!-- Good - visible text is the accessible name -->
<button>Submit Form</button>

Fix 7: Label Regions and Landmarks

For page structure elements:

<!-- Navigation regions -->
<nav aria-label="Main navigation">...</nav>
<nav aria-label="Footer navigation">...</nav>

<!-- Multiple similar sections -->
<section aria-labelledby="featured-heading">
  <h2 id="featured-heading">Featured Products</h2>
  ...
</section>

<section aria-labelledby="sale-heading">
  <h2 id="sale-heading">On Sale</h2>
  ...
</section>

Fix 8: Dynamic Content Updates

For content that changes dynamically:

<!-- Live region for status updates -->
<div aria-live="polite" aria-atomic="true">
  <!-- Dynamic content announced when changed -->
  Item added to cart
</div>

<!-- More urgent announcements -->
<div role="alert">
  Error: Please enter a valid email address
</div>

<!-- Progress indicators -->
<div
  role="progressbar"
  aria-label="Upload progress"
  aria-valuenow="75"
  aria-valuemin="0"
  aria-valuemax="100"
>
  75% complete
</div>

Platform-Specific Guides

Platform Troubleshooting Guide
Shopify Shopify ARIA Guide
WordPress WordPress ARIA Guide
Wix Wix ARIA Guide
Squarespace Squarespace ARIA Guide
Webflow Webflow ARIA Guide

Verification

After adding ARIA labels:

  1. Re-run axe DevTools:

    • Verify ARIA-related violations are resolved
    • Check for new issues introduced
    • Confirm elements have accessible names
  2. Test with screen reader:

    • Tab through all interactive elements
    • Verify meaningful announcements
    • Test custom widgets work correctly
  3. Check Accessibility Tree:

    • Open DevTools → Elements → Accessibility
    • Verify each element has proper Name and Role
    • Confirm no missing or incorrect values
  4. Keyboard navigation test:

    • Can you reach all interactive elements with Tab?
    • Do focus indicators show clearly?
    • Can you activate controls with Enter/Space?

Common Mistakes

  1. Using aria-label on non-interactive elements - Only use on elements that can receive focus
  2. aria-label overriding visible text - Creates confusing mismatch
  3. Empty aria-label values - aria-label="" provides no name
  4. Using role="presentation" on interactive elements - Removes semantics
  5. Incorrect ARIA role - Using wrong role for element type
  6. Missing required ARIA states - Some roles require specific attributes
  7. aria-hidden="true" on focusable elements - Creates invisible but focusable elements
  8. Duplicate IDs with aria-labelledby - References wrong element
  9. Not hiding decorative icons - Add aria-hidden="true" to decorative elements
  10. Over-using ARIA - Use semantic HTML first, ARIA as supplement

ARIA Decision Tree

Does the element need an accessible name?
├─ Native HTML provides it (visible text, label element)
│  └─ No ARIA needed
│
├─ No visible text (icon button, icon link)
│  └─ Add aria-label or visually hidden text
│
├─ Need to reference other elements for name
│  └─ Use aria-labelledby with ID references
│
└─ Custom widget with no native semantics
   ├─ Add appropriate role
   ├─ Add aria-label or aria-labelledby
   └─ Add required ARIA states/properties

Additional Resources

// SYS.FOOTER