DEV Community

Cover image for CSS Toggles/Switches
Milan Radojević
Milan Radojević

Posted on

CSS Toggles/Switches

So I made a bit of a pause with posts because university started, but I got some free time now so here's a new post. I got the idea while scrolling through codepen, and thought why not make some toggles with css.

A simple toggle

So how do we make this from scratch. First the toggle has two parts:

  • the body (the orangey part, from the gif we see it can be just an outline, filled in, or a thin bar)
  • and the knob (the purplish part)

And we don't really want to have to add two html tags for each toggle, so we want to add two elements by adding only one tag. To achieve this we use the ::after css selector. What it does is it adds a pseudo-element (the knob) inside of another one (the body).

Here's an example, try changing something:

Two properties that we need for every ::after element are content: ""; and display: block;.

The content property is just the text inside of the pseudo element, and if it's not set the ::after element won't render, so we just set it to empty.

By default ::after elements have display set to inline, but if we want to position the element and do some other things with it we need to set it to block so it behaves like a normal html element.

The rest is just for looks. I'll give one more tip, set the border-radius to a big value, it doesn't matter what it is, just that it's bigger than the dimensions of the toggle and it'll always round it nicely.

Adding the animation

Or rather in this case it's called a transition, and that exactly the name of the property we need to do this.

Instead of our elements "jumping" instantly from one property value (let's say width) to another (maybe we changed it from 100px to 200px), transition eases it by changing the property bit by bit over a set time period. For this to happen we need to tell the browser which properties should be transitioned, so we add:

.toggle::after {
  transition: margin-left 300ms ease;
}
Enter fullscreen mode Exit fullscreen mode

This will transition every change to the margin-left property over a period of 300 milliseconds.

The knob is already on the left, so we got the first state. To get to second state (being on the right) we can add to knob's margin-left property. This is what I used:

.toggle--sliding::after {
  margin-left: calc(var(--toggle-width) - var(--toggle-padding-horizontal)*2 - var(--knob-size));
}
Enter fullscreen mode Exit fullscreen mode

Now, I know it looks a bit intimidating, but here's how I got it.

We need to move it all the way to the right so we move however much the toggle is wide, but since it's a left margin we need to subtract the width of the knob. There's also the padding that we added to our toggle, we also subtract it, twice, since it's applied to both left and right side.

In css calc() is used to calculate something, so instead of having to use just one value (e.g. 40px, 2em, var(--toggle-width)) we can combine them.

var() is used to reference a variable in css.

Adding JavaScript

To change the state of the toggle, we want to apply the togggle--sliding class to it when click on it, or if it already has that class to remove it. Here's the code to do it:

var toggle = document.querySelector("#toggle");

toggle.addEventListener("click", () => {
  toggle.classList.toggle("toggle--sliding");
});
Enter fullscreen mode Exit fullscreen mode

It will add an event listener to an element with id="toggle".
But since it is impractical to write the same code again for every single toggle, we can add the event handler to every element with class="toggle" with this short code:

var toggles = document.querySelectorAll(".toggle");

toggles.forEach((togg) => {
   togg.addEventListener("click", () => {
      togg.classList.toggle("toggle--sliding");
    });
});
Enter fullscreen mode Exit fullscreen mode

Well I hope you got something out of this article. If you have anything to suggest or just wanna say something please do.

Also here's the code for the GIF, it's a bit messy since there were two different animations (or rather transitions):

P.S.

After someone asked about it, I realized I forgot to include in the article instructions on how to check if the toggle is on so here the js for it:

toggle.classList.contains("toggle--sliding");
Enter fullscreen mode Exit fullscreen mode

It will return true if the toggle contains that class (i.e. if it's turned on).

Top comments (6)

Collapse
 
abhishekcghosh profile image
Abhishek Ghosh

Nice. As an extension, you can actually get a hidden or stylized checkbox to maintain the toggle state and get rid of the JS entirely... A pure css only toggle.

Collapse
 
savagepixie profile image
SavagePixie • Edited

Would you mind explaining a bit more how you would go about doing that?

Collapse
 
abhishekcghosh profile image
Abhishek Ghosh

Sorry for the late reply, was inactive for a while. Basically, the idea is to maintain the toggle on/off state with a hidden checkbox's built-in checked state, and use sibling selectors to style the UI.

Something in the lines of:

.chkbox + blah {
// styles for off UI
}
.chkbox:checked + blah {
// styles for on UI
}

Thread Thread
 
savagepixie profile image
SavagePixie

Oh, that's a cool trick! Cheers!

Collapse
 
ubaid profile image
Ubaid Sid

I like this solution of toggle. A little bit of head scratching at the calc() function but I get it. But how do I get true or false value out of it, if I wanted to use it in a real application.

Collapse
 
mikister profile image
Milan Radojević • Edited

You can use toggle.classList.contains("toggle--sliding"); to check if it's on. Another way is to add a hidden checkbox and then get the value from that.

Or if you're using a framework like React or Vue, clicking on the toggle could change a variable inside of the component and the toggle--sliding class could be added based on whether that variable is true or false. Then you wouldn't even need to check on the DOM, because everything is handled from the JS side (at least by you, the framework does the rest).