In the past, I worked on several projects looking for a way to deal with custom styled proposed by some good designers to change aspect of form controls like checkbox, radio, file input, etc. As you may know, these elements are historycally not stylable for an accessibility reason. (as I heard). Still. I have a way to style checkboxes and radio buttons… Ready?

This blog post is a translation from the 2013 original post written in french. The technique used here is still used in production and works really fine.

Codes and styles I will show you are using CSS level 3 selectors. Old browsers are not concerned by the changes I will propose to you. An automatic fallback to the classical old styles will benefit to these old engines.

Why are we trying to style these form controls?

To harmonize styles between browsers, for sure, but also to make some of them more effective in precise contexts.
My goal is not to explain how the context works, my goal is just to give you technical solution. Be creative then 🙂

Slightly modify the checkboxes appearance

With that example, we will write a solid base for our CSS. It will give me the possibility to explain how selectors are working.
What I want to get.

Aperçu des styles attendus

See the demo

Let’s start with the HTML code. A simply composed code with one label and one input in each p.

   <input type="checkbox" id="test1">
   <label for="test1">Red</label>
   <input type="checkbox" id="test2" checked="checked">
   <label for="test2">Yellow</label>
   <input type="checkbox" id="test3" disabled="disabled">
   <label for="test3">Green</label>
   <input type="checkbox" id="test4" checked="checked" disabled="disabled">
   <label for="test4">Brown</label>

You have now in the order 3 checkboxes: 1 empty, 1 checked and 1 disabled and checked.

Let’s go with CSS.
The input:checkbox element is not stylable. We will play with the styles of the label element and its two pseudo-elements.

/* Hide the checkbox */
[type="checkbox"]:checked {
  position: absolute;
  left: -9999px;
/* Prepare the label */
[type="checkbox"]:not(:checked) + label,
[type="checkbox"]:checked + label {
  position: relative; /* permet de positionner les pseudo-éléments */
  padding-left: 25px; /* fait un peu d'espace pour notre case à venir */
  cursor: pointer;    /* affiche un curseur adapté */

It’s precisely in these selector you will find the magic: I will show custom checkbox with my recent browser, and classical ones with my old browser.

Targetting only recent browsers

All our custom styles will use this type of selector: [type="checkbox"]:checked or [type="checkbox"]:not(:checked) in there declaration. Which means if the web browser doesn’t recognize the selector, these styles will not be applied. It’s a kind of feature detection thanks to the selector.

Code explanation

The first déclaration allow you to place checkbox outside the viewport, on the left side to hide it visually without using display:none;

The second one makes the label element more attractive. The space is done to meet the futur checkbox.
The relative position is useless here to precisely place the checkbox (the label become the reference).

Creating our checkboxes

Now, we will visually create the checkbox thanks to :after (the check symbol) and :before (to make the box) pseudo-elements.

/* Checkboxes aspect */
/* :before is for the box */
[type="checkbox"]:not(:checked) + label:before,
[type="checkbox"]:checked + label:before {
  content: '';
  position: absolute;
  left:0; top: 2px;
  width: 17px; height: 17px; /* dim. of the box */
  border: 1px solid #aaa;
  background: #f8f8f8;
  border-radius: 3px;
  box-shadow: inset 0 1px 3px rgba(0,0,0,.3) /* slight inner shadow */
/* Check symbol aspect */
[type="checkbox"]:not(:checked) + label:after,
[type="checkbox"]:checked + label:after {
  content: '✔';
  position: absolute;
  top: 0; left: 4px;
  font-size: 14px;
  color: #09ad7e;
  transition: all .2s; /* animation incoming :p */
/* if "not checked" */
[type="checkbox"]:not(:checked) + label:after {
  opacity: 0; /* invisible */
  transform: scale(0); /* scaled down to 0 */
/* If "checked" */
[type="checkbox"]:checked + label:after {
  opacity: 1; /* visible */
  transform: scale(1); /* scaled up to normal */

The first two declarations are for the box and the check symbol aspects (size, color, etc). The two next define the states: not-checked or checked. An animation is played because of the transition property.

Styling differents states

Basics done, now we will set the other status: disabled, disabled checked, focused). Find below some styling ideas, but feel free to edit them.

/* Disabled aspect */
[type="checkbox"]:disabled:not(:checked) + label:before,
[type="checkbox"]:disabled:checked + label:before {
  box-shadow: none;
  border-color: #bbb;
  background-color: #ddd;
/* Disabled checked aspect */
[type="checkbox"]:disabled:checked + label:after {
  color: #999;
/* Styling label when disabled? :p */
[type="checkbox"]:disabled + label {
  color: #aaa;
/* Focused item */
[type="checkbox"]:checked:focus + label:before,
[type="checkbox"]:not(:checked):focus + label:before {
  border: 1px dotted blue;

That’s all for these custome styles.

Don’t forget to use vendor prefix with some of the CSS3 properties.

Let’s go further – Advanced styles

I will not explain everything about this demonstration, the idea is exactly the same.
You can now compose a more complexe HTML code using the same logic, pseudo-class and pseudo-elements.
Here another idea of what you can do with this code.

See the demo

These two demonstrations are using pixel unit (px) to make examples more understandable. You should try with  em, % or rem to create a more flexible and maintainable code, depending on your experience and web project.

Looking for ideas?

Now you can do what you want with checkboxes (or radio), find below so inspiration to go further.

Checkbox animée
Animated Checkboxes

Light Saber Checkboxes

Other ideas for checkboxes

And yes, really, you can do the same with radio buttons. Did you try?

Your turn now!

Cet article est également disponible en : French