iOS 5 Style Switch Control

Recreating the iOS 5 Switch Control with HTML5, CSS3 and a Bit of ECMAScript 5

The final result of this post will run in iOS 5, Safari 5.1, as well as the latest versions of Chrome, Firefox and Opera.

Previously I had created a version of the switch control in iOS. With the launch of iOS5 Apple complete updated the look of the switch control. They went with a rounded style, which they also did with most controls in their desktop operating system, Lion.

After playing around with the early betas of iOS 5, I came up with the following reproduction of the new switch control look using just HTML5, CSS3 and some JavaScript for the interactive part. Functionally the switch control is nothing more than a fancier way of presenting a checkbox. So, for our purposes we are going to use a checkbox. Except that we need a couple of tags to wrap the checkbox so we can make it look like the switch control. Fortunately the amount of wrapper is really minimal. If you examine the picture below, you will notice that the switch control really has only two parts: the oblong base and the circular thumb. In our case we need a third part: a checkbox input.

Switch Control

We’re going to make a minor tweak to this default look. You’ll notice that the version above is in English. Actually, only the English version has labels for “On” and “Off”, everyone else uses the international symbols instead. They look like this:

International version of Switch Control

If we ignore the “On/Off” parts and just look at the colored areas we can see that we’re only really dealing with a simple vertical gradient on the thumb and some inset box shadows on the switch control base. This makes our styling really easy. For the “On/Off” parts we don’t need extra markup. You’ll notice that they exist in relation to the switch control’s thumb. We can use CSS pseudo elements on the thumb to create them.

To recreate the iOS5 switch control all we need is the following markup:

1
2
3
4
<div class="switch">
    <span class="thumb"></span>
    <input type="checkbox" />
</div>

Without styling, this gives us a very normal checkbox:

Unstyled switch control

We know what the dimensions need to be by measuring the screenshots, so we can give the switch control base some styling:

1
2
3
4
5
6
7
.switch {
    height: 28px;
    width: 77px;
    border: 1px solid #979797;
    border-radius: 20px;
    overflow: hidden;
}

This will give us the following:

Switch control with rounded border

It looks kind of funny with the checkboxes. We don’t need to see them. We will be setting their checked state with JavaScript later on anyway. So for now we can hide them:

1
2
3
.switch input[type=checkbox] {
    display: none;
}

Now let’s add some color. How to re-create that gray shadow area? We’ll use a series of inset box shadows. Like gradients, you can define multiple box shadows on an element. These stack up like layers, the last one being the bottom-most and the first being the top-most. We need to create a sizable gray choke inside the switch base, so we’ll use a box shadow with four values instead of three to create that effect:

1
box-shadow: inset 0 12px 3px 2px rgba(232, 232, 232, 0.5);

To this we’ll add a second inset box shadow to create a darker shadow along the top inside of the switch control:

1
box-shadow: inset 0 1px 3px #BABABA, inset 0 12px 3px 2px rgba(232, 232, 232, 0.5);

Switch control with gray box shadow

Here’s the complete CSS definition for the switch control:

1
2
3
4
5
6
7
8
9
10
.switch {
    height: 28px;
    width: 77px;
    border: 1px solid #979797;
    border-radius: 20px;
    margin-top: -5px;
    box-shadow: inset 0 1px 3px #BABABA, inset 0 12px 3px 2px rgba(232, 232, 232, 0.5);
    cursor: pointer;
    overflow: hidden;
}

Now for a tricky part. This gray inset box shadow is for the off state. How do we implement the bluish on state? Well, first of all we need to decide how to represent the states in markup. We’ll do this by added a class of “on” to the switch control base. That means that the base will have a class of “switch on” for when it’s flipped on and just “switch” when it’s off. We can use a pseudo element on the switch base to create the blue state and position it in view or out of view based on the presence of the “on” class. Of course we’re going to need a little JavaScript to set and remove the “on” class when the user clicks. So, here’s the CSS for the on state. We create an empty text node and give it the height we need to match the base. We don’t give it a width just yet since that will get set when the switch has the “on” class. We give it a bluish background color and inset box shadow. The absolute positioning is so that when it’s show, it doesn’t push the thumb out of the switch but instead sits independently inside the switch.

1
2
3
4
5
6
7
8
9
10
.switch::before {
    content: "";
    display: block;
    height: 28px;
    width: 0px;
    position: absolute;
    border-radius: 20px;
    box-shadow: inset 0 1px 2px #0063B7, inset 0 12px 3px 2px rgba(0, 127, 234, 0.5);
    background-color: #64B1F2;
}

To show the “on” state we just need to give the blue pseudo element the same width as the base:

1
2
3
.switch.on::before {
    width: 77px;
}

If we add the “on” class to one of our switches, we can see how the on state looks:

1
2
3
4
<div class="switch on">
    <span class="thumb"></span>
    
</div>

Switch control with 'on' state

