Christian Heilmann

Checkboxes make excellent buttons

Thursday, September 24th, 2020 at 8:57 pm

checking off a list with a marker

Photo by TeroVesalainen

I like checkboxes – they give me a simple way in a tool to turn modes on and off without using much space. I especially like it that you can style them with CSS without jumping through hoops like you need to with buttons.

I like to use checkboxes as buttons. And here’s how.

A checkbox is a binary state. It is checked or not. So instead of reading out the state in an event handler, I tend to read the checked property.

What does this mean?

Take a look at this codepen :


See the Pen
Checkboxes as easy to style buttons
by Christian Heilmann (@codepo8)
on CodePen.


Looks neat and works. Let’s take a look at the how.

The first thing I do is to ensure that my code is accessible. That’s why every checkbox needs a label to explain to assistive technology like screen readers what it is.

<input type="checkbox" id="doublewide">
<label for="doublewide">Double width</label>
<input type="checkbox" id="doublehigh"> 
<label for="doublehigh">Double height</label>
<div id="box"></div>

This also has the real practical upshot that when I click on the label text (which is normally much bigger than the checkbox) I change the state of the checkbox. This also helps a lot on mobile devices. From a look and feel point of view, it means I don’t need the checkboxes any longer, so let’s hide them off screen:

/* Hide checkboxes */
[type=checkbox] {
  position: absolute;
  left: -50vw;
}

We style the labels to look “not selected” or greyed out:

label {
  background: #ccc;
  padding: 5px 10px;
  color: #333;
}

And we colour them when the checkbox is checked (let’s also add a transition to make it look smoother):

/* Selected */
[type=checkbox]:checked + label {
  background: #369;
  color: #fff;
  transition: 400ms;
}

Quick aside: you can also style them to interact to keyboard users focusing on the checkboxes and to mouse interaction on the label itself:

[type=checkbox]:focus + label {
  background: #9cf;
  color: #000;
}
[type=checkbox] + label:hover {
  background: #9cf;
  color: #000;
}

Now on to the JavaScript interaction. First, we need some references to the checkbox DOM elements (it makes sense to cache that as reading the DOM is expensive).

const dw = document.querySelector('#doublewide');
const dh = document.querySelector('#doublehigh');

We’ll define a change function to be called every time one of the checkboxes is clicked. In this one we read out the checked state of the checkboxes and react accordingly. In this case, changing the width of the box.

const change = _ => {
    box.style.width = dw.checked ? '100px' : '50px';
    box.style.height = dh.checked ? '100px' : '50px';
 };

The last thing to do is to add Event Listeners to the checkboxes to call the change function:

dw.addEventListener('click', change);
dh.addEventListener('click', change);

We could even use Event Delegation instead and apply this to all checkboxes. That way we don’t need to add a lot of listeners and we can dynamically add and remove checkboxes without having to re-iterate over them.

document.body.addEventListener('click', e => {
  if (e.target.type === 'checkbox') {
    change();
  }
})

Nothing magical here, but I really like the fact that instead of having to store the state myself, all I need to do is read the checked state and leave the interaction to the browser.

Share on Mastodon (needs instance)

Share on Twitter

My other work: