How to Create Custom Checkboxes with CSS

By Andrew Gehman custom checkboxes

Styling checkboxes with CSS can be frustrating. If you’ve ever tried changing the border or adding a background color to a simple checkbox input element, you know the feeling. While it isn’t quite as straightforward as styling most other elements, you can still create some pretty sweet looking checkboxes with a little work.

The way we will go about this involves hiding the html checkbox element, replacing it with a visual version of a ‘checkbox’ that we create with HTML/CSS, and doing so without compromising accessibility.

There are a few keys to make all this magic to happen:

  1. The :checked pseudo class selector. Sadly and somewhat unsurprisingly, the :checked pseudo class selector is not supported by IE8 and earlier. So, if you still need to support any of those versions of that godforsaken mess of a browser, these methods won’t work for you.
  2. The for attribute of the label element. A label with a for attribute value will ‘control’ a form input element with the same id. So, clicking on a label will also effect it’s matching input element.
    <input type="checkbox" id="my-checkbox"> <label for="my-checkbox">My Label</label>
  3. The adjacent sibling ( + ) combinator. Using the adjacent sibling combinator ( + ) allows you to select the element that directly follows an element.
  4. :before and :after pseudo elements

Here’s the HTML we’ll use for our example. We’ve got a basic form element with a checkbox input and a label.

<h3>Check Marks</h3>

 <div class="checkbox-example">

 <input type="checkbox" value="None" id="fancy-checkbox" class="fancy-checkbox" name="fancy-checkbox" />

 <label class="fancy-checkbox-label" for="fancy-checkbox">Form Label</label> 



We want to be sure we are creating our custom checkboxes while still maintaining accessibility. We will be hiding the input checkbox element, but we don’t want to hide the checkbox input by using display: none, or by using visibility:hidden. Doing so would have the desired visual effect of hiding the checkbox on the screen, however, it will also have the undesired effect hiding it from screen readers.

For this example, I’ll use the WordPress core version of hiding content. This method gives us the desired result of hiding the content (in this case, a checkbox element) while keeping it accessible to screen readers. WordPress has a built in ‘screen-reader-text’ class, but you can name it anything you’d like.

.fancy-checkbox {
    clip: rect(1px, 1px, 1px, 1px);
    position: absolute !important;
    height: 1px;
    width: 1px;
    overflow: hidden;

Next, we want to add some style to the label element. We want to give the label a position of relative because we are going to use absolute positioning on the :before and :after pseudo elements  that we will use to create our new custom checkbox. I’ve also added display: block and cursor: pointer. You’ll also note the width of 150px. This is really just to keep my example nice and tidy.

label.fancy-checkbox-label {
  display: block;
  cursor: pointer;
  position: relative;
  width: 150px;
  font-size: 18px;
  line-height: 1.5em;

Creating the Custom Checkbox

Now we’re ready to create our checkbox. We’re going to use the :before pseudo element of our label to define an exact height and width in pixels and give it a border. We’ll want the content to be empty (open/close quotes) and use position: absolute to position it exactly where we want it. For the example below, I’ve used a top value of 0px and right value of 30px.

label.fancy-checkbox-label:before {
    position: absolute;
    top: 0px;
    right: 30px;
    display: block;
    width: 20px;
    height: 20px;
    border: 2px solid #808080;
    background: #FFF;
   content: " ";

You may recall I said earlier in the post that the for attribute would be one of the keys to making this all work. We are using the :before pseudo element of the label to create a checkbox with CSS. When we check that label (which is styled to look like a checkbox), we are setting the checked state of our input. So, even though we’ve hidden the actual checkbox element, we can still utilize the :checked state.

Creating our Custom Checkmark

The next step will be to create the check mark using the :after pseudo element. This where we start to get really fancy. What we are essentially doing is creating a rectangle with a width of 29px and height of 8px, and giving a border to just the bottom and left sides of the rectangle. When we use transform to rotate the ‘rectangle’ 45 degrees, it has the appearance of a checkbox.

label.fancy-checkbox-label:after {
    position: absolute;
    top: 0px;
    right: 18px;
    width: 29px;
    height: 8px;
    content: " ";
    transform: rotate(-45deg);
    border-bottom: 2px solid #808080;
   border-left: 2px solid #808080;
   opacity: 0;


We also want to set the opacity to 0 so that the check doesn’t show by default, only when checked. This is where the magic of the :checked pseudo class selector comes into play along with the adjacent sibling combinator (+). What the CSS below indicates is that when the checkbox is checked, we want to target the :after pseudo element of the adjacent label element with the class of .fancy-checkbox-label.

input:checked + label.fancy-checkbox-label:after {

So now, if you click on the label text or the checkbox (which is the before pseudo element of the label), you should see your checkmark and the input’s state will be set to checked.

See the Pen Custom CSS Checkbox with Checkmark by Andrew Gehman (@AndrewGehman) on CodePen.dark

You don’t have to use a checkmark. You could use an “X”, or a block, or even and image. The Codepen example below shows a few more custom examples. You’ll find that I did some of the styles below a little differently than in the example above. Either way is fine. You can use you own creativity and ideas to adjust and tweak the CSS to create your own custom checkboxes.

See the Pen Custom Checkboxes Examples by Andrew Gehman (@AndrewGehman) on CodePen.dark

View Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Are You Ready To Do More with Mind?

Click the link below to get started!

Work With Mind