CSS Toggles/Switches
Tags: css, ui, webdev, tutorial
Published: 2019-10-03 15:21:28

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

{% codepen https://codepen.io/mikister/pen/PoYMmMe default-tab=html,result %}


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:

{% codepen https://codepen.io/mikister/pen/pozMwWv default-tab=css,result %}

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:

```css
.toggle::after {
  transition: margin-left 300ms ease;
}
```

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:

```css
.toggle--sliding::after {
  margin-left: calc(var(--toggle-width) - var(--toggle-padding-horizontal)*2 - var(--knob-size));
}
```

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:

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

toggle.addEventListener("click", () => {
  toggle.classList.toggle("toggle--sliding");
});
```

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:

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

toggles.forEach((togg) => {
   togg.addEventListener("click", () => {
      togg.classList.toggle("toggle--sliding");
    });
});
```

----

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):

{% codepen https://codepen.io/mikister/pen/GRKaNeY %}


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:

```js
toggle.classList.contains("toggle--sliding");
```

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