HTML & CSS - - By Craig Buckler

Even Better CSS3 Toggle Switches!

Many of you liked my previous article, How to Create a Toggle Switch in CSS3. However, a few issues were raised in the comments and on Twitter

  1. How do you apply toggles to radio buttons?
  2. Can accessibility be improved?
  3. Why doesn’t it work on older mobile Webkit browsers?

I’ve made some improvements, so please view the demonstration page and the HTML/CSS code…

Radio Button Support

An easy one to start with. Radio buttons function almost identically to checkboxes so we can simply apply a class of “switch” to every input to make it work, e.g.

<input type="radio" id="radio1" name="radio" class="switch" />
<label for="radio1">first radio button</label>

<input type="radio" id="radio2" name="radio" class="switch" checked="checked" />
<label for="radio2">second radio button</label>

<input type="radio" id="radio3" name="radio" class="switch" />
<label for="radio3">third radio button</label>

It’s very satisfying to see two switches alter position at the same time!

Amended Accessibility

Richard from Accessible Web Design raised a couple of concerns. First, the switches change between red and green; if you’re color-blind, it would not be easy to determine whether the switch was on or off. To remedy the problem, I’ve added a check and cross character to the background:

toggle characters

No additional element were required — I simply used a cross character instead of a space and positioned it on the right of the background using text-indent:

input.switch:empty ~ label:before
	content: '2718';
	text-indent: 2.4em;
	color: #900;

When the checkbox/radio is checked, it changes to a check character and is moved to the left of the background:

input.switch:checked ~ label:before
  content: '2714';
  text-indent: 0.5em;
  color: #6f6;

Cross-Browser CSS3 Animation
Originally, I placed the check/cross character on the white switch itself. This is complex for the browser since it must create a transition between two different characters. Firefox worked, but Chrome and IE10 take an easier route: they abandon the animation completely! It appears that Webkit and Trident will not permit animation on a pseudo element if its content is changing — even if you explicitly state that only the margin or color should be animated.

To address the issue, I applied the check/cross to the :before toggle background and removed its transition effect (the color will not smoothly change, but it’s hardly noticeable). Only the white :after switch position is animated now.

The next accessibility issue: keyboard focus. The previous toggles were difficult to use with keyboard only so I’ve applied a different label color and a box shadow to the toggle when it has focus:

input.switch:focus ~ label
  color: #000;

input.switch:focus ~ label:before
  box-shadow: 0 0 0 3px #999;

The result is possibly a little too subtle, but it’s easy to add your own effects:

toggle focus

The focus effect works in Firefox, IE and Opera — but fails in Chrome 26? It looks like a browser bug unless anyone knows differently?

More Webkit Woes

The final problem: the toggle switches fail in mobile browsers using older versions of Webkit such as Safari on the iPad 1.0 and the Android browser. The engine supports labels, is happy with :checkbox selectors and displays the initial state, but doesn’t want to modify pseudo elements after the initial page load. I even broke my own requirements and added a little JavaScript, but the browser laughed at my feeble attempts and wouldn’t budge.

Pseudo element animation has only been added to Webkit recently. It’s frustrating and, unlike the old IE6/7 days, it’s difficult to find workarounds which don’t adversely affect other browsers.

Anyway, assuming legacy Webkit users aren’t part of your demographic, please use the HTML/CSS code however you like. Alternatively, you’ll need to add further (real) elements or JavaScript to make it work.