Toggle Button
A toggle button is a button that can be toggled between two or three states. A toggle button that supports three states
is called a tri-state toggle button. In the case of a two-state toggle button, the button can be toggled between an on (pressed)
and off (not pressed) state. In that case the state of the button may be conveyed using the aria-pressed
attribute or by changing
the button label (e.g. "Mute" and "Unmute"). In the case of a tri-state toggle button, the button label must be used to convey
the state.
A toggle button may be implemented using a semantic HTML button element (recommended) or a non-button element with the role="button"
attribute.
Role
A toggle button widget must have a role of button. The best way to achieve this is to use the semantic HTML button
element.
If you are using a non-button element, you must add the role="button"
attribute to the element.
1<!-- Better to use the button element -->2↖ <button class="mute">Mute</button>34<!-- If not using the button element, add `role="button"` -->5↘ <div class="mute" role="button">Mute</div>
Button name
A toggle must have an accessible name. The accessible name is the text that is read by screen readers to describe the element.
There are many ways to provide an accessible name for a toggle button.
The accessible name should convey the functionality of the button. If the aria-pressed
attribute is used to convey the state
of the button, the accessible name must not include the state of the button, see toggle activation.
If the aria-pressed
attribute is not used, the accessible name should include the state of the button, for instance the button label
could change from Mute to Unmute when muted.
1<!-- The button text constitutes the accessible name -->2<button aria-presssed="false" class="mute">3 <span class="visually-hidden">Mute</span>4</button>56<!-- The button gets its accessible name from the aria-label attribute,7 the name will also change to convey the state of the button so8 aria-pressed is not set. -->9<button aria-label="Mute" />1011<!-- Button gets its accessible name from the aria-labelledby attribute -->12<span id="mute-label">Mute</span>13<button aria-pressed="false" aria-labelledby="mute-label">14 <span class="on" aria-hidden="true">On</span>15 <span class="off" aria-hidden="true">Off</span>16</button>
Focus sequence
A button must be focusable unless it is disabled. This is important for keyboard users who need to be able to navigate the button.
Semantic HTML buttons are always focusable when not disabled. Non-semantic buttons can be made focusable by adding the tabindex="0"
attribute to the button element.
1<div role="button" aria-pressed="false" aria-label="Mute" tabindex="0" />
Disabled buttons
A semantic HTML button can be disabled by adding the disabled
attribute to the button element. A disabled button is not focusable.
Non-semantic buttons can be disabled by adding the aria-disabled="true"
attribute to the button element.
In that case the developer may choose to make the button focusable or not. It should not be possible to toggle the state of the button
when disabled, see Toggle activation test. The aria-disabled
attribute should not be used on a semantic HTML button.
1<!-- Disabled non-semantic HTML button -->2<div role="button" aria-pressed="false" aria-label="Mute" aria-disabled="true" />34<!-- Disabled non-semantic HTML button that is focusable -->5<div role="button" aria-pressed="false" aria-label="Mute" aria-disabled="true" tabindex="0" />67<!-- Semantic HTML button should not use aria-disabled -->8✓ <button aria-pressed="false" aria-label="Mute" disabled />9✗ <button aria-pressed="false" aria-label="Mute" aria-disabled="true" />
ARIA owns
If the button should contain elements that cannot be implemented as descendants of the button,
the aria-owns
attribute can be used to indicate that the button owns the elements.
However, since the aria-owns
attribute is not supported by some assistive technologies,
it is best practice to avoid it. If it must be used, the aria-owns
attribute should be set
on the button and should point to the IDs of the owned elements.
Forbidden interactive children
A button must not contain any interactive children such as links or buttons. Such children would be inaccessible to screen reader users since they will be excluded from the accessibility tree.
Toggle activation
Conveying the state of the button
A toggle button can support up to three states. Activating the button toggles through the states.
The state of the button must be conveyed to screen reader users. In the case of a button with two states,
this can be done by adding the aria-pressed
attribute to the button element and updating the attribute when the button is activated.
The aria-pressed
attribute must be set to either true
or false
.
1<!-- Toggle is initially Off -->2<div3 id="mute-button"4 role="button"5 tabindex="0"6 aria-label="Mute"7 aria-pressed="false"8 class="off"9/>
1function toggleButton(buttonElement) {2 const isDisabled = buttonElement.getAttribute('aria-disabled') === 'true';3 if (isDisabled) {4 // Do nothing if button is disabled5 return;6 }7 const currentState = buttonElement.getAttribute('aria-pressed');8 if currentState == 'false' {9 buttonElement.classList.add('on');10 buttonElement.classList.remove('off');11 buttonElement.setAttribute('aria-pressed', 'true');12 } else {13 buttonElement.classList.add('off');14 buttonElement.classList.remove('on');15 buttonElement.setAttribute('aria-pressed', 'false');16 }17}
In some cases it may be more appropriate to modify the button label to convey the state of the button.
One such case is when the button has more than two states but even for a buttons with only two states,
one may decide to modify the label due to design considerations.
In any case where the label conveys the state of the button and is modified on activation,
the aria-pressed
attribute should not be used.
1<!-- Toggle label conveys its initial state -->2<div3 id="mute-button"4 role="button"5 tabindex="0"6 aria-label="Mute"7 class="off"8/>
1function toggleMuteButton(buttonElement) {2 const isDisabled = buttonElement.getAttribute('aria-disabled') === 'true';3 if (isDisabled) {4 // Do nothing if button is disabled5 return;6 }7 const currentLabel = buttonElement.getAttribute('aria-label');8 if currentLabel == 'Mute' {9 buttonElement.classList.add('on');10 buttonElement.classList.remove('off');11 buttonElement.setAttribute('aria-label', 'Unmute');12 } else {13 buttonElement.classList.add('off');14 buttonElement.classList.remove('on');15 buttonElement.setAttribute('aria-label', 'Mute');16 }17}
Keyboard activation
A toggle button must be able to be activated using the keyboard. This is important for keyboard users who need to be able to activate the toggle.
This can be achieved by adding a keyup
event listener to the button element. The event listener should check for the Enter
and Space
keys.
1const buttonElement = document.getElementById("mute-button");2buttonElement.addEventListener("click", () => toggleButton(buttonElement));3buttonElement.addEventListener("keyup", (event) => {4 if (event.key === "Enter" || event.key === " ") {5 toggleButton(buttonElement);6 }7});
Toggle activation test
The toggle activation test checks that the button can be activated by both mouse and keyboard.
It also tests that when activated, either the button name or the aria-pressed
attribute is updated
to reflect the new state of the Button.
The test verifies that the button name and aria-pressed
attribute are not both changed when
the button is toggled. Furthermore it checks that when using aria-presseed
, the button only supports
two states whereas when using the button name, there are no more than three states.
The test uses the button name or aria-pressed
attribute to verify that the state of the button
changes for each activation method.
It runs three times to test activation using a mouse click, the Enter
key and the Space
key.
For toggle buttons that are not implemented using a native HTML button, the test will check
that for disabled buttons (aria-disabled="true"
), their state does not changed when
clicked or activated using the keyboard.