Switch Role Support

Whether you use a <button> or <input type="checkbox"> as the basis for your switch depends on a few factors:

  1. Use <button> if:
    • you can count on JavaScript being available, and
    • flipping the switch has an immediate effect.

    Go read Under-Engineered Toggles Too.

  2. Use <input type="checkbox"> if:
    • you want to progressively enhance the control, and
    • flipping the switch will only take effect when the user submits it.

    Go read Under-Engineered Toggles.

Those two posts show you how to accessibly style your pill-like controls. Whether you follow any of that advice, use any of those styles, or ignore it all completely is mostly irrelevant from here out. All this post cares about is what happens once you add the switch role.

Normally, both aria-checked and aria-pressed allow for a mixed value. With the switch role, however, you are restricted to aria-checked and only the values true or false. The mixed value is invalid on the switch role in the current ARIA spec and into the ARIA 1.3 Editor’s Draft.

The aria-checked attribute of a switch indicates whether the input is on (true) or off (false). The mixed value is invalid, and user agents MUST treat a mixed value as equivalent to false for this role.

Test Case

As always, the example pen is available in debug mode for you to test with your favorite assistive technology. You should do that, especially since these results will be out of date as soon as a new release of anything comes out. I also did not test with voice control, though it generally cares little for ARIA.

There are no styles other than to indicate the buttons’ states. This post only cares about how the controls are exposed to screen reader users through the browser’s accessibility tree.

See the Pen Toggles by Adrian Roselli (@aardrian) on CodePen.

Testing

None of ARC, Axe, or Wave flag a mixed value when used with a switch role. Neither does the HTML validator. See my post Beware False Negatives for why I bother to mention this.

I left the screen reader instructions for each control in the following output. It is interesting to see how the instructions change depending on control types and roles.

Chrome 94–106 with JAWS 2021–2022

Chrome exposes aria-checked="mixed" on a switch role as “true”, which is against the specification. Interaction instructions are not offered for the switches (you do not need to provide them). Generally unrelated to this post, be careful using aria-checked on a native checkbox, as Scott O’Hara notes in a Chromium bug report.

Firefox 93–105 with NVDA 2021.2–2022.3

Firefox exposes aria-checked="mixed" on a checkbox with the switch role as “mixed”, which is against the specification. Firefox does not expose the switch role, always announcing it as a checkbox.

Narrator (Windows 10–11) with Edge 94–105

Edge exposes aria-checked="mixed" on a button with the switch role as “true”. Edge exposes aria-checked="mixed" on a checkbox with the switch role as “mixed”. Both of these are against the specification.

Narrator (Windows 11) with Firefox 105

This shows how when Firefox is paired with Narrator, Narrator diverges from NVDA when announcing the mixed state for aria-checked. I gave this a test based on feedback from James Scholes on Twitter.

TalkBack 9.1–13 (Android 11–13) with Chrome 94–106

TalkBack with Chrome announces aria-checked="mixed" on a checkbox with the switch role as “partially checked”, and on a button with the switch role as “checked”, both of which are against the specification. Interestingly, TalkBack notes you can toggle all controls, unless they are a checkbox in a mixed state, then you “activate” it.

TalkBack 9.1–13 (Android 11–13) with Firefox 93–105

TalkBack with Firefox announces a standard mixed checkbox as “not checked”. It also announces all buttons with aria-pressed as “switch”, though they do not have that role, and TalkBack does not announce their value.

VoiceOver (iPadOS 14.8–15.7) with Safari 14–15.6

VoiceOver announces a standard mixed checkbox as “unchecked”, and any other control with aria-pressed="mixed" or aria-checked="mixed" as “unchecked”. VoiceOver also announces a button with aria-pressed="mixed" as a checkbox. Finally, VoiceOver announces nothing as a switch. Scott O’Hara filed a WebKit bug in early 2019.

VoiceOver (macOS 11.4–12.6) with Safari 14.1.1–16

