Safari 17.4 dropped an interesting an unexpected feature, a native UI toggle switch control! It’s so new it’s not even in the HTML spec yet. While that might be a blocker for your project, you can use it today as a progressive enhancement on top of the standard <input type="checkbox">
.
<input type="checkbox" switch>
<input type="checkbox" checked switch>
Code language: HTML, XML (xml)
Adding a switch
attribute to a normal <input type="checkbox">
changes the visual affordance in Safari 17.4+ to a switch toggle input:
The “Active” state is the default Apple blue input color (like checkboxes) but accepts the CSS accent-color
property so you can make it more on brand with minimal effort. There’s even more good news because the styling story isn’t limited to accent colors, there are some new pseudo-elements to make even more customizations.
::thumb
and ::track
pseudo-elements?
The new Along with the new input[switch]
control, Safari is experimenting with ::thumb
and ::track
pseudo elements as a replacement for the old proprietary ::-webkit
prefixed pseudo-elements. They are current behind a flag in Safari Technical Preview 189+.
I was excited for input[switch]
but having standardized pseudo-elements for styling form controls is probably my favorite part of this feature release. It solves a minor pain point, but makes for much more approachable and maintainable CSS.
The Webkit blog demos for input[switch]
show what’s possible with new CSS and get pretty wild including a Light/Dark toggle with some embedded animations. I’m excited to see what people come up with but it’s a good time to talk about some UX concerns with switch toggle inputs.
UX Concerns
The good minds at Axess Lab had some strong words for switch toggle inputs in their post “Toggles Suck!” The post is worth a read, but the gist is that there are a lot of shoddy implementations where it would be more clear to use a button, a radio, or a checkbox instead of a fancy toggle. So before you rollout a brand new settings page UI, check with your customers to see if the current checkboxes and radios are working just fine.
Reading through some of the common complaints, there’s two major concerns you need to pay attention to with toggles:
- You need clear labelling
- You need clear “Active” and “Inactive” states
If your toggle is floating in space disconnected from any sort of label or context, that’s a clear fail. If your toggle has a weird label like “Don’t not unsubscribe”, that’s a fail. If your active state looks more disabled than your inactive state, you failed.
Webkit gives the following advice which is reflected in their demos:
Generally, we recommend using a switch when the end user understands the user interface element as a setting that is either “on” or “off”.
I hate nuance, but a lot of it depends on your situation and the context in which you’re using switch toggle inputs.
Internationalization Concerns
From an internationalization standpoint, you need to think in logical properties. The idea that “thumb to the right == active” is not always true. In rtl
languages “thumb to the left” is the active state. That’s where working in CSS logical properties helps you, you can use the keyword inline-end
instead and you get the correct behavior in both language directions in one line of code.
Accessibility Concerns
As far as accessibility goes, from a screen reader perspective there’s a few things happening under the hood. Adding the switch
attribute to a checkbox in Safari upgrades role="checkbox"
to role="switch"
and it announces its state in VoiceOver as “On” or “Off”, as opposed to either “checked” or “unchecked”. The can both be interacted with using the Spacebar.
One more difference to be aware of is that input[type="checkbox"]
can have an indeterminate
state while input[switch]
cannot, it’s a boolean value.
If you’re thinking about polyfilling this feature, be aware of some notes in how screen readers announce role="switch"
, but if you’re comfortable using input[switch]
with the fallback being a checkbox for unsupported browsers, it might make a great progressive enhancement for your page.
Detecting switch input support
Experimenting with input[switch]
I found myself wanting a different layout for regular checkboxes and when input[type="checkbox"][switch]
was enhanced. One nuanced difference I’ve noticed in settings screens is I like is the following:
- Checkboxes go at the
inline-start
(before the label) - Switches go at the
inline-end
(after the label)
This is not a hard and fast rule, but there are a couple different ways we can achieve this in our little example. Let’s start with JavaScript.
Use JavaScript to detect switch inputs
One way to detect switch toggle input support is to use JavaScript. I wrote a little script that checks for support and adds a class to the <html>
element.
// Remember Modernizr?!? lol.
function supportsSwitchInput() {
const switchInput = document.createElement('input');
switchInput.type = 'checkbox'
if('switch' in switchInput) return true;
return false
}
if(supportsSwitchInput()) document.documentElement.classList.add('supports-switch');
Code language: JavaScript (javascript)
In your CSS you’d do something like the following to flip the presentation of the checkbox row.
div:has(input[type="checkbox"]) {
display: flex;
flex-direction: row;
}
.supports-switch div:has(input[type="checkbox"][switch]) {
flex-direction: row-reverse;
}
Code language: CSS (css)
Experimental CSS detection for switch inputs
If JavaScript is not your thing and you want to only use CSS you could try something a little more experimental…
/* ::thumb only supported in Safari TP which supports input[switch] */
@supports selecotor(::thumb) {
div:has(input[type="checkbox"][switch]) {
flex-direction: row-reverse;
}
}
Code language: CSS (css)
We can’t detect support for input[switch]
exactly but does sniff for the ::thumb
pseudo-element support, which is supported in Safari TP along with input[switch]
.
Have you tried turning it off and on again?
Over the years I’ve coded a few toggle switches by moving a <span>
or ::after
pseudo-element inside a <button>
or <label>
based on a piece of state. It never felt like a good solution and it always felt clumsy. <input type="checkbox" switch>
is an elegant way to solve a commonly requested UI element.
And best of all, it’s a progressive enhancement. That means low-risk for testing and experimenting. Your mileage may vary with your users and you may want to wait for another browser to roll this out, but I already can tell this will save me a few dozen lines of weird CSS in my application.
Safari’s demo page:
https://webkit.org/demos/html-switch/
To see it working right now as I write… I had to use Safari Technology Preview updated to 17.4, then go to Feature Flags (after turning on Web Developer features) and turn on two feature flags:
aria-role=”checkbox” to aria-role=”switch”
This is a typo, the attribute name is just
role
, notaria-role
; the correct one is used further down.Using would ensure the switch role would be conveyed to screen readers with all browsers, not just ones that support
switch
. Changing the appearance of the input to look like a switch would still be necessary.Thanks Curtis! Fixed where appropriate.