Quick Tip: Easy CSS3 Checkboxes and Radio Buttons

Quick Tip: Easy CSS3 Checkboxes and Radio Buttons

Tutorial Details
  • Topic: CSS3
  • Difficulty: Intermediate
  • Estimated completion time: 20 mins

Ever wondered how to style checkboxes and radio buttons, but without JavaScript? Thanks to CSS3 you can!


Step 1: Understanding the Process

Recommended Reading: The 30 CSS Selectors you Must Memorize

For those of you that feel confident in your CSS abilities already and just want a nudge in the right direction, here is the most important line of CSS in the entire tutorial:

input[type="checkbox"]:checked + label {
}

Now, for those of you who feel you may need more direction, fear not,read onward!

Alright, so back on topic now. What exactly will we be doing? Well, due to CSS3′s nifty little :checked pseudo selector, we’re able to target an element based on its checked (or unchecked) status. We can then use the “+” adjacent sibling selector from CSS2.1 to target the element directly following the checkbox or radio, which in our case is the label.


Step 2: Setting up our HTML

Now, we start off by creating our HTML and CSS files (or however you prefer handling your styles) and get to work. I’ll assume you know how to do that, so we won’t have to get into it.

For the purpose of getting you on your way, I will only demonstrate this technique on a checkbox, but the process for radio buttons is identical, and is included in the source.

Okay, let’s actually begin then, shall we? We start by creating our checkbox input, followed by a label.

<input type="checkbox" />
<label>Styled Check Box</label>

Now, just in case you don’t know much about the <label> element, you must connect the input and the label in order for you to interact with the input through the label. This is done by using, for="" and the input’s ID.

<input type="checkbox" id="c1" name="cc" />
<label for="c1">Check Box 1</label>

I also will add a <span> inside of the label, which is more personal preference than anything else, but all will become clear in step 3.


Step 3: What We’re Here For: CSS

This is where the fun begins. First thing we do, which is the basis for this whole tutorial, is hide the actual checkbox.

input[type="checkbox"] {
    display:none;
}

Now that that’s done, we can style the label, but more specifically the span inside of the label. I do it this way in order to give myself more control over the exact position of the check box. Without it you would probably be using a background image in the label directly, and positioning it may become difficult.

input[type="checkbox"] {
    display:none;
}
input[type="checkbox"] + label span {
    display:inline-block;
    width:19px;
    height:19px;
    background:url(check_radio_sheet.png) left top no-repeat;
}

Alright, let me explain this quickly. First off, notice the background. I have a small sprite sheet for these, so all I have to do is set the background position on this span. The span itself is the exact width and height of each “sprite” in the sheet, making it easy to define each state.

Our sprite sheet

Our sprite sheet

Here is the rest of the CSS, specific to my styling. This is purely for aesthetics and specific to either my taste or this design.

input[type="checkbox"] {
    display:none;
}
input[type="checkbox"] + label span {
    display:inline-block;
    width:19px;
    height:19px;
    margin:-1px 4px 0 0;
    vertical-align:middle;
    background:url(check_radio_sheet.png) left top no-repeat;
    cursor:pointer;
}

Step 4: Making it Work

There’s not too much work left, so let’s get this wrapped up. The last thing you will need to do is provide a state for the element when the input is checked, and optionally also for on hover. This is quite simple, just take a look!

input[type="checkbox"] {
    display:none;
}
input[type="checkbox"] + label span {
    display:inline-block;
    width:19px;
    height:19px;
    margin:-1px 4px 0 0;
    vertical-align:middle;
    background:url(check_radio_sheet.png) left top no-repeat;
    cursor:pointer;
}
input[type="checkbox"]:checked + label span {
    background:url(check_radio_sheet.png) -19px top no-repeat;
}
The final result

Notice that because I used a sprite sheet, all I have to do is change the background position. Notice, also, that all I had to do to style the label’s span for when you “check” a checkbox/radio button, was use the CSS3 :checked pseudo selector.


Quick Note on Browser Support

Pseudo selectors have great support across all browsers, but IE spoils the party. You can pretty much forget about :checked in IE9 and below, but the fallback is graceful enough.

