Switch

A switch is an input widget that allows users to choose one of two values: on or off. Switches are similar to checkboxes and toggle buttons, which can also serve as binary inputs, although both checkboxes and toggle buttons can have three states: on, off, or indeterminate. A switch's state is conveyed to assistive technology users as "checked" or "unchecked" so use a switch when these states will be meaningful to the user.

A switch may be implemented using a semantic HTML checkbox (recommended), a button element with role="switch" or a non-semantic element with role="switch".

Role

A switch widget must have a role of switch. This is achieved by adding the role="switch" attribute to the element. Even if the switch is implemented using a semantic HTML checkbox, the role="switch" attribute must still be added.

Accessible name

A switch 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 switch. If an HTML checkbox is used, the accessible name should be provided by the checkbox label. The accessible name should convey the functionality of the switch but not the current state of the switch (On/Off).

1<!-- The checkbox label constitutes the switch's accessibility label -->
2<label>
3 Dark mode
4 <input type="checkbox" role="switch" class="on" />
5</label>
6
7<!-- The `label[for]` attribute may also be used instead of wrapping
8 the input in a label tag -->
9<label for="dark-mode">
10 Dark mode
11</label>
12<input type="checkbox" id="dark-mode" role="switch" class="on" />

If the switch is implemented using a button element or a non-semantic element, the accessible name can be provided in a couple of ways:

1<!-- The button text constitutes the switch's accessibility label -->
2<button role="switch" aria-checked="false">
3 <span class="label">Dark mode</span>
4 <span class="on" aria-hidden="true">On</span>
5 <span class="off" aria-hidden="true">Off</span>
6</button>
7
8<!-- Switch gets its accessibility label from the aria-label attribute -->
9<div role="switch" tabindex="0" aria-label="Dark mode" aria-checked="false">
10 <span class="on" aria-hidden="true">On</span>
11 <span class="off" aria-hidden="true">Off</span>
12</div>
13
14<!-- Switch gets its accessibility label from the aria-labelledby attribute -->
15<span id="dark-mode-label">Dark mode</span>
16<div role="switch" tabindex="0" aria-labelledby="dark-mode-label" aria-checked="false">
17 <span class="on" aria-hidden="true">On</span>
18 <span class="off" aria-hidden="true">Off</span>
19</div>

Focus sequence

A switch must be focusable unless it is disabled. This is important for keyboard users who need to be able to navigate to the switch.

Semantic HTML inputs and buttons are always focusable when not disabled. Switches implemented using non-semantic elements can be made focusable by adding the tabindex="0" attribute to the switch element.

Disabled switches

A switch implemented using a checkbox or button element can be disabled by adding the disabled attribute. In that case, the switch will not be focusable.

Switches implemented using non-semantic elements can be disabled by adding the aria-disabled="true" attribute to the switch element. In that case the developer may choose to make the switch focusable or not. The aria-disabled attribute should not be used on a semantic HTML checkbox or button element.

1<!-- Disabled non-semantic HTML switch -->
2<div role="switch" aria-label="Dark mode" aria-checked="false" aria-disabled="true">
3 ...
4</div>
5
6<!-- Disabled non-semantic HTML switch that is focusable -->
7<div role="switch" tabindex="0" aria-label="Dark mode" aria-checked="false" aria-disabled="true">
8 ...
9</div>
10
11<!-- Switches implemted using semantic elements should not use aria-disabled -->
12<button role="switch" aria-checked="false" disabled>...</button>
13<input type="checkbox" role="switch" class="on" disabled />
14<button role="switch" aria-checked="false" aria-disabled="true">...</button>
15<input type="checkbox" role="switch" class="on" aria-disabled="true" />

ARIA owns

If the switch should contain elements that cannot be implemented as descendants of the switch, the aria-owns attribute can be used to indicate that the switch 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 switch and should point to the IDs of the owned elements.

Forbidden interactive children

A switch 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 switch

When a switch is activated, its state changes from On to Off or vice versa. The state of the switch must be conveyed to screen reader users. This can be done by updating the aria-checked attribute to reflect the new state of the switch. The aria-checked attribute must be set to either true or false.

1<!-- Switch is initially Off -->
2<div
3 id="dark-mode-switch"
4 role="switch"
5 tabindex="0"
6 aria-label="Dark mode"
7 aria-checked="false"
8 class="off"
9/>
1function toggleSwitch(switchElement) {
2 const isDisabled = switchElement.getAttribute('aria-disabled') === 'true';
3 if (isDisabled) {
4 // Do nothing if switch is disabled
5 return;
6 }
7 const currentState = switchElement.getAttribute('aria-checked');
8 if currentState == 'false' {
9 switchElement.classList.add('on');
10 switchElement.classList.remove('off');
11 switchElement.setAttribute('aria-checked', 'true');
12 } else {
13 switchElement.classList.add('off');
14 switchElement.classList.remove('on');
15 switchElement.setAttribute('aria-checked', 'false');
16 }
17}

Keyboard activation

A switch must be able to be activated using the keyboard. This is important for keyboard users who need to be able to toggle the switch. This can be achieved by adding a keyup event listener to the switch element. The event listener should check for the Enter and Space keys.

1const switchElement = document.getElementById('dark-mode-switch');
2switchElement.addEventListener('click', () => toggleSwitch(switchElement));
3switchElement.addEventListener('keyup', (event) => {
4 if (event.key === 'Enter' || event.key === ' ') {
5 toggleSwitch(switchElement);
6 }
7});

Toggle activation test

The toggle activation test checks that the switch can be activated by both mouse and keyboard. It also tests that when activated, the aria-checked attribute is updated to reflect the new state of the switch. The test uses the aria-checked attribute to verify that the state of the switch changes for each activation method. It runs three times to test activation using the mouse, the Enter key and the Space key.

In the case of an HTML checkbox input, the test will fail if the checkbox element includes the aria-checked attribute since a native HTML checkbox should only use the checked attribute. The rest of the toggle activation test is skipped since keyboard activation and updating the checked attribute is handled by the browser.

For switches that are not implemented using a native HTML checkbox, the test will check that for disabled switches (aria-disabled="true"), their state does not changed when clicked or activated using the keyboard.

Summary of Tests

NameConformance LevelWCAG Success Criteria
RoleA4.1.2 Name, Role, Value
Accessible nameA4.1.2 Name, Role, Value
Focus sequenceA2.1.1 Keyboard, 2.4.3 Focus Order
ARIA checkedA1.3.1 Info and Relationships
ARIA ownsA1.3.1 Info and Relationships
Forbidden interactive childrenA4.1.2 Name, Role, Value
Toggle activationA2.1.1 Keyboard, 4.1.2 Name, Role, Value