Fancy checkboxes and radio buttons with CSS

Many young guns ask about how to style custom checkboxes and radio buttons in forms. I prepared a typical markup, a few lines of CSS and some JavaScript functions (Safari label behavior fix included).

For those in a hurry, go straight to Fancy checkboxes demo page. Also simple jQuery version available.

The structure

Each radio button and/or checkbox input element should be surrounded with <label>tags. Here’s the example:

<label class="label_check" for="sample-check">
    <input name="sample-check" id="sample-check" value="1" type="checkbox" />
    Sample Label
</label>

<label class="label_radio" for="sample-radio">
    <input name="sample-radio" id="sample-radio" value="1" type="radio" />
    Sample Label
</label>

The presentation

We are going to remove inputs far away to the left and instead place a background image to each label. Radios and checkboxes will be toggled, because clicking/spacepressing the corresponding label toggles them on or off.

See the CSS sample:

.has-js .label_check,
.has-js .label_radio { padding-left: 34px; }
.has-js .label_radio { background: url(radio-off.png) no-repeat; }
.has-js .label_check { background: url(check-off.png) no-repeat; }
.has-js label.c_on { background: url(check-on.png) no-repeat; }
.has-js label.r_on { background: url(radio-on.png) no-repeat; }
.has-js .label_check input,
.has-js .label_radio input { position: absolute; left: -9999px; }

The behavior

And finally, some JavaScript trickery to handle all the className switching. In Safari, labels are not clickable, hence a few extra Safari speciffic lines.

var d = document;
var safari = (navigator.userAgent.toLowerCase().indexOf('safari') != -1) ? true : false;
var gebtn = function(parEl,child) { return parEl.getElementsByTagName(child); };
onload = function() {
    
    var body = gebtn(d,'body')[0];
    body.className = body.className && body.className != '' ? body.className + ' has-js' : 'has-js';
    
    if (!d.getElementById || !d.createTextNode) return;
    var ls = gebtn(d,'label');
    for (var i = 0; i < ls.length; i++) {
        var l = ls[i];
        if (l.className.indexOf('label_') == -1) continue;
        var inp = gebtn(l,'input')[0];
        if (l.className == 'label_check') {
            l.className = (safari && inp.checked == true || inp.checked) ? 'label_check c_on' : 'label_check c_off';
            l.onclick = check_it;
        };
        if (l.className == 'label_radio') {
            l.className = (safari && inp.checked == true || inp.checked) ? 'label_radio r_on' : 'label_radio r_off';
            l.onclick = turn_radio;
        };
    };
};
var check_it = function() {
    var inp = gebtn(this,'input')[0];
    if (this.className == 'label_check c_off' || (!safari && inp.checked)) {
        this.className = 'label_check c_on';
        if (safari) inp.click();
    } else {
        this.className = 'label_check c_off';
        if (safari) inp.click();
    };
};
var turn_radio = function() {
    var inp = gebtn(this,'input')[0];
    if (this.className == 'label_radio r_off' || inp.checked) {
        var ls = gebtn(this.parentNode,'label');
        for (var i = 0; i < ls.length; i++) {
            var l = ls[i];
            if (l.className.indexOf('label_radio') == -1)  continue;
            l.className = 'label_radio r_off';
        };
        this.className = 'label_radio r_on';
        if (safari) inp.click();
    } else {
        this.className = 'label_radio r_off';
        if (safari) inp.click();
    };
};

Also, be sure to check the previous post about how to preload all those interface graphics.

Demo

Fancy checkboxes demo page

Marko Dugonjić is a designer specialized in user experience design, web typography and web standards. He runs a nanoscale user interface studio Creative Nights and organizes FFWD.PRO, a micro-conference and workshops for web professionals.

Interested in more content like this?