In today's post I wanted to share a technique which I use to create pure CSS toggle switches. This is very easy to implement, and is a stylish replacement to regular checkbox HTML inputs.
Video Tutorial
If you prefer this tutorial in the form of a video, check it out on my YouTube channel, dcode:
What makes this CSS-only?
In order to make this solution CSS-only we're going to be using traditional <input type="checkbox">
elements but they're going to be hidden through CSS.
Our CSS will then make use of the :checked
pseudo-class and the general sibling combinator (~
) to dynamically apply either a "on" or "off" style to our switch.
Writing the HTML
Let's start with the HTML. For this, we're going to be wrapping everything inside a <label>
element. This will allow us to click anywhere within the wrapper and it will toggle the state of our hidden checkbox.
<label class="toggle" for="myToggle">
<input class="toggle__input" name="" type="checkbox" id="myToggle">
<div class="toggle__fill"></div>
</label>
Ensure the for
attribute of the <label>
matches up with the id
of the input field, as shown above.
As you can see, we have the regular input field and we have the fill element. The fill element represents everything visible, including the circle slider itself.
Moving onto the CSS
The first thing to do is to define a few variables to control the dimensions of the toggle switch - this allows us to change the "width" in one place, and everything will be sized based on this number.
While we're here, let's display the .toggle
as inline-block
so it flows easier and ensure the correct cursor is displayed:
.toggle {
--width: 40px;
--height: calc(var(--width) / 2);
--border-radius: calc(var(--height) / 2);
display: inline-block;
cursor: pointer;
}
Next, we can hide the actual input field:
.toggle__input {
display: none;
}
Styling the switch fill
Now we're done with the wrapper, let's move onto the switch itself. For this, we can use the variables defined above and apply some basic styles:
.toggle__fill {
position: relative;
width: var(--width);
height: var(--height);
border-radius: var(--border-radius);
background: #dddddd;
transition: background 0.2s;
}
Notice the transition
- this is in place to ensure we get a smooth animation as the background color changes from grey to green (as we'll see shortly).
Styling the switch circle (or slider)
For the circle, we can make use of the ::after pseudo-element in CSS - let's create it like this:
.toggle__fill::after {
content: "";
position: absolute;
top: 0;
left: 0;
height: var(--height);
width: var(--height);
background: #ffffff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.25);
border-radius: var(--border-radius);
transition: transform 0.2s;
}
Most of these are self-explanatory but as shown we're using position: absolute
to position the circle in the top left corner before setting a width and height via defined variables.
Getting the toggle to work
As mentioned earlier, the toggle is going to work by using the :checked pseudo-class and the general sibling combinator.
Let's define a couple of rules below, which will be activated when the <input>
is checked:
.toggle__input:checked ~ .toggle__fill {
background: #009578;
}
.toggle__input:checked ~ .toggle__fill::after {
transform: translateX(var(--height));
}
As we can see, translateX
is used here to push the circle to the right side. In order for this to line up, it must be pushed the same amount as the height of the container.
And that's it! The toggle switch should be fully functional. If anyone else has suggestions for improvement or different styles, please leave them in the replies below 😁
Top comments (2)
This is a GREAT tutorial - I really appreciate it when people make tutorials without unnecessary bloat that makes it harder to learn. Here are two ways to make it even simpler. (1) You do not need "for " in your label or id= in your input when the label surrounds the content. (2) You do not need 3 classes. You can select your css on label[role=toggle], input[role=toggle] and div[role=toggle]. which makes using the pattern MUCH simpler in the html. In fact, you could just set the role for the label and then select the other two items as its siblings. Another suggestion - in tutorials just use named colors instead of hex colors. "Green" and "grey" communicate much more clearly.
@dcodeyt
I have a question about the JavaScript rabble sort you did on YouTube. I was wondering if you could help. I’m following you on Twitter @djdingle247 . Can you reach out there?