DEV Community

Cover image for 3 State Switches are not tough - Ultimate guide to CSS Switches using CSS only
Navdeep Mishra
Navdeep Mishra

Posted on

3 State Switches are not tough - Ultimate guide to CSS Switches using CSS only

Hi there, when it comes to switches most people look for modern libraries but building core is very important.
Creating switches are very easy. Today I will be sharing the guide to create CSS switches.

We'll be seeing -

  1. 2 state switches
  2. 2 state switches with custom icons.
  3. 3 state switches

2 State Switches

For creating switches you need a way to figure out the active states. If you are using JavaScript then there are lot of ways you can do it by dynamically adding classes on click. When it comes to css we have one very popular way which is by using the input type checkbox. Which changes it's state and you can target that state easily with CSS only.

Check the below html structure -

<div class="switch1-container">
      <h5> Simple two state switch</h5>
      <div class="switch1">
        <input type="checkbox" id="switch1-input"/>
        <label for="switch1-input"></label>
        <span class="switch-bg"></switch>
      </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Note - Here in this html structure for giving the bg to the switch I experimented with a separate span and used absolute positioning. You can achieve the similar behavior using the ::before pseudo element.

Main thing to know here is that for getting the on status for the switch we can use the checked state of the input element.
You can easily get it on input using :checked pseudo class.

For styling the CSS switch button we have to make use of the input label. The good thing is when you click on the label then input states gets toggled. So hide the input and design the label with good looking round circle.

Once you designed the b and the circle now for moving the circle with the bg to give feel like a switch. Make use of transform property. Use translateX to move the circle and add transition on transform to make it smooth. 🙃

That's it
Your switch is ready. 🫡

Checkout the Css below to understand what we discussed.
Practical knowledge is more important you know.😎

*{
  margin:0;
  padding:0;
}
*,*::before,*::after{
  box-sizing:border-box;
}

:root{
    --clr-primary: #d9d9d9;
  --clr-secondary:#fafafa;
  --clr-clear:#ffffff;
  --clr-grey: #6d6d6d;
  --clr-dark:#1e1e1e;
  --ff-base:'Inter', sans-serif;
  --fw-regular: 400;
  --fs-200:0.875rem;
  --fs-300:1rem;
  --fs-400:1.125rem;
  --fs-500:1.375rem;
  --fs-600:1.75rem;
  --fs-700:3.25rem;

}
html{
  color-scheme: dark;
}
body{
  font-family: var(--ff-base);
  font-size: var(--fs-300);
  font-weight: var(--fw-regular);
}

.container{
  max-width:80%;
  margin: auto;
  height:100dvh;
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
/*   justify-content:center; */
  padding: 4rem;
}

input{
  display:none;
  cursor: pointer;
}
label{
  cursor: pointer;
}

.switch1-container h5{
  margin-bottom: 1rem;
}
.switch1{
  position:relative;
  border-radius: 30px;
  width:6rem;
  height:2.5rem;
  background: #ccc;
  display:flex;
  align-items:center;
  transition: .3s background-color ease;
}
.switch-bg{
  background-color: #eceeef;
  position:absolute;
  top:4px;
  left:5px;
  width:90%;
  height:80%;
  border-radius:30px;
  transition: background-color .2s ease-out;
}


.switch1 label {
  background-color:white;
  border: 1px solid #bbb;
  display:inline-block;
  width:24px;
  height:24px;
  border-radius:50%;
  display:inline-block;
  vertical-align:bottom;
  margin:5px 8px;
  z-index:2;
  transition: transform .3s ease;

}
.switch1 input:checked + label{
  transform: translateX(56px);
}
.switch1 input:checked + label + .switch-bg {
  background-color: dodgerblue;
}

Enter fullscreen mode Exit fullscreen mode

2 State Switches with Custom Icon
For adding the icon to your switches is very easy you can use various way of doing that. I picked it using css only even no extra Html. I used fontawesome for directly using icons in the stylesheet. The one I created is useful when you want to toggle between dark and night mode. 😎

Just add this to your styles sheet and in Html file.

Html

<div class="switch2-container">
      <h5> Simple two state siwtch with custom icon</h5>
      <div class="switch2">
        <input type="checkbox" id="switch2-input"/>
        <label for="switch2-input"></label>
        <span class="switch-bg"></switch>
      </div>
    </div>
Enter fullscreen mode Exit fullscreen mode

Css


.switch2-container h5{
  margin-bottom: 1rem;
}
.switch2{
  position:relative;
  border-radius: 30px;
  width:6rem;
  height:2.5rem;
  background: #ccc;
  display:flex;
  align-items:center;
  transition: .3s background-color ease;
}
.switch2-bg{
  background-color: #eceeef;
  position:absolute;
  top:4px;
  left:5px;
  width:90%;
  height:80%;
  border-radius:30px;
  transition: background-color .2s ease-out;
}


