iOS Voiceover Safari
Android Talkback Chrome
Windows Jaws Chrome
Windows NVDA Chrome
Buttons vs links
If it goes somewhere, it’s
- 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
If it does something, it’s a
- 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
Use semantic HTML
- This semantic HTML contains all accessibility features by default.
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-labelcan be used.
aria-labelwill (typically) replace the inner text of the button for the screen reader output.
- DO NOT repeat the inner text in the
aria-labelas 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.
- Inner text should describe the purpose of the button.
aria-label="Button purpose"can also be used (as a last resort)
- Native button identifies as button by default
role="button"for custom elements
aria-haspopup="true"for menu, listbox or modal
aria-controls="popupId"is not well supported
- Toggle buttons
- Menus or expanders use
- Use the
disabledstate for completely inactive buttons that shouldn’t be focusable
aria-disabled="true/false"state for inactive custom elements
- Focus must be visible
- Custom elements (like
tabindex="0"to be focusable