Performing actions
If clicking it does something on this page, it is a <button>. Not a div, not a span, not a link.
A real button comes with the keyboard, the role, and the focus ring for free.
In one minute
- Does something here (save, open a menu, toggle) →
<button type="button">. - Goes somewhere else (a new page or URL) →
<a href>. That was lesson 04. - Give every button a name that says what it does, even if it only shows an icon.
- If it can be on or off, tell the user with
aria-pressed.
Think of a light switch versus a painting of a light switch. They look alike, but only the real one works when you
reach for it in the dark. A div with a click handler is the painting. Screen reader users and keyboard users
reach for it and nothing happens.
1. Real button or fake button? Try the keyboard
A browser button gives you four things you would otherwise have to build by hand. Use the lab to compare. Press Tab to move onto each one, then Enter or Space to use it.
Step 1. Pick which element is behind the "Save draft" control.
Nothing pressed yet.
| Feature | Result |
|---|
The span can be patched with role="button", tabindex="0", and a key handler for Enter and Space. That
is three fixes to rebuild what <button> already does, and you will still forget something like the
disabled behaviour. Reach for the real element first.
2. A button that can be on or off
Some buttons keep a state: pinned, muted, bold. Sighted people see the colour change. Screen reader users need the same
fact in the markup. aria-pressed="true" adds "pressed" to what is announced.
Step 2. Toggle the button, then switch the markup between "colour only" and "with aria-pressed".
Switching the text from "Pin note" to "Unpin note" and setting aria-pressed reads as "Unpin note, pressed", which is confusing. Keep the label steady and let the state change.
3. The name says what the button does
A screen reader user often jumps through a list of buttons without seeing the page around them. Each name has to make sense alone. Choose which name is better for a button that deletes an invoice.
Step 3. Select a name and see how it sounds in a list of all buttons on the page.
- Icon-only buttons need a name from
aria-labelor visually hidden text. A tooltip (title) is not reliable. - You can restyle a button freely. Never remove
:focus-visiblewithout a visible replacement.
4. Disabled can be the wrong signal
A disabled button leaves the tab order, so a keyboard user can never land on it to learn why. Compare the
two ways of blocking "Submit".
disabled skipped by Tab
aria-disabled still focusable
With aria-disabled the browser will not stop a click for you, so your handler must check the flag and do nothing.
Common mistakes
| You see | Why it is a problem | Fix |
|---|---|---|
<div onclick> | No focus, no Enter or Space, no role. | <button type="button"> |
<a href="#"> that runs script | Announced as a link, but it does not go anywhere. | Use a button. |
| Icon with no text | Announced as just "button". | aria-label or hidden text. |
outline: none | Keyboard users lose their place. | Style :focus-visible instead. |
Everything is disabled | User cannot learn what is wrong. | Explain, or use aria-disabled. |
- Put the mouse away. Press Tab until you reach "Pin note", then press Space.
- In lab 1, select the span, then Tab through the page. Does it ever get focus?
Remember
- Action on this page →
<button type="button">. - The visible label is the name. Icon-only buttons add
aria-label. - On/off buttons use
aria-pressed, and the label stays the same. - Prefer
aria-disabledplus an explanation when users need to know why.