CSS: Display checkboxes as switches

Checkboxes are kind of the most boring elements on a web page since the browsers always display them the same. Another problem is that the checkbox itself is pretty small. Of course it’s not a real problem for such checkboxes:

You can click on the checkbox itself or on the label to toggle it. So the small size of the checkbox itself is not an issue. But when you have something like this, it’s more of a problem:

Identification code:
Display name:
Activated:

Even if you wrap the checkbox in a label for it, you can still click anywhere near the checkbox to toggle it but it’s not visible to the user that it’s possible.

A better solution would be to have some alternative representation of the checkbox e.g. as a switch. It’s also important to be able to adapt the size of the switch. Here’s how it should look like in the end (the first one is the normal size and the second one is twice as large):

switches

Since you cannot style the checkbox itself, we’ll have to add a new element and hide the checkbox. Additionally, since some scripts will read the state of the checkbox, we need to keep the checkbox in there (even if not displayed) and we need to make sure that the state of the switch and the state of the check are in sync.

The easiest way to do it is to use a label. As can be see above if a label is created for a checkbox, clicking on the label toggles the checkbox. So the basic idea is to:

  • add a label for the checkbox
  • hide the checkbox
  • style the label to look like a switch
  • have a styling of the label based on the state of the checkbox

For the last point, we’ll leverage a special kind of CSS selector which selects elements that are placed immediately after a given element. This is done with a plus sign between the two elements:

input[type="checkbox"] + label

This would select the labels immediately following a checkbox. Using this we can style the labels depending on the state of the checkbox.

So first we need some HTML to have a checkbox:

<input class="switch" id="check1" type="checkbox" />

Note that I’ve used the class “switch” in case you do not want to turn all checkboxes into switches but only some of them. If you do want to turn them all into switches, just remove this class from all selector in the CSS and jQuery code in the rest of this post.

Your checkbox should also need to have an ID to be referenced by the label. Now let’s add the label dynamically using jQuery:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
    $( document ).ready(function() {
        $('input[type="checkbox"].switch').each(function() {
            if ($(this).attr("id")) {
                $('<label for="'+$(this).attr("id")+'"></label>').insertAfter($(this));
            }
        });
    });
</script>

It does the following:

  • Search for all checkboxes to be turned into switches
  • Only change the ones with an ID
  • Add a label immediately after the checkbox referencing the ID of the checkbox

Note that we do not need a special class for the label as we need to make the appearance dependent on the checkbox state anyway so no extra selector for the label is required.

If you want to achieve a solution without JavaScript, you’ll have to add the label after the checkbox manually in the HTML code e.g.:

<input id="check1" class="switch" type="checkbox">
<label for="check1"></label>

I’d rather use JavaScript and CSS pseudo-classes than polute my HTML code with elements which are only needed for styling. But I guess others may see it in a different way and rather use pure CSS and HTML than introduce some JavaScript.

Now we have this all in place, we just need to style the checkboxes and labels. An easier way to do it would be to use a background image. But I tend to avoid using images except for icons. And also if the element has to have a variable size, I definitely try to avoid images. The goal in this post is anyway no do show you how to make nice looking backgrounds with CSS only so we’ll stick to a plain two color background.

The first step is to hide the checkboxes we’ll turn into switches:

input[type="checkbox"].switch {
	display: none;
}

Now we want the label to look like a switch, so have a border, be rounded on the left and right and have a background color depending on its state (e.g. red for not checked and green for checked):

input[type="checkbox"].switch + label {
	background-color: #EE9090;
	border: 1px solid black;
	border-radius: 999px 999px 999px 999px;
	display: inline-block;
	width: 1.6em;
}

For the border radius, you’ll need to add the following to make it work in older browsers (i.e. Firefox < 4.0, Chrome < 5.0, Safari < 5.0, iOS Safari < 4.0 and Android Browser < 2.2):

-moz-border-radius: 999px 999px 999px 999px;
-webkit-border-radius: 999px 999px 999px 999px;

Please also note that IE only supports it starting with version 9.0.

The inline-block is a great invention. For those who still don’t know it, it’s a kind of float on steroids. It means it handles width and height just like a block element. But is displayed inlined with the surrounding elements just like inline elements. So this means that the label will have a width and height although it doesn’t have a text and that the round element we’ll add afterwards will be displayed in it.

Note that we’ve defined the width in ems. This allows us to make the element scale when we increase the font size.