That’s all we need for the switch control’s base. Now let’s tackle the thumb. We’ll make the span a block element with dimensions, set its positioning to relative so we can give it a higher z-index than the other elements in the switch control, specifically, the blue on state pseudo element. Next up: border, box shadow and gradient, very straightforward. And finally, because we want to have the thumb slide back and forth when the switch is clicked, we need to enable a CSS transition and give it a default translate value. Note: you will need to add an appropriate vendor prefix for the gradient, transition and transform.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.switch > .thumb {
    display: block;
    width: 26px;
    height: 26px;
    position: relative;
    top: 0;
    z-index: 3;
    border: solid 1px #919191;
    border-radius: 28px;
    box-shadow: inset 0 2px 1px white, inset 0 -2px 1px white;
    background-color: #CECECE;
    background-image: linear-gradient(top, #CECECE, #FBFBFB);
    transition: all 0.125s ease-in-out;
    transform: translate3d(0,0,0);
}

This gives us the following:

Switch control with thumb

As you can see, all thumbs are in the same place. We need to define a translate value for their “On” state:

1
2
3
4
5
.switch.on > .thumb {
    -webkit-transform: translate3d(49px,0,0);
    -o-transform: translateX(49px);
    -moz-transform: translateX(49px);
}

Which gives us:

Switch control thumb in 'on' state

Now the only thing left is to create the “on/off” indicators. We’ll start with the “on” one. It’s really quite simple. a vertical stripe with a border around it. We’ll create a pseudo element that has an empty text node, style it and position it beside the thumb. Here’s the CSS:

1
2
3
4
5
6
7
8
9
10
11
12
.switch > .thumb::before {
    content: "";
    display: block;
    height: 14px;
    width: 2px;
    background-color: white;
    box-shadow: 0px -1px 1px #666;
    border: none;
    position: absolute;
    top: 6px;
    left: -24px;
}

Switch control with 'on' state indicator

And for the “off” indicator, we create a pseudo element with an empty text node styles as a circle positioned to the right of the thumb:

1
2
3
4
5
6
7
8
9
10
11
.switch > .thumb::after {
    content: "";
    display: block;
    height: 10px;
    width: 10px;
    border-radius: 10px;
    border: solid 2px #777;
    position: absolute;
    right: -32px;
    top: 6px;
}

Switch control with 'off' state indicator

Now we have a fully styled switch control with minimal markup. We just need to add some interactivity. For that we’ll have to write some JavaScript. Since this is a self-contained example, I’m going to use the very latest version of ECMAScript 5. This gives me an easy way to get DOM elements and toggle classes on elements. If you want to reuse this you’ll need to switch those parts out for whatever methods your chosen JavaScript library provides.

So, first up I’m going to wrap everything up in an anonymous function:

1
2
3
(function() {
})();

Next I need a convenience method to get a collection of nodes and turn it into an array so I can iterate over it. I use call slice method of the Array object and pass in the results of querySelectorAll. That will convert the node collection into an array:

1
2
3
4
5
(function() {
    var $$ = function(selector) {
        return Array.prototype.slice.call(document.querySelectorAll(selector));
    }
})();

Now I want to define an event that executes when the DOM is fully loaded:

1
2
3
4
5
6
7
8
(function() {
    var $$ = function(selector) {
        return Array.prototype.slice.call(document.querySelectorAll(selector));
    }
    document.addEventListener("DOMContentLoaded", function() {
    
    }, false);
})();

After getting an array of all switch controls in the document, we iterate through them with the **forEach** method and bind a click event listener. The listener will execute a function that toggles the class “on”. ECMAScript 5 introduces a new token collection for classes called classList. This has several useful functions: add, remove, contains and toggle. To accomplish these methods with straight JavaScript you would need to use regular expressions. Instead I can just use **Element.classList.toggle(“on”)** to add and remove the class when the user clicks:

1
2
3
4
5
6
7
8
9
10
11
12
(function() {
    var $$ = function(selector) {
        return Array.prototype.slice.call(document.querySelectorAll(selector));
    }
    document.addEventListener("DOMContentLoaded", function() {
        $$(".switch").forEach(function(switchControl) {
            switchControl.addEventListener("click", function toggleSwitch() {
                switchControl.classList.toggle("on");
            }, false);
        });
    }, false);
})();

With the above JavaScript in our document, when the user clicks a switch control, the class “on” will be added to or removed from the switch, causing its thumb to slide to the left or right accordingly. This handily takes care of our visual requirements for the functionality of the switch control. However, we do need to manage the checked state of the checkbox. The first thing we’ll do is make sure any switch controls that had the class “on” during page load have their checkboxes set to chekced. Since the checkbox is the last element in the switch control div, we can reference it that way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function() {
    var $$ = function(selector) {
        return Array.prototype.slice.call(document.querySelectorAll(selector));
    }
    document.addEventListener("DOMContentLoaded", function() {
        if (switchControl.classList.contains("on")) {
            switchControl.lastElementChild.checked = true;
        }      
        $$(".switch").forEach(function(switchControl) {
            switchControl.addEventListener("click", function toggleSwitch() {
                switchControl.classList.toggle("on");
            }, false);
        });
    }, false);
})();

Next we need to update a switch controls checkbox when the switch control itself is clicked. We just need to again get a reference to the checkbox and set its clicked state to the opposite of what it was when the user clicked:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(function() {
    var $$ = function(selector) {
        return Array.prototype.slice.call(document.querySelectorAll(selector));
    }
    document.addEventListener("DOMContentLoaded", function() {
        var checkbox;
        if (switchControl.classList.contains("on")) {
            switchControl.lastElementChild.checked = true;
        }      
        $$(".switch").forEach(function(switchControl) {
            switchControl.addEventListener("click", function toggleSwitch() {
                checkbox = switchControl.lastElementChild;
                checkbox.checked = !checkbox.checked;
                switchControl.classList.toggle("on");
            }, false);
        });
    }, false);
})();

And that’s all you need to make the switch controls work. The final example has some extra JavaScript to output some text when the user flips a switch on to show them working. For Safari, Chrome and Opera, I use innerText to set the text value, but Firefox uses textContent. So the code has to deal with those differences.

You can try the working example. If you want the code, just save that page to your desktop. Everything is self-contained in the page.

Advertisements

Leave a comment