Creating a JavaScript-Free Radio Toggle in CSS3
You'll often find yourself using toggle sliders as a trendy alternative to checkboxes. Today we'll be creating one using plain CSS3 and HTML.
Step 1: Begin With the Markup
Create a new HTML document and add a span
tag with a class of toggle-bg
. This will be the background area for the toggle.
1 |
|
2 |
<!DOCTYPE html>
|
3 |
<!--[if lt IE 7 ]><html class="ie ie6" lang="en"> <![endif]-->
|
4 |
<!--[if IE 7 ]><html class="ie ie7" lang="en"> <![endif]-->
|
5 |
<!--[if IE 8 ]><html class="ie ie8" lang="en"> <![endif]-->
|
6 |
<!--[if (gte IE 9)|!(IE)]><!--><html lang="en"> <!--<![endif]--> |
7 |
<head>
|
8 |
|
9 |
<meta charset="utf-8"> |
10 |
<title></title>
|
11 |
<meta name="description" content=""> |
12 |
<meta name="author" content=""> |
13 |
|
14 |
<!-- Don't Forget the Viewport Metatag https://enva.to/A79s3G -->
|
15 |
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> |
16 |
|
17 |
<!-- CSS -->
|
18 |
<link rel="stylesheet" href="your_stylesheet.css"> |
19 |
|
20 |
</head>
|
21 |
<body>
|
22 |
|
23 |
<span class="toggle-bg"></span> |
24 |
|
25 |
</body>
|
26 |
</html>
|
Step 2: Semantics and Functionality
To make the toggle functional, we'll be using some invisible radio inputs
. These have just a couple of requirements:
- They must have different values
- They must have the same name
I'll be using on
and off
as my values, with a name of toggle
, but you may change this depending on your usage. For example, maybe you'll be toggling between yes
and no
.
Add the input
to the span
you created earlier. Your code should look like this:
1 |
|
2 |
<span class="toggle-bg"> |
3 |
<input type="radio" name="toggle" value="off"> |
4 |
<input type="radio" name="toggle" value="on"> |
5 |
</span>
|
Depending on your own usage, you may also want to place this all within a form.
Step 3: The Toggle Switch
The small circle that will be the switch itself is simply a span
that we'll style later on. Give the span
a class of switch
and insert it after the inputs
.
This is your final HTML markup:
1 |
|
2 |
<span class="toggle-bg"> |
3 |
<input type="radio" name="toggle" value="off"> |
4 |
<input type="radio" name="toggle" value="on"> |
5 |
<span class="switch"></span> |
6 |
</span>
|
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.)
1 |
|
2 |
.toggle-bg{ |
3 |
background: #222; /* You'll want to see the area being toggled, but feel free to change the color */ |
4 |
display: block; /* ...So that we can set a height and width */ |
5 |
float: left; /* ...So that it doesn't take up the full width of the page */ |
6 |
height: 7px; /* You can change this later if you want */ |
7 |
position: relative; /* Required to allow the switch to move around */ |
8 |
width: 26px; /* This can be changed later as well */ |
9 |
}
|
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
.
1 |
|
2 |
.toggle-bg input{ |
3 |
height: 28px; |
4 |
left: 0; |
5 |
margin: 0; /* Reset the margins and padding */ |
6 |
opacity: 0; /* Invisible! */ |
7 |
padding: 0; |
8 |
position: absolute; |
9 |
top: -10px; /* Shift vertically */ |
10 |
width: 36px; |
11 |
z-index: 2; /* We want the input to be over the span.switch, which we'll give a z-index of 1 */ |
12 |
}
|
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
.
1 |
|
2 |
.switch{ |
3 |
background: #ccc; |
4 |
display: block; |
5 |
float: left; |
6 |
height: 14px; |
7 |
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.*/ |
8 |
position: relative; |
9 |
top: -4px; /* ...To keep it centered vertically */ |
10 |
width: 14px; |
11 |
z-index: 1; /* Remember, it must be below the invisible inputs */ |
12 |
}
|
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.
1 |
|
2 |
.toggle-bg input:checked~.switch{ left: -1px; } /* initial toggle position */ |
Next, we'll tell the browser the final toggle position.
1 |
|
2 |
.toggle-bg input~:checked~.switch{ left: 13px; } /* final relative toggle position */ |
Finally, we'll put the :checked
input
behind the unchecked input
and the switch so that the second input
can be clicked.
1 |
|
2 |
.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:
1 |
|
2 |
.toggle-bg{ |
3 |
background: #222; /* You'll want to see the area being toggled, but feel free to change the color */ |
4 |
display: block; /* ...So that we can set a height and width */ |
5 |
float: left; /* ...So that it doesn't take up the full width of the page */ |
6 |
height: 7px; /* You can change this later if you want */ |
7 |
position: relative; /* Required to allow the switch to move around */ |
8 |
width: 26px; /* This can be changed later as well */ |
9 |
}
|
10 |
|
11 |
.toggle-bg input{ |
12 |
height: 28px; |
13 |
left: 0; |
14 |
margin: 0; /* Reset the margins and padding */ |
15 |
opacity: 0; /* Invisible! */ |
16 |
padding: 0; |
17 |
position: absolute; |
18 |
top: -10px; /* Shift vertically */ |
19 |
width: 36px; |
20 |
z-index: 2; /* We want the input to be over the span.switch, which we'll give a z-index of 1 */ |
21 |
}
|
22 |
|
23 |
.switch{ |
24 |
background: #ccc; |
25 |
display: block; |
26 |
float: left; |
27 |
height: 14px; |
28 |
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.*/ |
29 |
position: relative; |
30 |
top: -4px; /* ...To keep it centered vertically */ |
31 |
width: 14px; |
32 |
z-index: 1; /* Remember, it must be below the invisible inputs */ |
33 |
}
|
34 |
|
35 |
.toggle-bg input:checked~.switch{ left: -1px; } /* initial toggle position */ |
36 |
|
37 |
.toggle-bg input~:checked~.switch{ left: 13px; } /* final relative toggle position */ |
38 |
|
39 |
.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
.
1 |
|
2 |
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.
1 |
|
2 |
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:
1 |
|
2 |
body{ |
3 |
background: #f6f8f9; |
4 |
margin: 200px; |
5 |
}
|
Add the following to the styles for .toggle-bg
:
1 |
|
2 |
background: linear-gradient(to bottom, #f6f8f9 0%,#e5ebee 50%,#d7dee3 51%,#f5f7f9 100%); |
3 |
box-shadow: 0 1px 0 #fff, inset 0 0 2px #d7dee3, inset 0 1px 0 #d7dee3, inset 0 1px 5px #d7dee3; |
Add the following to the styles for .switch
:
1 |
|
2 |
background: radial-gradient(ellipse at center, #ffffff 0%,#fefefe 50%,#fdfdfd 51%,#ffffff 100%); |
3 |
box-shadow: 0 1px 1px #65727b, 0 0 1px #b6bdc2; |

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
:
1 |
|
2 |
height: 30px; |
3 |
top: 0; |
4 |
width: 30px; |
Change the final toggle position to be left 41 pixels:
1 |
|
2 |
.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
:
1 |
|
2 |
height: 30px; |
3 |
top: 0; |
4 |
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:
1 |
|
2 |
height: 30px; |
3 |
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.
Browser Support
Because this method relies on some brand new CSS3 features, older browsers aren't supported. Browser support for the :checked
pseudo-class includes the following:
- All versions of Chrome
- All versions of Firefox
- Internet Explorer 9+
- Opera 9+
- Safari 3+
And the following browsers have support for the general-sibling combinator:
- All versions of Chrome
- All versions of Firefox
- Internet Explorer 7+
- Opera 9+
- Safari 3+
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 childinput
is checked. Or, even better, figure out if this is possible using nothing but CSS. - Add a
label
to eachinput
within an iOS style toggle and selectively show and hide thelabels
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's
label
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
.
I hope you found this tutorial useful. Thanks for reading!