All rules

Sheath rule

a11y-button-accessible-name

Buttons must have an accessible name for screen readers.
Package
Core
Default severity
error by default
Auto-fix
Manual fix

#Why

Buttons need text content, aria-label, aria-labelledby, or title to be accessible to screen reader users. Without an accessible name, users cannot understand what action the button performs.

#Examples

#Bad

<!-- Empty button -->
<button></button>
<!-- Icon-only button without accessible name -->
<button><i class="icon-save"></i></button>
<!-- Input button without value -->
<input type="button">
<!-- Image input without alt -->
<input type="image" src="submit.png">

#Good

<!-- Text content -->
<button>Save Changes</button>
<!-- Icon with aria-label -->
<button aria-label="Save"><i class="icon-save"></i></button>
<!-- A child SVG may contribute the name -->
<button><svg aria-label="Save"></svg></button>
<!-- Using aria-labelledby -->
<span id="btn-label">Submit Form</span>
<button aria-labelledby="btn-label"><i class="icon-check"></i></button>
<!-- Input with value -->
<input type="button" value="Click Me">
<!-- Submit/reset have default accessible names -->
<input type="submit">
<input type="reset">
<!-- Image input with alt -->
<input type="image" src="submit.png" alt="Submit Order">
<!-- Using title attribute -->
<button title="Close dialog"><i class="icon-x"></i></button>
<!-- Native label association -->
<label for="save-button">Save changes</label>
<button id="save-button"></button>

#Notes

  • Text inside hidden, inert, aria-hidden="true", script, style, or template descendants does not name the button. A hidden element explicitly referenced by aria-labelledby can still provide a name.
  • Visible text serves everyone, not only screen reader users
  • aria-label is useful for icon-only buttons
  • Submit and reset inputs have browser-default accessible names ("Submit" and "Reset") when value is omitted; an explicitly empty value has no accessible name
  • Image inputs require an alt attribute for accessibility
  • Opaque attribute spreads such as {{ $attributes }} make the final name unknowable and are not checked. Every explicit branch that produces an exposed button must have a name
  • A static aria-labelledby value names the button only when at least one referenced id exists in the rendered document
  • A button with a non-empty Alpine x-text or x-html expression, or Livewire wire:text expression (<button wire:text="label"></button>), is treated as having content because client-side code fills the empty body before the user interacts with it. Bare, empty, and whitespace-only expressions do not count

#References

#Related Rules