.switch2 label {
/*   background-color:white; */
/*   border: 1px solid #bbb; */
  display:inline-block;
  width:24px;
  height:24px;
  border-radius:50%;
  display:inline-block;
  vertical-align:bottom;
  margin:3px 8px 0px;
  z-index:2;
  transition: transform .3s ease;

}
.switch2 input:checked + label{
  transform: translateX(56px);
}
.switch2 input:checked + label + .switch-bg {
  background-color: dodgerblue;
}

.switch2 label::before{
  font: var(--fa-font-solid);
  content: "\f185";
  color: gold;
  font-size: 1rem;
  border: 1px solid #ccc;
  border-radius: 50%;
  padding: 4px;
}
.switch2 input:checked + label::before{
  content: "\f186";
  color:gold;
  border: none;
}
.switch2 input:checked + label + .switch-bg{
  background: black;
}
Enter fullscreen mode Exit fullscreen mode

Now the third one is the powerful one.
3 State Switches

The godfather

For this one I use input type radios because of one good behavior that when you have radio group and each has same name attribute then if you select one then the other one will be de-selected automatically.

I created my html structure like this.

 <div class="switch3-container">
      <h5> Simple three state siwtch</h5>
      <div class="switch3">
        <input type="radio" id="switch3-radio1" name="radio"/>
        <label for="switch3-radio1">FrontEnd</label>

        <input type="radio" id="switch3-radio2" name="radio"/>
        <label for="switch3-radio2">BackEnd</label>

        <input type="radio" id="switch3-radio3" name="radio"/>
        <label for="switch3-radio3">FullStack</label>

      </div>
    </div>

Enter fullscreen mode Exit fullscreen mode

For this I have used three html input elements with type radio.
Each has it's own label for showing the text.
For highlighting the selected option, I have use the ::before pseudo element on .switch3.

Thanks to new :has selector of css which made this more easier and cleaner html as well.

Check the css below -

.switch3-container h5{
  margin-bottom: .7rem;
}
.switch3
{

  position: relative;
  height:50px;
  display:flex;
  justify-content:space-around;
}
.switch3 label{
  padding: 1rem;
  z-index:1;
}
.switch3::before{
  content: '';
  position: absolute;
  left:0;
  width: 34%;
  height: 100%;
  background: #14142a;
  border: 1px solid #fff;
  border-radius: 12px;
  opacity: .5;
  transition: .5s left ease;
}

.switch3:has(#switch3-radio1:checked)::before{
  left: 0%;
}
.switch3:has(#switch3-radio2:checked)::before{
  left: 33%;
}
.switch3:has(#switch3-radio3:checked)::before{
  left: 66%;
}
Enter fullscreen mode Exit fullscreen mode

That's how all three looks like.

Toggle switches using css only with three state switch

Experiment with my codepen - Codepen Live

Similarly we can create 4 state switch as well.
If you have any doubts or questions, feel free to discuss in the comments.
Any better approach? let's discuss. 😎

Thanks for reading.
Please follow for more Webdev content. 😉

Navdeep M.

Top comments (10)

Collapse
 
artxe2 profile image
Yeom suyun

Using DEV.to's embed functionality, you can attach examples that actually run on sites like CodePen.

Collapse
 
citronbrick profile image
CitronBrick

I had only thought about syntax highlighting.

triple-backtick language-name
code
triple backtick

Wasn't aware of embedding before.

Collapse
 
navdeepm20 profile image
Navdeep Mishra

Ohh! I didn't knew this before. 😉 Thanks for sharing. 💓

Collapse
 
cezarytomczyk profile image
Cezary Tomczyk

Nice one. I'd add styles for disabled state yet.

Collapse
 
navdeepm20 profile image
Navdeep Mishra

Yes, actually I literally forgot that 😂😅

Collapse
 
phillip27 profile image
Phillip Munn

I tried your sample in Firefox and couldn't get the tri-state switch to work. Perhaps there's some CSS feature that doesn't exist there yet?

Collapse
 
navdeepm20 profile image
Navdeep Mishra

Yes, I also noticed that. It's due to the :has not fully implemented in firefox.
See - connect.mozilla.org/t5/ideas/when-...

I'll provide alternate solution without :has selector.

Collapse
 
navdeepm20 profile image
Navdeep Mishra

@phillip27 I have added a solution for without :has which works with firefox too.

Collapse
 
sreno77 profile image
Scott Reno

This is really cool. Thanks!

Collapse
 
xkochevnikx profile image
Святослав Деев

good afternoon, thank you very much for your help! Hello from St.Petersburg)