Video examples
iOS Voiceover
Android Talkback
Windows Jaws Chrome
Windows NVDA Chrome
Buttons vs links
If it goes somewhere, it’s <a>
link.
- When the user clicks a link, they are taken to a different location in the site.
- Either another page or even another area of the same page
- A link can look like a big shiny button but it must be coded as
<a>
link
If it does something, it’s a <button>
- Buttons cause an action to occur on the same page
- Submit a form (even when submission takes you to a new page)
- Open a menu
- Launch a modal
- Expand details
- A button can look like a link, but it must be coded as a
<button>
Code examples
Use semantic HTML
- This semantic HTML contains all accessibility features by default.
- It uses CSS pseudo attributes to create the arrow indicator, no Javascript.
Focusable disabled button
The preferred method is to use aria-disabled="true"
so screen reader users can find the button, click submit and be notified of errors in the form.
Fully disabled button
A button that uses the disabled attribute will not be focusable, but it is still discoverable by the screen reader while browsing.
When you can’t use semantic HTML
This custom button requires extra attributes and JS event listeners. Adding tabindex="0"
makes it focusable.
When there’s no inner text that text doesn’t make sense
- As a last resort,
aria-label
can be used. aria-label
will (typically) replace the inner text of the button for the screen reader output.- DO NOT repeat the inner text in the
aria-label
as some screenreaders will read both.
When there are repeating buttons
Sometimes the design will call for multiple buttons with the same text label. In a case like this, aria-label
can be used to name each control’s purpose.
Developer notes
Name
- Inner text should describe the purpose of the button.
aria-label="Button purpose"
can also be used (as a last resort)
Role
- Native button identifies as button by default
- Use
role="button"
for custom elements
Group
- Use
aria-haspopup="true"
for menu, listbox or modal aria-controls="popupId"
is not well supported
State
- Toggle buttons
aria-pressed="true/false"
- Menus or expanders use
aria-expanded="true/false"
- Use the
disabled
state for completely inactive buttons that shouldn’t be focusable - Use
aria-disabled="true/false"
state for inactive custom elements
Focus
- Focus must be visible
- Custom elements (like
<div>
) needtabindex="0"
to be focusable