Step 4: Style the Background

To keep things simple, we'll start out with the bare minimum required to get this toggle working. Add the following to your CSS document (or to your style element if you're using inline CSS.)

.toggle-bg{
background: #222; /* You'll want to see the area being toggled, but feel free to change the color */
display: block; /* ...So that we can set a height and width */
float: left; /* ...So that it doesn't take up the full width of the page */
height: 7px; /* You can change this later if you want */
position: relative; /* Required to allow the switch to move around */
width: 26px; /* This can be changed later as well */
}

Step 5: The Invisible Inputs

These inputs need to be absolutely positioned, then shifted slightly to fit correctly. We'll then turn their opacity down to 0.

Step 6: The Toggle Switch

The switch should be square, so we can round it into a perfect circle later using a border-radius. It needs relative positioning to be able to move around, and since we need to give it a height and width, it will be a block-level element floated to the left.

.switch{
background: #ccc;
display: block;
float: left;
height: 14px;
left: -1px; /* This is the starting point. When adding a border radius, a small bit of the background is shown if we use left: 0;, so -1px is best.*/
position: relative;
top: -4px; /* ...To keep it centered vertically */
width: 14px;
z-index: 1; /* Remember, it must be below the invisible inputs */
}

Step 7: CSS Hackery!

CSS3 added the new general sibling combinator, a selector that uses the tilde (~) to select elements that share the same parent. The order in which they appear in the CSS (before or after the tilde) mirrors the order in which they appear in the DOM.

We also have access to the new :checked pseudo-class. This will allow us to specifically target the (currently invisible) radio input that is checked..

First, we'll use these selectors to reset the starting position of the toggle switch.

Finally, we'll put the :checkedinput behind the unchecked input and the switch so that the second input can be clicked.

.toggle-bg input:checked{ z-index: 0; }

By now, it should look like this:

Step 8: Story So Far

Here is the complete, unstyled CSS:

.toggle-bg{
background: #222; /* You'll want to see the area being toggled, but feel free to change the color */
display: block; /* ...So that we can set a height and width */
float: left; /* ...So that it doesn't take up the full width of the page */
height: 7px; /* You can change this later if you want */
position: relative; /* Required to allow the switch to move around */
width: 26px; /* This can be changed later as well */
}
.toggle-bg input{
height: 28px;
left: 0;
margin: 0; /* Reset the margins and padding */
opacity: 0; /* Invisible! */
padding: 0;
position: absolute;
top: -10px; /* Shift vertically */
width: 36px;
z-index: 2; /* We want the input to be over the span.switch, which we'll give a z-index of 1 */
}
.switch{
background: #ccc;
display: block;
float: left;
height: 14px;
left: -1px; /* This is the starting point. When adding a border radius, a small bit of the background is shown if we use left: 0;, so -1px is best.*/
position: relative;
top: -4px; /* ...To keep it centered vertically */
width: 14px;
z-index: 1; /* Remember, it must be below the invisible inputs */
}
.toggle-bg input:checked~.switch{ left: -1px; } /* initial toggle position */
.toggle-bg input~:checked~.switch{ left: 13px; } /* final relative toggle position */
.toggle-bg input:checked{ z-index: 0; }

Step 9: Border Radii

You now have a goofy-looking square slider, so let's round out those corners! We'll add a border-radius of 8px to the span with a class of toggle-bg and to the span with a class of switch.

border-radius: 8px;

Note: As with opacity and the transition coming up, I'm not using any vendor prefixes here for the sake of the tutorial, but you'll need them for compatibility with some older browsers.

Step 10: Transitions

Transitions should be added to the span with a class of switch. Feel free to adjust the settings. Left: is the only part of the following transition that can not be changed.

transition: left .2s ease;

Step 11: Background, Shadows, Gradients...

Now that all the functionality is there, everything can be customized to your liking. You can add a box-shadow and background gradient to the span with a class of toggle-bg or to the switch. Because it's round, radial gradients will tend to look better on the switch.

Here are my settings, but please change them to suit your style and your project.

Give the body an off-white background and a margin so the toggle isn't right in the corner:

Step 12: iOS Toggles

For an iOS style toggle, where the switch is the same size as its background, change the following in the style for .switch:

height: 30px;
top: 0;
width: 30px;

Change the final toggle position to be left 41 pixels:

.toggle-bg input~:checked~.switch{ left: 41px; }

Change the sizes of the inputs and remove vertical shifting by changing the following in the styles for .toggle-bg input:

height: 30px;
top: 0;
width: 70px;

Change all your border radii to 30px instead of 8px.

Tip: keep the border-radius value equal to the height of the element it modifies.

And finally, change the height and width of .toggle-bg to fit the changed elements:

height: 30px;
width: 70px;

Conclusion

Congratulations! You can now create JavaScript-free toggles with some cool new CSS3. You'll probably want to change the background gradients, and luckily there is a great online tool to help with this; check out Colorzilla's free gradient editor. There's also a Mac app called Gradient that works similarly.

jsFiddle

I've created three jsFiddles for you to experiment with on your own. The first includes the bare minimum CSS you'll need to have a working toggle slider. The second, much shinier, includes everything from the first one plus some extra styles and transitions. The last one is the one I'm calling an "iOS style toggle" since, like the toggles on iOS, the switch's height is equal to the toggle background's height.

Because this is all CSS, there are quite a few variations you can create. Here are a few to get your started:

Create a vertical, rather than horizontal toggle (maybe for an equalizer?)

Use an image with an angular gradient as the switch's background for a more realistic, metallic look.

Add a label to the inside of a large toggle.

Use jQuery to change the background of .toggle-bg when a child input is checked. Or, even better, figure out if this is possible using nothing but CSS.

Add a label to each input within an iOS style toggle and selectively show and hide the labels using jQuery.

Note: when creating a label for the toggle's radio inputs, you'll need to use JavaScript to show and hide the inactive input'slabel if you wish to be able to use the label as a way of unchecking the input. Otherwise, you'll only be able to check (but not uncheck) the input by pressing the label.