Style a Checkbox as Toggle


What is this article about?

I found this wonderful codepen created by Himalaya Singh. In this pen, Himalaya takes a HTML checkbox and changes it into an ios style toggle button using pure CSS without any JavaScript. This is quite the useful codepen and this article contains my notes on how to best read and understand Himalaya’s CSS.

The CSS and HTML snippets in this article are not Himalaya’s original code (but still heavily inspired by it). I slightly modified the snippets during my analysis for the worse. So definitly check Himalaya’s original codepen after reading this article.

How it works

The overall strategy is to use a HTML input of type checkbox and then to hide it using an opacity of 0. That way the user cannot see the input but they can still interact with it.

In a second step, a so-called knob and a background layer are added to the toggle. The knob is moving from left to right and displays the checked state of the input. It also can contain a text. The layer acts as a visual border for the input. Both the knob and the layer contain a color.

The HTML input type checkbox has two states, checked and unchecked. CSS classes are used via a selector that selects both possible states. Within the CSS classes for each state, the knob and the layer are styled. A CSS transition is used to define how the styling changes when the input transitions between both of it’s states.

HTML and Styling 

A HTML input with type checkbox is created.

<div class="button r" id="button-1">
<input type="checkbox" class="checkbox">
<!--<div class="knobs"></div>-->
<!--<div class="layer"></div>-->

We’ll take care of the knob and the layer later.

The surrounding button is positioned.

position: relative;
top: 50%;
width: 74px;
height: 36px;
margin: -20px auto 0 auto;
overflow: hidden;

Then the input element is styled. To hide the input, an opacity of 0 is used.

/* opacity 0 is entirely transparent, this hides the checkbox but lets the user interact with it still */
position: relative;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
opacity: 0;
cursor: pointer;
z-index: 3;

At this point you will absolutely see nothing any more on your page. To add graphical representation back, let’s start by adding the knob.

It is important to start with the knob because the knob is what gives the layer content. Starting with the empty layer causes the layer to collapse completely. A collapsed div is basically invisible, hard to style and generally a source of confusion.

Adding the knob

In the HTML, activate the knobs div by removing the comment around it. Also add a CSS class that positions the knobs div within it’s positioned parent.

/* styles the div that is inserted below the input checkbox html element */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;

Style the two states checked and unchecked of the knobs div.

#button-1 .knobs:before
content: 'NO';
position: absolute;
top: 4px;
left: 4px;
width: 20px;
height: 10px;
color: #fff;
font-size: 10px;
font-weight: bold;
text-align: center;
line-height: 1;
padding: 9px 4px;
/*background-color: #03A9F4;*/
background-color: #f44336;
border-radius: 50%;
transition: 0.3s cubic-bezier(0.18, 0.89, 0.35, 1.15) all;

/* Style for when the checkbox is checked */
#button-1 .checkbox:checked + .knobs:before
content: 'YES';
left: 42px;
background-color: #03A9F4;
/*background-color: #f44336;*/

The CSS selector above contains .checkbox:checked which is how the checkbox state is targeted using CSS. The first of the two states does not contain any state so this is the default unchecked state. A transition is contained in the first CSS class. The transition defines how the transition between both CSS classes is animated. This animation moves the knob from left to right and vice versa, it changes the text and the color.

Adding the Layer

To also style the layer, first uncomment the layer div in the HTML. Then, using the same principle as for the knobs, define two CSS classes one per checkbox state that define the appearance of the layer in each state and how the transition between the two states is animated.

/* The layer is the background that the slider knob is displayed inside.
It is inserted as a separate div below the input checkbox html element
The layer provides the visual appearance and the outline border around the ckeckbox. */
--dummy-style: foo; /**/
position: absolute;
border-radius: 100px;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
background-color: #fcebeb;
/*background-color: #ebf7fc;*/
/*background-color: #f44336;*/
transition: 0.3s ease all;
z-index: 1;

The general sibling combinator is made of the "tilde" (U+007E, ~)
character that separates two sequences of simple selectors.
The elements represented by the two sequences share the same parent in the document tree
and the element represented by the first sequence precedes (not necessarily immediately)
the element represented by the second one.
#button-1 .checkbox:checked ~ .layer
/*background-color: #fcebeb;*/
background-color: #ebf7fc;
/*background-color: #03A9F4;*/

#button-1 .knobs, #button-1 .knobs:before, #button-1 .layer
transition: 0.3s ease all;

Summary and Next Steps

The toggle works and looks awesome. Things that come to mind are, how to you get translated text onto the toggle? Maybe it is easier to not have any text on the knob of the toggle to save a lot of work. Also the CSS probably should be translated to SCSS if that is what your project uses. Another important part is to use the toggle in a form element of your framework of choice. A test has to be made if the input works nicely with Angular, Vue and React.

Leave a Reply