Now, we’ll use pseudo-classes to dynamically add a round element which will be moved from left to right when the checkbox is checked:

input[type="checkbox"].switch + label:before {
	background-color: silver;
	border: 1px solid black;
	border-radius: inherit;
	box-sizing: border-box;
	content: "";
	display: block;
	height: 0.8em;
	width: 0.8em;
}

So the content is empty because we do not want to display some text but only have a background and borders displayed.

box-sizing is used so that the width and height defined do already contain the borders. We want this element to be half the width of the containing label and to be round (so same height and width). Since the width of the label is defined in ems, the height and width of this element also have to be defined in ems. But since we need the border to always be 1 pixel (defining it also in ems makes it look terrible when the size is increased), we should define the width and height to be 0.8em – 1px. This is not possible. So we use box-sizing to say that the 0.8em already contains the border.

Note that box-sizing, requires a prefix for all versions of Firefox (both mobile and desktop) and required a prefix for Safari until version 5.0, for iOS Safari until version 4.3, for Blackberry Browser version 7.0, Android Browser until version 3.0. Also not that Internet Explorer version 7.0.

So you may want to add the following:

-moz-border-radius: inherit;
-web-kit-border-radius: inherit;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;

When the checkbox is checked, we want to change the background color and move the round element to the right. Changing the background color is easy. The trick to move it to the right is to add a padding to the label. But adding the padding will make the label larger so at the same time we have to reduce the width of the label:

input[type="checkbox"].switch:checked + label {
	background-color: #90EE90;
	padding-left: 0.8em;
	width: 0.8em;
}

So now the switch is working ! But we want the transition to be smoother when the checkbox is checked or unchecked. This is done with the transition-duration and transition-property CSS properties:

input[type="checkbox"].switch + label {
	transition-duration: 0.3s;
	transition-property: padding, width;
}

This means that when changing the padding and width the transition will be distributed over 300 milliseconds. You could also add the change of background color but I felt it looks kind of strange because it looks like it changing to some kind of brownish color between red and green. Also, for this CSS property there are also browser specific prefixes:

-webkit-transition-duration: 0.3s;
-moz-transition-duration: 0.3s;
-o-transition-duration: 0.3s;
-webkit-transition-property: padding, width;
-moz-transition-property: padding, width;
-o-transition-property: padding, width;

Now we’re done !

To get a larger switch use the following:

<div style="font-size:200%"><input id="check2" type="checkbox" /></div>

You can find the whole code below:

<html>
        <head>
                <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
                <script type="text/javascript">
                        $( document ).ready(function() {
                                $('input[type="checkbox"].switch').each(function() {
                                        if ($(this).attr("id")) {
                                                $('<label for="'+$(this).attr("id")+'"></label>').insertAfter($(this));
                                        }
                                });
                        });
                </script>
                <style type="text/css">
                        input[type="checkbox"].switch {
                                display: none;
                        }

                        input[type="checkbox"].switch + label:before {
                            background-color: silver;
                            border: 1px solid black;
                            border-radius: inherit;
                            -moz-border-radius: inherit;
                            -web-kit-border-radius: inherit;
                            box-sizing: border-box;
                            -moz-box-sizing: border-box;
                            -webkit-box-sizing: border-box;
                            content: "";
                            display: block;
                            height: 0.8em;
                            width: 0.8em;
                        }

                        input[type="checkbox"].switch:checked + label {
                            background-color: #90EE90;
                            padding-left: 0.8em;
                            width: 0.8em;
                        }

                        input[type="checkbox"].switch + label {
                            background-color: #EE9090;
                            border: 1px solid black;
                            border-radius: 999px 999px 999px 999px;
                            -moz-border-radius: 999px 999px 999px 999px;
                            -webkit-border-radius: 999px 999px 999px 999px;
                            display: inline-block;
                            transition-duration: 0.3s;
                            -webkit-transition-duration: 0.3s;
                            -moz-transition-duration: 0.3s;
                            -o-transition-duration: 0.3s;
                            transition-property: padding, width;
                            -webkit-transition-property: padding, width;
                            -moz-transition-property: padding, width;
                            -o-transition-property: padding, width;
                            width: 1.6em;
                        }
                </style>
        </head>
        <body>
                <input class="switch" id="check1" type="checkbox" />
                <br/><br/>
                <div style="font-size:200%"><input class="switch" id="check2" type="checkbox" /></div>
        </body>
</html>

Leave a Reply

Your email address will not be published. Required fields are marked *