How to Create a Toggle Switch in CSS3

Craig Buckler
Share

You’ll find mobile-interface-like toggle switches in various places around the web but I wanted to improve existing examples.

toggle switch

Specifically, I wanted a solution which:

  1. progressively enhanced standard checkboxes
  2. did not use superfluous HTML tags or attributes
  3. supported input labels
  4. used only CSS without images or JavaScript
  5. used relative units so the controls are resizable/responsive
  6. had some slick animation
  7. ideally worked in a range of mobile browsers, and
  8. degraded gracefully so it remained usable in all browsers

View the demonstration page and the HTML/CSS code…

The HTML

We require a input checkbox and a label:

<div>
	<input type="checkbox" id="switch1" name="switch1" class="switch" />
	<label for="switch1">first switch</label>
</div>

The input has a class of “switch” assigned. This ensures we can retain normal checkboxes should we need them.

HTML purists will be horrified to see a wrapper div but it’s only necessary if you require two or more toggle switches — you cannot have more than one switch (or further labels) in the the same parent container. Besides, you’ll probably need div wrappers to separate form elements anyway.

The HTML will render well in most browsers with minimal styling. IE6, 7 and 8 users will see this:

toggle switch

The CSS

Now for the interesting part. First, we’ll hide the input box using a negative margin — this can be preferable to display:none which often disables it on mobile devices:

input.switch:empty
{
	margin-left: -999px;
}

You may have seen the :empty selector used in my recent post, How to Create a Responsive Centered Image in CSS3. It only matches elements which have no children but, since it’s not supported in IE8 and below, those browsers won’t attempt to apply the styles.

Next, we’ll style the sibling labels of the input checkbox:

input.switch:empty ~ label
{
	position: relative;
	float: left;
	line-height: 1.6em;
	text-indent: 4em;
	margin: 0.2em 0;
	cursor: pointer;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}

The main properties to note are position:relative, the text-indent which provides room for our switch, and the line-height which defines its height.

The toggle itself is created using :before and :after pseudo-elements for the colored background and the white switch accordingly:

  • both elements are absolutely positioned at the left-hand edge of our label
  • the white switch is set to a smaller size and has a left margin applied to align it on the background
  • a border-radius and inset box-shadow is applied to give some depth, and
  • a transition is defined for the animation.
input.switch:empty ~ label:before, 
input.switch:empty ~ label:after
{
	position: absolute;
	display: block;
	top: 0;
	bottom: 0;
	left: 0;
	content: ' ';
	width: 3.6em;
	background-color: #c33;
	border-radius: 0.3em;
	box-shadow: inset 0 0.2em 0 rgba(0,0,0,0.3);
	-webkit-transition: all 100ms ease-in;
	transition: all 100ms ease-in;
}

input.switch:empty ~ label:after
{
	width: 1.4em;
	top: 0.1em;
	bottom: 0.1em;
	margin-left: 0.1em;
	background-color: #fff;
	border-radius: 0.15em;
	box-shadow: inset 0 -0.2em 0 rgba(0,0,0,0.2);
}

Finally, when the checkbox is checked, we move the switch to the right-hand edge and change the background color:

input.switch:checked ~ label:before
{
	background-color: #393;
}

input.switch:checked ~ label:after
{
	margin-left: 2em;
}

View the demonstration page and the HTML/CSS code…

Everyone likes a toggle switch! Please use the code however you like. A link back to this article or a “hey Craig, that’s awesome/awful” tweet is appreciated.