Styling Form Checkboxes

One of the many challenges of web design is creating easy to use, pretty to look at forms. Yeah, I know—forms are boring and difficult to style consistently. But they don't have to be. Let's take on one of the more difficult to style elements: a checkbox. Oh—and we're going to do this without javascript. Scared? Don't be.

You may be asking yourself what the point of this all is. If that's the case, don't even worry about it; the default checkbox isn't horrible, no matter what browser you're using. If you've ached to have creative control over elements like this, read on.

I also want to note something important before we start. While I am a designer at heart, I am also quite a bit of a UI/UX fanatic. In my opinion, if you break standard functionality in order to achieve a visual presentation, you've sacrificed too much. That's why I like this technique so much: it still allows for all standard functionality, including clicking on the label text to check the box.

First thing you should know is that this is going to be a variation on a common checkbox hack; we're going to use the label as a block element. We're also going to make use of the :before pseudo element.

Step one is to create our markup. in this case, it is quite simple: just a checkbox and its corresponding label. Please note that I'm omitting all attributes that don't relate to the visual presentation. You typically will need the "value" and "name" attributes for any checkbox you use in a form.

<div class="wrapper">
  <input type="checkbox" id="our_pretty_checkbox" />
  <label for="our_pretty_checkbox">Here is our Label Text</label>

See the Pen 2bb72de17bd9b9ac1663797f4d1c19fa by Jesse Couch (@designcouch) on CodePen.

This is typical for any normal checkbox, so we're not treading any new ground here. The fanciness will be in our CSS. Ready? Good.

Step two is writing the basic styles for the checkbox. We want to do a few things: first, we want to remove the default styling for all browsers. We're not going to bother trying to style the actual checkbox; instead, we're just going to hide it and style the label instead. It's important to note that we can't hide it using "display:none;" —this would make it non-functional. Instead, we need to crank the opacity back to 0. We're also going to set it's position to absolute to make sure that it doesn't affect the layout of the label.

#our_pretty_checkbox {
  position: absolute;
  opacity: 0;
  -moz-opacity: 0;
  -webkit-opacity: 0;
  -o-opacity: 0;

See the Pen 7727b8b1282b81433af6e7a483d461ae by Jesse Couch (@designcouch) on CodePen.

Ok, so far so good. Now we're going to move on to the step three: the label. We're going to do a few atypical things here, so pay close attention.

First off, we're going to give the label relative positioning. This allows us to position the :before pseudo element within the label using absolute positioning. We're also going to plan ahead and give it some padding on the left to make sure that there is space for the checkbox we're going to create using the :before pseudo element. Ready for me to stop talking and give you the CSS? Ok.

#our_pretty_checkbox + label {
  position: relative;
  padding: 0 0 0 32px;
  font-size: 16px;
  line-height: 16px;

See the Pen 1542af216049cd3dbaf35d0d801880f7 by Jesse Couch (@designcouch) on CodePen.

On to step four. As I said before, we're going to use the :before pseudo element to create our checkbox. This is pretty simple stuff but here's the gist: We're making it a block element using display:block and giving it a height and width equal to the line-height of the label. This will make sure everything lines up visually between the checkbox and the label text. We're also absolutely positioning it within the label and moving it to the far left. There's nothing in the content—yet. That's our next step.

#our_pretty_checkbox + label:before {
  content: "";
  display: block;
  position: absolute;
  height: 16px;
  width: 16px;
  text-align: center;
  font-size: 14px;
  line-height: 16px;
  background: #9ee5ff;

See the Pen f0e99db5db1ac3c3cd0c334f90caa30b by Jesse Couch (@designcouch) on CodePen.

And finally, we're on to step five. We're going to add a checkmark to the box when it is selected using the :checked attribute and a unicode character.

#our_pretty_checkbox:checked + label:before {
  content: "\2713";

See the Pen 044a880dd0b08fc5585a3fe1d7be9a38 by Jesse Couch (@designcouch) on CodePen.

Short and sweet.

I hope this has been a help to folks who may have struggled in the past with styling checkboxes—any questions, as always feel free to comment below.


See the Pen 2bb72de17bd9b9ac1663797f4d1c19fa by Jesse Couch (@designcouch) on CodePen.

Did I help you out?

Read More

←  Styling Form Radio Buttons Moving from Print to the Web  →

There are 4 comments. Add Yours.

A Note from the Moderator

Thanks for taking the time to comment! I'll respond as quickly as possible if necessary. In the meantime, please keep the following in mind:

  • All comments must be appropriate. I'll delete 'em if you get nasty.
  • Please allow for response time. I'm here as much as possible, but can't always respond as quickly as some would like.
  • Please stay on topic.
  • I don't work for free—if you want custom work, feel free to get in touch and I'll write up a quote!
  1. Patras Talib

    Patras Talib

    Jan 17, 2015

    It was awesome dude it helps me in many ways thanks you.

    Reply to Patras

    1. Jesse


      Jul 16, 2015

      Glad you liked it, Patras.

  2. Me


    Mar 20, 2018

    Is there a good reason to attach the before pseudo-element to the label instead of to the radio itself? Doing so would be compatible with the pattern, which is just as common as the pattern.

    Reply to Me

    1. Jesse


      Apr 12, 2018

      Thanks for the question, Me. Yes - there is a reason. Radios and Checkboxes are only minimally style able, and are not block elements in the true sense. They cannot have any HTML contents. Therefore, pseudo-elements cannot be contained within them. Using the label does two things. First, a label is a block level element and contain the pseudo-element as intended. Second, a label when properly linked to its input will act exactly like said input when clicked.