IE9
This is IE9 on Windows 7, believe it or not..

Mobile browsers are an issue too – support of :checked is unclear. Mobile Safari pre iOS 6 doesn’t support it, for example.


Conclusion

Alright, so we’re done, right? Well let’s just double check. First the HTML:

<input type="checkbox" id="c1" name="cc" />
<label for="c1"><span></span>Check Box 1</label>

Does yours look the same? Don’t forget to add in that <span>! When experimenting with this on your own, I highly suggest finding new or different ways of doing this part. I would love to see what you come up with to make it more efficient. Now for the CSS:

input[type="checkbox"] {
    display:none;
}
input[type="checkbox"] + label span {
    display:inline-block;
    width:19px;
    height:19px;
    margin:-1px 4px 0 0;
    vertical-align:middle;
    background:url(check_radio_sheet.png) left top no-repeat;
    cursor:pointer;
}
input[type="checkbox"]:checked + label span {
    background:url(check_radio_sheet.png) -19px top no-repeat;
}

Everything present? Perfect. Keep in mind that a lot of this styling is specific to the style I created for the demo files. I encourage you to create your own, and experiment with placement and presentation.

In conclusion, the most important thing you could carry away from this is the very first line of CSS I wrote at the very top:

input[type="checkbox"]:checked + label {
}

