Everything Frontend

Cross-browser CSS3 Image-Free Custom Checkbox

A while ago I wrote an article on creating custom checkbox in webkit which comes very handy when you a mobile but on desktop you need more compatibility. In this post I’m going to show you how to create an image-free cross-browser pure-css (except for IE < 9) custom checkbox element.

In webkit version we used input element itself for a box and a :before pseudo element to create a tick. This trick won’t work in any other browser and it really shouldn’t because according to the standard tags without content like <input> or <br> can’t have pseudo elements. That means that we need to create some extra markup:

<span class="custom-checkbox">
    <input type="checkbox" checked/>
    <span class="box"><span class="tick"></span></span>

The reason <span class="box"> is located next to checkbox and not wrapped around it is because we want to stylize it based on input state using sibling selector. For example selector input:active + .box will match our span when user activates a checkbox. Additionally CSS3 provides us with :checked pseudo-selector that solves the problem of translating checked state to our replacement. Finally we overlay real checkbox on top of our custom markup and set it’s opacity to 0. Tick itself is created in same manner as it was in webkit version. Here’s complete code for all browsers supporting CSS3:

.custom-checkbox {
    position: relative;
    display: inline-block;

.custom-checkbox > .box {
    position: relative;
    display: block;
    width: 14px;
    height: 14px;
    border: 1px solid #ccc;
    background-color: #eee;
    border-radius: 4px;

.custom-checkbox > .box > .tick {
    position: absolute;
    left: 2px;
    top: -2px;
    width: 14px;
    height: 6px;
    border-bottom: 2px solid #333;
    border-left: 2px solid #333;
    -webkit-transform: rotate(-45deg);
    -moz-transform: rotate(-45deg);
    -o-transform: rotate(-45deg);
    -ms-transform: rotate(-45deg);
    transform: rotate(-45deg);
    display: none;

.custom-checkbox > input:checked + .box > .tick {
    display: block;

.custom-checkbox > input {
    position: absolute;
    outline: none;
    left: 0;
    top: 0;
    padding: 0;
    width: 16px;
    height: 16px;
    border: none;
    margin: 0;
    opacity: 0;
    z-index: 1;

.custom-checkbox > input:active + .box {
    border-color: #aaa;
    background-color: #ddd;


In order to support IE7 and IE8 we will need a little bit of JavaScript (I will use jQuery) to emulate :checked pseudo selector with a regular class like this:

if ($.browser.msie && parseInt($.browser.version) < 9) {
  var inputs = $('.custom-checkbox input');
  inputs.live('change', function () {
    var ref = $(this),
      wrapper = ref.parent();
    if (ref.is(':checked')) wrapper.addClass('checked');
    else wrapper.removeClass('checked');

Now we need to add another rule to show checkbox based on that class:

.custom-checkbox.checked > .box > .tick {
    display: block;

Be aware that it is important to add this as separate rule because IE < 9 refuses to parse any rule with selectors containing unrecognized pseudo selector like :checked.

Now we need to fix rotation of a tick 45 degrees counterclockwise. It’s done using filter property with a special transform matrix:

.oldie .custom-checkbox > .box > .tick {
    filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865476, M12=0.7071067811865475, M21=-0.7071067811865475, M22=0.7071067811865476, sizingMethod='auto expand');

I’m using html tag css class technique from html5 boilerplate to target old IE browsers. Now we handle transparency:

.oldie .custom-checkbox > input {
    filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0);

Finally there are few other CSS fixes that have nothing to do with functionality but remove IE oddities:

.oldie .custom-checkbox {
    zoom: 1;

.oldie .custom-checkbox > .box > .tick {
    left: 1px;
    top: -5px;
    zoom: 1;

You can see checkbox in action on a demo page.