DEV Community

Cover image for Create a simple On/Off slide toggle with CSS
James Bubb
James Bubb

Posted on

Create a simple On/Off slide toggle with CSS

Cover photo by Wolfgang Mennel on Unsplash.

I was asked the other day at work to change a two-button setup for managing the active status of a user into some kind of slide toggle widget.

Button Sketch
So instead of clicking a button to either enable/disable a user, there would be some kind of on/off toggle to change the user's status.

In the past, when I've had to do something like this I've just Googled a solution and then copied it into whatever project I've been working on.

This time, I thought it would be nice to create my own from scratch understanding how to create such a widget.

Here's the finished result of the slide toggle:

I opted to go for a simple slide toggle for on/off states using just HTML/CSS to make the widget more accessible and to give me a bit more a challenge!

Here's how I did it.

The Markup

<div class="custom-checkbox">
  <input id="status" 
         type="checkbox" 
         name="status">
  <label for="status">
    <div class="status-switch"
         data-unchecked="Off"
         data-checked="On">
    </div>
  </label>
</div>
Enter fullscreen mode Exit fullscreen mode

Hopefully, the markup speaks for itself but here are some points if you're not sure what's going on above:

  • We wrap everything in a div which we'll use to set an overall height and width for our toggle
  • There is a checkbox input which we'll hide with our CSS styles, the main content will be inside the label element (which itself has a further div inside it)
  • Specifying the input's id inside the for attribute of the label element allows the user to check/uncheck the checkbox input element by clicking the label element
  • The div inside the label has two data attributes (data-unchecked and data-checked) which will form the text for the two states of our slide toggle

Initially, I had tried using two radio button inputs for the slide toggle with each representing either an on or off state.

However, I only got so far with this approach before the styling became a nightmare so I decided to change the radios for a single checkbox which made everything much easier.

As the slide toggle can only have two states; either 'checked' or 'unchecked', it makes more sense to use a checkbox.

The SCSS

The styling of the slide toggle could be done in plain CSS however, using SCSS makes working with the hierarchy of elements easier.

There's a reasonable amount so let's split it up into sections.

The container element

.custom-checkbox {
  width: 260px;
  height: 60px;

  input#status {
    display: none;
Enter fullscreen mode Exit fullscreen mode

The first thing we'll do is just set some dimensions for the slide toggle. Changing the width and height values will scale all of the other elements to fit.

We'll also hide the input element so we don't see the default HTML checkbox input element leaving all of the customisation to the content inside the label.

The label and inner div content

    + label {
      height: 100%;
      width: 100%;
      > .status-switch {
        cursor: pointer;
        width: 100%;
        height: 100%;
        position: relative;
        background-color: grey;
        color: white;
        transition: all 0.5s ease;
        padding: 3px;
        border-radius: 3px;
Enter fullscreen mode Exit fullscreen mode

Using the adjacent sibling selector (the + symbol) we can select the label which is next to the input element.

After ensuring the height and width is 100% we target the inner div of the label, with the class of .status-switch.

This will be the 'unchecked', 'inactive' or 'off' state of the slide toggle.

Some key things here are to ensure the position is set to relative as we'll be absolutely positioning elements inside this and the padding value set will also be used to adjust the inner height and width of the 'slide' button too.

You can choose a background-color for the unchecked state here.

The before and after pseudo-elements

        &:before,
        &:after {
          border-radius: 2px;
          height: calc(100% - 6px);
          width: calc(50% - 3px);
          display: flex;
          align-items: center;
          position: absolute;
          justify-content: center;
          transition: all 0.3s ease;
        }
Enter fullscreen mode Exit fullscreen mode

The above is setting some common styles for the before and after pseudo-elements which will display the On / Off elements.

There's some basic flex styling to position the text content in the centre of each of the elements and we're using the value that we set in for padding in the previous step to reduce the width and height slightly so that there is a little bit of a border around the slide toggle button.

If you were to view the slide toggle element now, it would look a bit like this:

Incomplete toggle widget

That's because of the before and after elements are not visible yet, and they have no content.

So let's add some separate styles to these to position them correctly.

Positioning before and after

        &:before {
          background-color: white;
          color: black;
          box-shadow: 0 0 4px 4px rgba(#000, 0.2);
          left: 3px;
          z-index: 10;
          content: attr(data-unchecked);
        }

        &:after {
          right: 0;
          content: attr(data-checked);
        }
Enter fullscreen mode Exit fullscreen mode

We'll make the before element have a white background which will form the 'button' like part of the slide toggle and set it's left property to 3px which will again, preserve the border around the toggle.

The after element will be positioned on the right hand-side of the containing element.

Both of the elements will get their content set to the respective data-attribute we set in the initial markup.

Our slide toggle should look like this now:

Inactive state

 Updating the checked state

    &:checked + label > .status-switch {
      background-color: green;

      &:after {
        left: 0;
        content: attr(data-unchecked);
      }

      &:before {
        color: green;
        left: 50%;
        content: attr(data-checked);
      }
Enter fullscreen mode Exit fullscreen mode

To finish off our slide toggle, we'll simply target the :checked state of the input element and drill down to the .status-switch class which is assigned to the inner div of the label.

Inside the selector, we can set a new background-color and then essentially just 'swap over' the before and after pseudo-elements.

We do this by updating their left positions and also updating their content properties.

Because of the transition properties that have been set up, the before element appears to glide into position and the background-color of the .status-switch fades in and out.

Checked state

Conclusion

There's a lot you can do to create your own custom inputs, buttons, toggles and sliders with just some CSS.

The aim of this is to make the code a little bit more accessible, removing the need for JavaScript.

I was surprised how relatively easy it was to create the styling for the slide toggle, once I had got to grips with the hierarchy of the elements and what should happen when the input is in a checked or unchecked status.

I've only made use of Sass nesting features in the completed code, just to try and make it as readable as possible but the example could be improved to make use of other Sass features such as variables, mixins etc. to make a more reusable and customisable slide toggle component.

Full code and demo is on Codepen: https://codepen.io/codebubb/pen/OJXRGyN

Follow me on Twitter (@codebubb) for more articles and videos.

Have you got your own custom checkbox or slide toggle examples? Let me know in the comments below!

Top comments (5)

Collapse
 
amnemonic profile image
Adam Mnemonic

Nice tutorial but in my opinion toggle switches are too confusing, they provide poor visual feedback. Please just use checkbox input in your next project. 🙂

uxmovement.com/mobile/the-confusin...

Collapse
 
codebubb profile image
James Bubb

OK, thanks for the feedback!

Collapse
 
khangnd profile image
Khang

Very cool, thanks for sharing.

Collapse
 
codebubb profile image
James Bubb

You're welcome 😀

Collapse
 
mrmascott10 profile image
Michael Scott

I've been needing something as simple as this for weeks. So glad I finally found your post. Thank you!