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
<button>
Continue
</button>
This semantic HTML looks like a link, but it is still a button.
<button class="inline">
Continue
</button>
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.
<button disabled>
Continue
</button>
Focusable disabled button
When a button isn’t ready to submit a form yet, but can still be clicked, use aria-disabled="true"
to increase perceivability for people using a screen reader. Ex: Clicks submit and is notified of errors in the form.
<button aria-disabled="true">
Continue
</button>
When you can’t use semantic HTML
This custom button requires extra attributes and JS event listeners. Adding tabindex="0"
makes it focusable.
<div role="button" tabindex="0">
Continue
</div>
Avoid icon buttons
Buttons with no visible text (icon only) are inadvisable.
Use your words
You can think of words like a group of symbols that mean things.
When using an icon with no text is unavoidable, DO NOT rely on the aria-label
for the button name.
- 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.
<button aria-label="Get my location">
<!-- icon goes here -->
</button>
Repeating buttons
When there are multiple buttons (ex: Buy now) with the same inner text it can be helpful to use an aria-label
to differentiate identically named buttons.
- Only do this when it’s programmatically feasible to automate the naming convention.
- If this relies on a manual process, it’s inadvisable as buttons will inevitably become mis-named.
<button aria-label="Buy now, EspressoMatic Coffee Maker">
Buy now <!-- Ambiguous text doesn't describe the specific intent -->
</button>
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.
<h1>Payment details</h1>
<span>1 Jan, 2024</span>
<button aria-label="Edit payment date">
Edit
</div>
<span>$200.00</span>
<button aria-label="Edit payment amount">
Edit
</div>
Design notes
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
- Avoid using
aria-haspopup="true"
for menu, listbox or modal; it has mixed support and results. aria-controls="popupId"
is not well supported, but isn’t harmful
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 (ex:<div>
)
Focus
- Focus must be visible
- Custom elements (like
<div>
) needtabindex="0"
to be focusable