Frequently I'm faced with the task of using material icons and turning them into interactive buttons. For instance, there might be an icon that when clicked reveals text and rotates. When it's clicked again it rotates back to the original position and the text disappears.
<div onClick={toggleButton()}>
<i
role="toggle button"
aria-pressed="true"
alt="Toggle text"
class="material-icons"
>
toggle_off
</i>
Random text...
</div>
<div onClick={toggleButton()}>
<i
role="toggle button"
aria-pressed="true"
alt="Toggle text"
class="material-icons"
>
toggle_on
</i>
</div>
-an if conditional would render either of these divs based on pressed or not pressed
Typically, I handle this button adding a role, aria-state, and alt text to the material icon and change the aria-state when clicked. However, I always feel that I might not be doing this as effectively as I should.
What is the proper way to make something like an icon used as a toggle button accessible?
(I know that best practice for WCAG (web accessibility) is to use a button component, but because of the unique nature of material icons that's not possible.)
You'll need to change a few things:
role="toggle button"
should be role="button"
aria-pressed="true"
is fine if your default button state is pressed. Otherwise, its initial value should be false
alt
, as it doesn't belong here. If you're including text in your icon (as in your example code), you don't need to replace anything. Otherwise, use aria-label
and put your button text theretabindex="0"
so that your icon can be reached by keyboardTo make your icon behave like a real button, you'll also need to listen for keys like the space-bar and enter key. These keystrokes should be treated as clicks, as a real button would.
const el = document.querySelector('my-icon');
el.addEventListener('keypress', handleKeyPress);
el.addEventListener('click', handleClick);
function handleClick() {
// Update aria-pressed
}
function handleKeyPress(e) {
// Handle space-bar and enter key
}
So, in the end, your toggle icon button might look something like this:
<i
role="button"
aria-pressed="false"
class="material-icons"
>
Button text
</i>
or (sans visible button text):
<i
role="button"
aria-pressed="false"
aria-label="Button text"
class="material-icons"
></i>