In CSS we have many ways to style things in any way we want. When it gets to forms though, things get a little complicated. Text inputs are easy, but checkboxes and radio buttons are very difficult to style with CSS. In this tutorial I’ll show you exactly how to style these inputs and make awesome forms with just CSS!
The Basics
Effectively, a really good idea for styling checkboxes the only way to style checkboxes, radio buttons and drop downs is with this little piece of CSS:
appearance: none;
This will take all browser styles off those inputs and we can start to alter them as regular elements. They’ll still have the same properties as before, just without the Mac or Windows layout. Unfortunately support is once again, quite low. Webkit is the only browsers supporting this (Opera sort of supports it), which gives you about 40% of the market at the moment (according to Statcounter).
It’s okay though! We can make this stuff degrade quite nicely, so for users of Webkit things will look great, but Opera, IE and Firefox users will just get regular old forms.
What we can use
So what weapons can we use to create these effects? Well to create extra elements we can use :after
, :before
(so we don’t have to change the HTML that much). Then for radio buttons and checkboxes we can use :checked
. There are a few other pseudo elements we can use as well (such as :hover
). Using a mixture of these we can create some pretty cool custom forms.
Lets Begin
Recently there has been a bit of discussion on the web about :before
and :after
when using forms. The general consensus is that the fact that psuedo content works on forms in webkit is a bug because forms cannot hold content, and pseudo content comes after the real content in an element. Unfortunately, I am going to be using these quite a lot to get the effects we want. The best thing about this, of course, is users using unsupported browsers will just see basic form elements.
To get started, lets make a few checkboxes and radio buttons. I’m going to make two types of each, one being small and the other being large. These are just a bunch of inputs with id’s, names and classes, so nothing too fancy, just regular forms.
<div> <label>Checkbox Small</label> <input type="checkbox" id="checkbox-1-1" class="regular-checkbox" /> <nput type="checkbox" id="checkbox-1-2" class="regular-checkbox" /> <input type="checkbox" id="checkbox-1-3" class="regular-checkbox" /> <input type="checkbox" id="checkbox-1-4" class="regular-checkbox" /> </div> <div> <label>Checkbox Big</label> <input type="checkbox" id="checkbox-2-1" class="regular-checkbox big-checkbox" /> <input type="checkbox" id="checkbox-2-2" class="regular-checkbox big-checkbox" /> <input type="checkbox" id="checkbox-2-3" class="regular-checkbox big-checkbox" /> <input type="checkbox" id="checkbox-2-4" class="regular-checkbox big-checkbox" /> </div> <div> <label>Radio Small</label> <div class="button-holder"> <input type="radio" id="radio-1-1" name="radio-1-set" class="regular-radio" checked /> <input type="radio" id="radio-1-2" name="radio-1-set" class="regular-radio" /> <input type="radio" id="radio-1-3" name="radio-1-set" class="regular-radio" /> <input type="radio" id="radio-1-4" name="radio-1-set" class="regular-radio" /> </div> </div> <div> <label class="radio-1">Radio Big</label> <div class="button-holder"> <input type="radio" id="radio-2-1" name="radio-2-set" class="regular-radio big-radio" /> <input type="radio" id="radio-2-2" name="radio-2-set" class="regular-radio big-radio" /> <input type="radio" id="radio-2-3" name="radio-2-set" class="regular-radio big-radio" checked /> <input type="radio" id="radio-2-4" name="radio-2-set" class="regular-radio big-radio" /> <input type="radio" id="radio-2-5" name="radio-2-set" class="regular-radio big-radio" /> </div> </div>
The next step is some CSS. To begin we style the normal checkbox. We remove any default styles with the appearance property, and then just style as we see fit. I’ve added a few box shadows and borders.
.regular-checkbox { -webkit-appearance: none; background-color: #fafafa; border: 1px solid #cacece; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05); padding: 9px; border-radius: 3px; display: inline-block; position: relative; }
After that we set the styles for when the user clicks on the checkbox (:active
) and when the checkbox is checked.
.regular-checkbox:active, .regular-checkbox:checked:active { box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px 1px 3px rgba(0,0,0,0.1); } .regular-checkbox:checked { background-color: #e9ecee; border: 1px solid #adb8c0; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05), inset 15px 10px -12px rgba(255,255,255,0.1); color: #99a1a7; }
Then we just need to make an :after
pseudo element. This will contain the check mark (for when the box is checked) by using content
.
.regular-checkbox:checked:after { content: '\2714'; font-size: 14px; position: absolute; top: 0px; left: 3px; color: #99a1a7; }
And then it’s just a matter of some resizing for the big checkboxes
.big-checkbox { padding: 18px; } .big-checkbox:checked:after { font-size: 28px; left: 6px; }
The radio buttons follow a similar pattern, only this time we use rounded corners and an actual element rather than text for the highlighted radio button. I’m sure you can figure it out.
.regular-radio { -webkit-appearance: none; background-color: #fafafa; border: 1px solid #cacece; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05); padding: 9px; border-radius: 50px; display: inline-block; position: relative; } .regular-radio:checked:after { content: ' '; width: 12px; height: 12px; border-radius: 50px; position: absolute; top: 3px; background: #99a1a7; box-shadow: inset 0px 0px 10px rgba(0,0,0,0.3); text-shadow: 0px; left: 3px; font-size: 32px; } .regular-radio:checked { background-color: #e9ecee; color: #99a1a7; border: 1px solid #adb8c0; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05), inset 15px 10px -12px rgba(255,255,255,0.1), inset 0px 0px 10px rgba(0,0,0,0.1); } .regular-radio:active, .regular-radio:checked:active { box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px 1px 3px rgba(0,0,0,0.1); } .big-radio { padding: 16px; } .big-radio:checked:after { width: 24px; height: 24px; left: 4px; top: 4px; }
And we’re done! Not that difficult, eh? As I mentioned before, this will only work in webkit browsers but it gives those with the extra support some nice extra features. It will gracefully degrade in most browsers since we’re using padding, rather than defining the width and height which can lead to some odd styles in Firefox.
Internet Explorer assumes padding is around the input so in IE the buttons will have a border and padding surrounding the actual input, but it’s nothing life threatening. All things considered, the smaller buttons are perhaps more suitable if cross browser compatibility is the name of the game, merely because they are more aesthetically pleasing in Internet Explorer. Check out the demo in Chrome or Safari to see the final product.
Update! [Sept 11th 2012]
There has been a lot of talk about the cross browser compatibility of this page, and obviously there is very little to speak about. In the comments, Gunnar pointed out that using targeting a label or span after the form element would fix this problem, so I thought I’d show you guys how to do that. This requires a little more HTML.
We need to adjust the HTML so that there is a label after every input. Then we use the for tag to associate each label with their corresponding input. So our updated HTML looks like this:
<div> <div class="tag">Checkbox Small</div> <input type="checkbox" id="checkbox-1-1" class="regular-checkbox" /><label for="checkbox-1-1"></label> </div> <div> <div class="tag">Checkbox Big</div> <input type="checkbox" id="checkbox-2-1" class="regular-checkbox big-checkbox" /><label for="checkbox-2-1"></label> </div> <div> <div class="tag">Radio Small</div> <div class="button-holder"> <input type="radio" id="radio-1-1" name="radio-1-set" class="regular-radio" checked /><label for="radio-1-1"></label> </div> </div> <div> <div class="tag">Radio Big</div> <div class="button-holder"> <input type="radio" id="radio-2-1" name="radio-2-set" class="regular-radio big-radio" /><label for="radio-2-1"></label> </div> </div>
Next we alter the CSS slightly so that the CSS targets the label, not the actual input. We don’t have to use the experimental appearance tag either! So instead of writing what we wrote before, .regular-checkbox
, we’re going to write:
.regular-checkbox + label { }
We’re also going to set the checkboxes and radio buttons so they don’t display. The updated CSS looks like this:
label { display: inline; } .regular-checkbox { display: none; } .regular-checkbox + label { background-color: #fafafa; border: 1px solid #cacece; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05); padding: 9px; border-radius: 3px; display: inline-block; position: relative; } .regular-checkbox + label:active, .regular-checkbox:checked + label:active { box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px 1px 3px rgba(0,0,0,0.1); } .regular-checkbox:checked + label { background-color: #e9ecee; border: 1px solid #adb8c0; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05), inset 15px 10px -12px rgba(255,255,255,0.1); color: #99a1a7; } .regular-checkbox:checked + label:after { content: '\2714'; font-size: 14px; position: absolute; top: 0px; left: 3px; color: #99a1a7; } .big-checkbox + label { padding: 18px; } .big-checkbox:checked + label:after { font-size: 28px; left: 6px; } .tag { font-family: Arial, sans-serif; width: 200px; position: relative; top: 5px; font-weight: bold; text-transform: uppercase; display: block; float: left; } .radio-1 { width: 193px; } .button-holder { float: left; } /* RADIO */ .regular-radio { display: none; } .regular-radio + label { -webkit-appearance: none; background-color: #fafafa; border: 1px solid #cacece; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05); padding: 9px; border-radius: 50px; display: inline-block; position: relative; } .regular-radio:checked + label:after { content: ' '; width: 12px; height: 12px; border-radius: 50px; position: absolute; top: 3px; background: #99a1a7; box-shadow: inset 0px 0px 10px rgba(0,0,0,0.3); text-shadow: 0px; left: 3px; font-size: 32px; } .regular-radio:checked + label { background-color: #e9ecee; color: #99a1a7; border: 1px solid #adb8c0; box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05), inset 15px 10px -12px rgba(255,255,255,0.1), inset 0px 0px 10px rgba(0,0,0,0.1); } .regular-radio + label:active, .regular-radio:checked + label:active { box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px 1px 3px rgba(0,0,0,0.1); } .big-radio + label { padding: 16px; } .big-radio:checked + label:after { width: 24px; height: 24px; left: 4px; top: 4px; }
This version of the code works in the latest version of all browsers (IE, Firefox, Opera and Chrome), so you don’t have to worry about whether or not it will work. I’ve updated the download and demo links so that you can see the updated version of the code. If you’ve enjoyed this, please follow us on twitter!
Hey! If you enjoyed this check out our post on creating more custom checkboxes, where we create a bunch of cool, custom checkboxes for use on your next project.
Comments
Yeah ! Cool stuff !
Thank you.
You can also see some researches by a friend french blogger : http://www.screenfeed.fr/demos/personnalisation-checkbox-radio/
See ya !
Yes, I love this CSS feature, I just wish there was wider support! Customizing forms is something web designers have wanted to do with just CSS for a long time, but at least now you can sort of see how it would be implemented.
It’s nice CSS3…
Yeah it’s really nice :)
I love the future of CSS =D
Seems Internet Explorer 9 has a problem with
This solution:
http://www.screenfeed.fr/demos/personnalisation-checkbox-radio/
The checkboxes cant be rotated as the CSS states. Looks bad from a users point of view. Cant that be fixed?
Using a conditional stylesheet for IE9 could be a solution :p
I agree that these WebKit examples are cool, but it seems that we will not have a wider browser support in the future, at least on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=241985
It’s a pity.
Yeah it is a shame, but appearance is supported experimentally in firefox, so I hope soon we’ll see a full implementation closer to webkit in the near future.
Hi.This is awesome.How about http://modernizr.com/ I think this script can make your custom form element work in IE and Firefox too.I don’t know too much about javascript but if you had time look at modernizr.com and see what you can do.Best regards!
have you tried them with firefox12??
It’s not working
‘input’ elements are empty elements, i.e. they must not have content. No generated content either, thatβs why ‘content’ on ::after does not work.
However, if you have the ‘label’ after the ‘input’, you could use ‘:checked + label::before’ for styling.
You’re right. This techniques is through a sort of ‘loophole’ where webkit considers input elements to have content. I do believe that label method would work nicely.
what do you mean by wrapping the checkbox in a label
Nice, cool stuff! but, why we should do this tricks?
It’s nice to give prettier features to those who are using browsers which can support it. If you check out the rest of the comments, Gunnar suggests using a label around the inputs and styling the label, which would give wider browser support.
Doesn’t works on Firefox…
Hey! It doesn’t work in Firefox because Webkit treats inputs as elements with content. As mentioned elsewhere in the comments you could put a label around the input and style the label to make a faux radio button. Hope this helps!
Works on android. Thanks.
this is cool stuff man.. tweeting it and tomorrow ll share to my team
Can you update this code to work with the label around the input so it works in Firefox? I’m not quite getting what I need to do. What is the support for IE8?
Thanks!
This is a great snippet of code. Has anyone came up with a way to do something similar with drop-down select boxes?
Great tutorial. I got this to work on all majors browsers but when viewed on iPad, it does not seem to be working. You cannot check anything. Has anyone heard/encountered that as well?
Cheers
not working in ie 8
Not support IE 8….
wow, thats great, realy a goot way to style input elements.
i thats great , but there is a problem when moving with tab key to focus on those elements , do anyone have a solution for that?
cheers
uzi
I ran into the tab key access issue as well.
Check my solution here: https://gist.github.com/4627560
How do you get labels to the right of each radio button. Everytime I try to add a label it gets put into the radio button.
place the text portion of your code outside of the label tags and it works fine.
If we do that the form becomes unusable for users with screen readers. The natural hierarchy of the form is lost and the syntax becomes invalid.
This is great!
I’ve used this tutorial a few times now and it is the best for styling both buttons and check boxes.
Thanks! :)
test…
Right, no IE8 support.. is there anyway to get this working?
In IE8? No.
yes it is – javascript or update ;)
Hi also have a problem in IE8 is there any work around for this ? Also Chrome not styling properly, any help with this ?
Hi,
I used a couple of conditional IE hacks in my code, very useful to know these, the reason being I needed to degrade the buttons to work in IE and also on Android, the checkbox was not clickable for some reason.
Used this,
Your code here for IE browsers
Your code here if not IE
Also I found that this code did not work in Chrome
content: ‘\2714’; (this is the tick graphic)
I changed this to
content: ‘0BB’; (this is a double chevron)
Its not working in mozilla firefox..
I understand that the label for the radio button has to go outside of the label tags. However, when I do that, they don’t align properly (the text appears lower than the radio button). I’ve tried various methods of alignment, etc and just can’t seem to get it to work since I’m not using the label tags. Any ideas? Thank you!
This is why I hate IE. Even though this works in the new browsers, the fact that it’s not even compatible with 8 makes this completely useless.
Hi, How do I reduce the size of the “regular text box” even a little bit more?
It is not compatible with ios. To be compatible with ios add onclick=”” to label tag.
More information:
http://stackoverflow.com/questions/5421659/html-label-command-doesnt-work-in-iphone-browser
Nice, been looking for something like this for a while. Better than using javascript! Shame it doesn’t work in IE…the bane of a developer’s life!
Cant seem to get neither this demo nor a local version to work on either iOS5 (Chrome/Safari with webkit), nor Android 4…
Is this trick dead or am I missing something?
Got it to work in Android 4, my bad. But now I found out that MSIE 8 isnt working. Thats rather bad if thats true. At least a “fall back” to old style would be acceptable in that case. Btw. iOS5 needs a javascript fix to work.
Nice one! Really handy, easy to understand and implement! Thanks!
the “+label” solution is a problem, since having the text be part of the button makes a lot of sense in a touch-setting.
Thanks my dear friend
Thanks for this tutorial, I enjoyed playing around with it and got it working on a mobile site I’m building. But, I agree with kaare, the button label needs to be “clickable” too. Even with the larger buttons, it’s still more difficult than it should be to select them on a touchscreen.
Nice tutorial, thank you
For the radio buttons is there any way to get it to click on and off? I just have 1, it’s basically for an agreement. Do you think I’m better off using the checkbox instead? I just prefer the styling of the radio button.
Thanks!
Use checkboxes instead.
hi ,
very excellent customization.
But IE 8 not working.
Can you please any alternative solution…Please provide me…it is very urgent…
Thanks
Karthik
hi! when i used your code for checkbox, the label goes inside the checkbox, what i need is for the label to be on the right side of the checkbox. Can you help me on this?
It’s awesome..!!! :)
Thank you! this is awesome!
However, could you please come up the style for the ‘disabled’ checkbox?
I’ve tried:
.regular-checkbox input[type=checkbox]:disabled + label:after {…}
.big-checkbox > input[type=checkbox]:disabled + label:after {…}
both not working,
Please help!
Thanks for this awesome tut. For people to know, this is not supported in early versions of IE and Firefox…
Hi. Not working in IE8?
I suggest adding “cursor: pointer;” to .regular-radio + label definition.
Its really cool.
Bro, you rocks. After a long time I get this article. Its very helpfull.
Thank you, this is a nice tutorial :=)
Hi,
How do you use it to take a value from a mysql database and then submit the value?
Eg I currently use:
if ($smoke == 1)
{$checksmoke = checked;}
else {$checksmoke = unchecked;}
echo ” Smoke Detector?”;
echo ” “;
to tick the box as read from db, then use the new value.
can’t figure how to amend your code?
Great article, I love the way these checkboxes look and the size.
Thanks,
Dallin
Hi Johnny,
Thanks for your shareing.
I am confused at how to get the value of checkbox, since it apply those css style. I found the val() is unchanged whether it is checked or not. would you gives out a solution using javascript?
thanks
Mingjun
does anyone have an issue with adding the “Required” property?
after debugging i’m getting this error “An invalid form control with name=’title’ is not focusable.”
please help.
Very handy post. It helped me a lot! Thank you!
“This version of the code works in the latest version of all browsers (IE, Firefox, Opera and Chrome), so you donβt have to worry about whether or not it will work.”
And not tested on Safari? Not working on Iphone!
It is behaving abnormally on Android 4.3(Galaxy S4). The check action gets repeated twice; if you try to check, it gets checked and unchecked.
Can you suggest me a fix?
Thank you for this tutorial and dowload. U really saved me.