VoiceOver announces a standard mixed checkbox as “unchecked”. Scott O’Hara filed another WebKit bug almost two weeks ago identifying it as a regression. Otherwise Safari/VO handles switch correctly, and far better than its iOS/iPadOS cousin.

Verdict

The switch role does not allow mixed states. Ensure your switch never gets set to a mixed state, otherwise, well, problems.

Switches do not always announce as switches. It may be worth keeping this in mind for documentation — both for developers and end users.

The best performers for switches are TalkBack 9.1 / Android 11 with Firefox 93 (though buggy with toggled buttons and mixed native checkboxes) and VoiceOver / macOS 11.4 with Safari 14.1.1 (though buggy with mixed native checkboxes). The worst are Firefox 93 with NVDA 2021.2 (it does not announce them as switches, and is wrong with mixed buttons) and VoiceOver / iOS 14.8 with Safari 14 (it does not announce them as switches, and is wrong with mixed buttons and checkboxes).

I am naming these because Safari is both the best (desktop) and the worst (mobile) and Firefox is also both the best (mobile) and the worst (desktop). This should drive home the point that a browser should not be expected to perform the same on mobile and desktop, even for the same version.

If you are trying to figure out which approach is right for you, well, I don’t know. I’m not your dad.

Updated Verdict, 5 October 2022

In the year since I wrote this post, there have been minor changes.

In short, things that were broken are still broken. If a switch was not announced as a switch before, it still isn’t (looking at you NVDA/Firefox and VoiceOver/iOS).

Update: 6 October 2022

I added another example which uses aria-checked on the checkbox. I held off before because the checked property (or lack thereof) should take priority, but realized it was an incomplete test without it. The native property and the aria-checked values stay in synch.

I also filed two browser issues:

Have not gone back to sort the VO/Safari/iPadOS wonkiness yet.

In the interest of spamming all the issue trackers, I also left a comment on Core AAM #75 Sanity-check mappings for switch role.

Update: 22 December 2023

Safari Technology Preview 185 landed with support for a proposed native switch element. More accurately, it landed with support for:

<input type="checkbox" id="ck01sp" switch checked>
<label for="ck01sp">Pliers</label>

The switch role has two key differences from a checkbox: it does not allow an indeterminate state but it does allow a required state. Which means I was obligated to update my example above to confirm support in Safari Technology Preview 185.

The good news is that it is exposed correctly and announces as expected (including “on” and “off” versus “checked” and “not checked”):

For more context, a switch element was propsed with WHATWG HTML in 2018, Google worked on it for a while, along with the some WICG discussion, then abandoned it. Open-UI started a discussion in 2021, but other than a comment on valid indeterminate use cases, has mostly died there.

Then in July 2023 (5 months ago), a new contributor filed a WHATWG PR to add a switch attribute to a checkbox and here we are. Note that it has not been merged into WHATWG HTML, so Apple Safari has chosen to get way ahead of this one.

The good news is that conversations on Mastodon suggest visual cues are pretty good (such as contrast and honoring color preferences). The less good news is that this feature is not part of the spec yet and, as I noted in my post about allowing <hr> in <select>, makes me want a versioned spec versus the shifting one we have now:

Since WHATWG HTML is versionless, and since it essentially only takes one browser implementation to get into WHATWG HTML, and since the people approving PRs to WHATWG HTML seem to be the same ones adding features to Safari and Chrome, perhaps the best way to stay on top of HTML today is to look at Chrome “intent to ship” announcements and Safari feature releases.

Remember how breathless Apple was when it announced it was the first browser to ship this new <search>l element? Expect the same here in a few months. While it is indeed swell that Safari flipped a bit to take its existing support for the ARIA switch role and apply it to this element, be wary when it brags about being first.

Anyway, don’t use this in production yet.

One Comment

Reply

It’s a nice addition, but the implementation seems a bit rushed. The control doesn’t scale properly and visually breaks when changing the zoom level (e.g., screenshots at 400% and 25% zoom level.)

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>