Using that, you can create a whole manner of different things. The possibilities with :checked go beyond checkboxes and radios for use in forms, but I’ll leave you to experiment with that on your own. I hope you enjoyed this short article and I hope you take what you see here and expand or improve upon it!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.nerdskullz.com Pietro – Nerdskullz

    The conclusion should be “for God’s sake avoid using IE”… By the way great article ;)

  • http://emiliocobos.net/ Emilio Cobos Álvarez

    You should use position: absolute; clip: rect(1px, 1px, 1px, 1px); instead of display: none; for accessibility.

    • http://www.snaptin.com Ian Yates

      Very good point bringing accessibility up – thanks :)

    • http://twitter.com/ted_drake ted_drake

      I agree with this comment. Display:none hides the input from screen readers and other assistive technology. So the user would hear about the label, but wouldn’t be able to interact with the input. Also note, you could do something like this:

      input[type=checkbox]:checked + label {color:red}
      Place the span over the checkbox, allowing the user to click on the label to check the hidden checkbox. This will give you the desired effect while also making it accessible.

  • http://www.codelabgh.com Joe

    Wow this is way to cooool. IE is always going backwards. That wont stop us fronteers.

    • http://codeconquest.com Charles @ CodeConquest.com

      Not true. IE 10 is catching up. But it will be a while before the older versions die out.

      • Matt

        Oh, come on! People have been saying this for years: “IE [next version] will be better. We’ll just have to deal with the older versions for a little while longer.” IE 9 is still emerging, and it’s already out of date. Due to Microsoft’s release/update methods, we’ll ALWAYS be dealing with old versions of IE.

  • Prateek Gupta

    Hi Nick Dugger,
    Thanks for this, ya its really Easy and great CSS3 Checkboxes and Radio Buttons.

  • Alexey Raspopov

    Good, but will not work on Mobile Safari

    • http://www.snaptin.com Ian Yates

      No reason why not (?) iOS 6.0 Safari:

      • Alexey Raspopov

        SUDDENLY
        Thanks for that :)
        And I’m pretty sure that on iOS < 6 it's will no work :)

        • http://front-back.com/ jkneb

          Hi simply put a onclick=”" on the DOM element for Safari iOS 5.1 and it’ll work

          • Alexey Raspopov

            It’s disgusting! Why I should put JavaScript code within HTML?

          • http://front-back.com/ jkneb

            :) indeed. Well it’s only if you want it working on ios<6. Big thanks to Apple !

      • Mihkel

        Not working on ios 5.1 mobile safari. The :checked seems to be not supported

        • http://www.snaptin.com Ian Yates

          I can’t find any decent compatibility tables for pseudo selectors (which include mobile browsers) so it’s a difficult one to track properly.. Thanks for the feedback on iOS 5.1, I’ll update the post :)

        • Nick Dugger
          Author

          According to the MDN, :checked works since Mobile Safari v1.3
          https://developer.mozilla.org/en-US/docs/CSS/:checked#Browser_compatibility

          If anyone can find a compatibility table that says otherwise, please let me know!

        • Alexey Raspopov

          No, :checked is supported on iOS 5.1. Linkage by attribute for (for labels) doesn’t support in iOS < 6

          • Artyom Kolnogorov

            Linkage works in iOS. Use “onclick” attribute:

            Check Box 1

  • http://apkestudio.com Francisco Aguilera G.

    Great article.
    It will be good to see a similar solution to style a select element.

  • http://www.darkyndy.com Paul Comanici (darkyndy)

    Nice tutorial. Made some changes to the code (less css) http://jsfiddle.net/darkyndy/jgE7Y/

  • Chung Xa

    Very good, but it’s much nicer if keyboard events worked

  • Maux

    Example using ‘: before’ on lables instead of ”
    http://jsbin.com/efewag/2/edit

    • Nick Dugger
      Author

      Very nice. Once upon a time I tried this same method. Not sure why I didn’t here. Good thinking!

      • http://www.foralien.com Anna L.

        May be, because checking/un-checking does not work with :before approach when user click on checkbox’s replacing picture itself instead of clicking on whole label (what a lot of people do in attempt to be precise).

        • http://gustavoguichard.com Gustavo Guichard

          It will work if you put a padding-left into the label, like this:
          padding-left: 20px;
          Just try it..

  • http://www.customicondesign.com Custom Icon Design

    I dont know whether I can use it in my next project. There are so many browser it can’t support. I hate so many browser and the compatibility:)

  • http://www.step4wd.com M. Ahmad Zafar

    What about retina display? These images will appear blur

    • http://www.snaptin.com Ian Yates

      Incorporating Hi Res Sprite Sheets wouldn’t be a problem, or you could even use webfont icons through :before pseudo elements thinking about it (fontawesome has an “icon-check” and “icon-check-empty”).. Perhaps worth demonstrating when I have a spare five minutes :)

      • http://www.step4wd.com M. Ahmad Zafar

        Yes please spare 5 mins for the webfont icons approach through pseudo elements :-)

        • http://www.snaptin.com Ian Yates

          No need now – Maux has done it with special characters (same principle). Nice job Maux :)

  • http://www.idesignit.co.il Elron

    Thats really neat!
    Great thinking Nick, well done!

  • Maux

    Here another example using “before” instead of empty tags “span”, and using special characters instead of sprite images. This example has been tested in “Firefox and 16.0.1/Win7″ and the font used was “sans-serif”…
    http://jsbin.com/itejak/1/edit

    • http://gustavoguichard.com Gustavo Guichard

      You still need to add the padding-left into the label to be able to click in the checkbox itself.

  • kshirod

    Wow…cool tutorial :)

  • http://codeconquest.com Charles @ CodeConquest.com

    Didn’t know it was possible to include attributes in CSS selectors. I’ll remember that!

  • http://www.remi-grumeau.com Remi Grumeau

    Well, nothing new in the concept, and you are missing two spot:
    - Accessibility
    - Retina display

    Fixing it is easy: get rid of PNG and use pseudo-elements EM-sized instead. Then you don’t have to make some crappy @2x PNG fix for Retina, and also support partially sighted people that increase A LOT their browser’s default font size.

    I’m not commenting about mobile browser bad support of it, you already did that. Note that IE8 fully support adjacent selector btw.

    • Remi Grumeau

      There, sounds way better to me! –> http://jsfiddle.net/pbFSF/

      • http://www.joezimjs.com Joe Zim

        If this looks better to you, then there’s something wrong in your head.
        http://www.joezimjs.com/wp-content/uploads/badcheck.png

        That’s what your fiddle looks like on Firefox on Win7

        • Remi Grumeau

          ah ah :) Agree it misses -moz & -o prefixed transform. i’m sure you can manage to make a copy/paste man…

          • http://twitter.com/zomigi Zoe M. Gillenwater

            It’s not just the transform—you also need to add left and top values to position the checkboxes and radio buttons in the hole left by the label’s left padding. Here’s a fixed version:
            http://jsfiddle.net/tZ6ga/10/

  • Glenn

    IE 9+ does accept the pseudo selector :checked (double check at http://kimblim.dk/css-tests/selectors/). After investigating your mark-up by placing the comment before your DOCTYPE declaration you force IE 9 into quirks mode.

    Simply moving your HTML comment under the DOCTYPE will put it into standards mode.

    • http://www.facebook.com/pavan.dongray Pavan Dongray

      yup it works in IE9 as well as in IE10………. TY so much

      • http://www.facebook.com/pavan.dongray Pavan Dongray

        screenshot of IE10

        • http://www.facebook.com/jiri.crispeyn Jiri Crispeyn

          try adding

          above , will fix this bug in IE

  • Pingback: Some links for light reading (17/10/12) | Max Design

  • http://thecssninja.com/ Ryan

    Nice one, as for iOS Safari support adding an onclick attribute to the label triggers the checkbox to check[1].

    [1] http://www.thecssninja.com/css/custom-inputs-using-css#onclickios

    • http://front-back.com/ jkneb

      +1 you rock

    • http://www.facebook.com/IamRohitAzad Rohit Azad

      nice i like it +1

  • komiska

    Great work! Thanks for this!

    Is there a way actually to style the dropdown arrow of the element?

    There are some crazy workarounds for Firefox, but from what i’ve seen in the fora , the bug has not yet been fixed.

    What is your experience with the customizing of ?

    • komiska

      Sorry, forgot to take out the code brackets: i was talking about the “select” element and the dropdown arrow for it!

  • http://www.alfystudio.com Ahmad Alfy

    Well then I think the attribute selector input[type="checkbox"] { display:none; } should be re declared in IE specific style sheet to display it.

  • http://www.joezimjs.com Joe Zim

    Instead of using “display:none” on the check boxes, just use negative margins on the :before element and cover the old checkboxes and radios up. That way, if the :before elements don’t work, the normal checkboxes show up just fine. Tested in FF, Chrome, IE7+ and works great in all. IE7 and IE8 fall back perfectly.

    http://jsfiddle.net/h6JFG/3/

  • Guest

    The fallback in IE doesn’t work for me … I coded it like you, but it doesn’t want to work.
    I don’t know, but if you write display:none, how it should appear on IE. Need some IE targeting with display:inline-block, or not ?
    Thanks.

  • http://front-back.com/ jkneb

    In fact there’s a better approach for the ie8 problem talked a while ago by @ryanseddon and @leaverou the idear is to prepare usual styles for inputs and label in ie8 and under. And when you’re done simply put all the present technique styles into a :not(#foo) css3 selector. ie8 doesn’t understand this selector so nothing is going to be applied to it and it’s just perfect. Wufoo forms actually uses the :not(#foo) workaround. Here’s the links I found : http://www.thecssninja.com/css/custom-inputs-using-css and http://lea.verou.me/2010/02/cssninjas-custom-forms-revisited-to-work-with-css-sprites/

  • Fabsn

    It’s buggy when you click repeatedly in FF16, selecting the text instead of toggling the checkbox. Also keyboard events are not supported.

    • http://front-back.com/ jkneb

      lol you want a monkey proof technique? you could use a user-select: none to prevent users selecting the text AND you could set it only if repeatedly click event is triggered. It would work.

  • Pingback: Tip: Cambiar estilos de Checkbox y Radio Button con CSS3

  • Pingback: Best of Tuts+ in October 2012 | DigitalMofo

  • Pingback: Best of Tuts+ in October 2012 | SearchPsd Blog

  • Pingback: Best of Tuts+ in October 2012 | Mediasapiens.tv Design Hunters

  • http://twitter.com/chovy chovy

    buggy in firefox. click the checkbox multiple times

  • Pingback: Best of Tuts+ in October 2012 | Создание сайтов во Владивостоке

  • Pingback: Best of Tuts+ in October 2012 | GMancer

  • dukio

    Works on iOS 6.0.1 from iPhone 4S

  • Pingback: Best of Tuts+ in October 2012 - Website Design Prices

  • Pingback: Best of Tuts+ in October 2012 - itcenter-bg.com | itcenter-bg.com

  • gowtham
  • Pingback: Reference Links | Sathya's Log

  • altaphista

    Very nice styles, thanks helped me a lot.

    One thing, if you want it to work with validations (f.e. https://github.com/posabsolute/jQuery-Validation-Engine ), you can’t have it set on display:none; so use this for the checkbox/radio button:

    input[type="checkbox"] {
    float: left;
    width: 0px;
    opacity: 0;
    height: 0px;
    }

    • James

      new to this, but I believe `height: 0px;` also invalidates because of its detriment to accessibility.

  • http://ouroboros-group.org/ Сергей Николаев

    Thank’s

  • http://www.facebook.com/julian.kingman Julian Kingman

    That’s so clever! I didn’t even know you could click a label. Love it, and also that it degrades well.

  • Dyutiman

    Nice tricks. Thanks for sharing.
    But, how do I manage this when I have a check box without a label. Use case will be like putting a check box in a column of a table.

  • http://www.facebook.com/casachit Sachit Tandukar

    Okay author is totally mistaken here. Pseudo selector works in IE9. I have attached image as proof. Please correct this article. :D

    • http://twitter.com/valkan_spb Валканыч

      Your image doesn’t show any checked item.

  • http://www.facebook.com/JoanMHernandezH Joan Manuel Hernández

    Very nice, it’s a shame mobile doesn’t support it…

  • Pingback: Quick Tip: Easy CSS3 Checkboxes and Radio Buttons | Webdesigntuts+ « the blog

  • thenoclist

    I’m not getting the graceful degradation. Using IE < 9 I'm just seeing my background image for the unchecked state instead of the normal checkbox. How is this supposed to degrade if we're setting display: none to the checkbox itself?

  • Pingback: 튜토리얼관련 | design labs

  • MikeMarcacci

    Hmmm… this is a good start, but it can get even better: instead of using elements, we can use the ::before pseudo element. Also, why use images when you can style the buttons with CSS? Add FontAwesome to the mix and this is pure gold! :-)

  • Senad Licina

    hey there,

    just informing you that I have made a version with disabled checkboxes.
    https://github.com/Senci/oauth-infrz/blob/master/Resources/img/checkboxes.png

    and the code for that… (I think you should be able to figure it out. ;) )


    input[type="checkbox"][disabled] + label span {
    display:inline-block;
    width:19px;
    height:19px;
    margin:-1px 4px 0 0;
    vertical-align:middle;
    background:url('/img/checkboxes.png') -38px top no-repeat;
    }

    input[type="checkbox"][disabled]:checked + label span {
    background:url('/img/checkboxes.png') -57px top no-repeat;
    }

    label[disabled] {
    cursor:default;
    }

    input[disabled] {
    cursor:default;
    }

  • Spanner

    Thats great but it wont work in ie6

    • http://www.facebook.com/rob.vanseventer Rob van Seventer

      i asume this is a joke. ie what?

  • Pick Back

    I have been searching this for a long time. Finally, found it. Thanks you :)

  • Beepop

    This comment is a fake….

  • Beepop

    Do you have a problem admin?

  • Pingback: Displaying JavaScript Modal Windows using Bootstrap

  • Pingback: Displaying JavaScript Modal Windows using Bootstrap | iPixel Creative | Singapore Web Design & CMS Development Company Blog

  • Mosh

    Thanks a lot!! So easy

  • http://www.facebook.com/profile.php?id=624934146 Mina Magdy

    Could you please update it with version has js .. to work with IE8 ? thank you

  • Alex

    thanks a lot guys, deposited this in my memory bank. Love this kind of stuff from tuts+!

  • Pingback: Tıklanmaları Javascript yerine CSS ile işlemlemek

  • Pingback: CSS Styled Checkbox | BlogoSfera

  • boufroy

    thanks a lot! this saved me a load of time by not having to rewrite some in built radio button behaviour in javascript

  • http://www.facebook.com/jiri.crispeyn Jiri Crispeyn

    Thanks a lot, very easy to follow and really helped me out. Really have to thank you for this, wrote it on my own but ended up having bugs in FF, Opera and IE but this fixed it all!