CSS Is More Powerful Than You Think It Is! — Part 1

Checkbox Hack: A cool trick to make simple toggle interactions without using JavaScript.

Anshul Sanghi
5 min readJan 22, 2018

Disclaimer: This trick may have serious issues from accessibility point of view (keyboard navigation, speech synthesis, etc.). I suggest you to not use it if those things are an essential part of the website you are building.

I recently worked on a project for a designer. It was his portfolio website. The design is quite unique in terms of how the navigation works. You may visit the website here. But, this wasn’t just another development project, we discussed and decided to make the navigation interaction purely based on CSS, no JavaScript in order to improve performance of the page. This is what we are going to look at in this post.

First let’s dive a little deeper into the basics of this hack. The three most essential CSS concepts that you need to know for this are the Pseudo-Selectors, Attribute Selectors, and the General Sibling Combinator (or sibling selector in simpler words). These concepts are out of the scope of this post, so I suggest you learn about them first.

We are going to work with :checked, and :not pseudo-selectors, tilde ~ sibling selector, and for attribute selector in CSS.

Here’s what we are going to build first:

Let’s get the basic markup done:

<input type="checkbox" id="checkbox" hidden>
<label for="checkbox">Toggle Div</label>
<div id="toggle"></div>

The input is hidden because we don’t actually want to show the input itself. Instead we can use it’s label because clicking on the label will change the state of the input as well. It is important that input , label , and the div element that you are going to manipulate based on the state of the input are siblings to each other in the markup to make use of sibling selectors.

Now that we have the markup, let’s dive into the CSS part. For this demo, let’s fix the position of the div that’s going to show/hide and make it full height:

#toggle {
height: 100vh;
width: 400px;
background: red;
position: fixed;
top: 0;
}

Now let’s see how we can toggle this div. I am using position property to put the div out of the viewport to the left and move it based on condition. You can use other effects to show hide like opacity. Basically any animatable property can be used.

First, when the checkbox is not checked, the div should be out of the viewport:

#checkbox:not(:checked) ~ #toggle {
left: -450px;
}

This is simple to understand if you know selectors. We first use :not pseudo selector with :checked pseudo selector to reverse the condition, then select it’s sibling div with ~ selector. Now whatever CSS we write in this block will be applied to the div only when the checkbox is not checked.

Similarly, for checked state, we simply omit :not pseudo selector:

#checkbox:checked ~ #toggle {
left: 0;
}

That’s all we need to make the functionality work, you can add transition property to make it animated.

One thing that you might not know is that you can have multiple labels for a single input. So it’s not limited to only a single element to toggle the div.

If you have followed along this far and test it in the browser, you’ll see that there is no way to hide the div as the label itself goes under the div and is not accessible then. You may use z-index, or move the label along with the div based on the state, or something else, it’s better that you play with that for better understanding this.

This trick can also be used to build elements like accordions as well. If you did visit the website I mentioned at the beginning, you might be wondering how I am handling 2 checkboxes where checking one automatically unchecks the other. Think about what you have learned so far and what are the other input types that might help us. Do you have your answer now? Radio Inputs!

Every HTML developer knows how radio inputs work. You set a common name for them and only one of them can be selected at a given time by default. Using the same concepts above with the radio input type is what is being used on the mentioned website. All the selectors and concepts remain the same, only html changes.

Let’s see an example for that as well. Here’s what we are going to build:

The markup is very similar with just small changes:

<input type="radio" name="navigation" id="radio1" hidden checked>
<label for="radio1">Toggle Div</label>
<div id="toggle1"></div>
<input type="radio" name="navigation" id="radio2" hidden>
<label for="radio2">Toggle Div</label>
<div id="toggle2"></div>

There are two radio inputs with similar structure as previous code. We have kept the first one checked by default. Now we toggle each of them (move in and out of viewport) based on the state of the radio buttons. Here’s the complete CSS:

#toggle1, #toggle2 {
height: calc(100vh - 50px);
width: 100vw;
position: absolute;
top: 50px;
transition: all 0.4s ease-in;
}

#toggle1 {
background-color: red;
}

#toggle2 {
background-color: green;
}

#radio1:checked ~ #toggle1 {
left: 0;
}

#radio1:not(:checked) ~ #toggle1 {
left: -150vw;
}

#radio2:checked ~ #toggle2 {
left: 0;
}

#radio2:not(:checked) ~ #toggle2 {
left: 250vw;
}

This should be pretty easy to understand as we have already discussed everything that’s been used here.

That’s all folks. That’s how you make interactions with just some simple CSS concepts that people usually do using JS. I have shown very basic examples here. This trick is useful in a lot of cases. Play around with it and if you make something interesting, do share. Again, if you would like to see a live example using this trick, visit this website and play around with the navigation both on desktop and mobile, both of which are purely based on this trick.

This post was the first of this series. Next we are going to look into how we can use :before and :after pseudo elements to create cool effects, add tooltips, as well as make a table properly responsive on mobile devices.

If you have any questions or suggestions, leave them below in the comments section. Follow me to stay updated on this series and learn more CSS tricks that will make you a better CSS developer as well as a better Web Developer.

Cheers.

--

--

Anshul Sanghi

Full-Stack Developer majorly working with React & NodeJS (TypeScript). Experienced in Go. Beginner in Rust. Freelancer.