Customizing Checkboxes and Radios Inputs Using CSS

Have you ever wanted to make prettier checkboxes or radio inputs? Are you annoyed that each browser displays these elements differently? This post will explain one method for creating customized, accessible input fields that are cross-browser friendly.

There aren’t really any options for styling the actual inputs themselves, but we’ll work around that using the ::before and ::after pseudo elements to create the illusion of beautifully customized checkmarks or radio inputs.


Here is our final result:

What are your favorite sports?

Check the answers that apply:

What is your favorite sport?

Pick one:

Setting it up

First we’ll need some basic inputs to work with. The HTML below will give us very basic radio and checkbox inputs with sibling labels, each wrapped in a div.

Note: The markup may vary depending on your application, so you would need to modify as needed. For example, if you’re doing this with Gravity Forms you would need to target the list elements output by each field.

<div class="checkbox-wrapper">
     <input type="checkbox" name="baseball" id="question-1-option-1">
     <label class="checkbox-label" for="question-1-option-1">Baseball</label>
</div>

This basic HTML is repeated for each checkbox input as well as each radio input with minor changes to switch the input to a radio. Here is the full markup for the example above:

<form action="">

     <div class="question-wrapper">
          <p class="question">What are your favorite sports?</p>
          <div class="question-label">Check the answers that apply:</div>
          <div class="checkbox-wrapper">
               <input type="checkbox" name="baseball" id="question-1-option-1">
               <label class="checkbox-label" for="question-1-option-1">Baseball</label>
          </div>
          <div class="checkbox-wrapper">
               <input type="checkbox" name="basketball" id="question-1-option-2">
               <label class="checkbox-label" for="question-1-option-2">Basketball</label>
          </div>
          <div class="checkbox-wrapper">
               <input type="checkbox" name="football" id="question-1-option-3">
               <label class="checkbox-label" for="question-1-option-3">Football</label>
          </div>
          <div class="checkbox-wrapper">
               <input type="checkbox" name="hockey" id="question-1-option-4">
               <label class="checkbox-label" for="question-1-option-4">Hockey</label>
          </div>
          <div class="checkbox-wrapper">
               <input type="checkbox" name="soccer" id="question-1-option-5">
               <label class="checkbox-label" for="question-1-option-5">Soccer</label>
          </div>
     </div>

     <div class="question-wrapper" style="margin-top: 30px;">
          <p class="question">What is your favorite sport?.</p>
          <div class="question-label">Pick one:</div>
          <div class="radio-wrapper">
               <input type="radio" name="question-2" id="question-2-option-1">
               <label class="radio-label" for="question-2-option-1">Baseball</label>
          </div>
          <div class="radio-wrapper">
               <input type="radio" name="question-2" id="question-2-option-2">
               <label class="radio-label" for="question-2-option-2">Basketball</label>
          </div>
          <div class="radio-wrapper">
               <input type="radio" name="question-2" id="question-2-option-3">
               <label class="radio-label" for="question-2-option-3">Football</label>
          </div>
          <div class="radio-wrapper">
               <input type="radio" name="question-2" id="question-2-option-4">
               <label class="radio-label" for="question-2-option-4">Hockey</label>
          </div>
          <div class="radio-wrapper">
               <input type="radio" name="question-2" id="question-2-option-4">
               <label class="radio-label" for="question-2-option-4">Soccer</label>
          </div>
     </div>

</form>

Breaking down the CSS

Next, let’s dive into the CSS that makes this all work.

First, we need to give each checkbox/radio wrapper a position of relative since we will be absolutely positioning our input and label elements.

.question-wrapper .checkbox-wrapper,
.question-wrapper .radio-wrapper {
     position: relative;
     display: block;
     margin-bottom: 12px;
}

Next we will hide the inputs visually since we will be using pseduo elements to give a visual representation of the checkbox/radio input.

.question-wrapper .checkbox-wrapper input[type="checkbox"],
.question-wrapper .radio-wrapper input[type="checkbox"] {
     position: absolute;
     left: 0;
     top: 0;
     opacity: 0;
     height: 0;
     width: 0;
}

Now we’ll style the label. This is where the most important element comes in to play; the pseudo ::before & ::after elements.

.question-wrapper .checkbox-wrapper label,
.question-wrapper .radio-wrapper label {
     padding-left: 34px;
     position: relative;
     z-index: 100;
     cursor: pointer;
     color: #b5267b;
     font-size: 16px;
     line-height: 24px;
     display: block;
     color: black;
}

.question-wrapper .checkbox-wrapper label::before,
.question-wrapper .radio-wrapper label::before {
     display: inline-block;
     font-style: normal;
     font-variant: normal;
     text-rendering: auto;
     -webkit-font-smoothing: antialiased;
     content: "\f00c";
     font-weight: 600;
     font-family: "Font Awesome 5 Pro";
     left: 4px;
     position: absolute;
     color: white;
     z-index: 10;
     top: 1px;
     background: white;
     -webkit-background-clip: text;
     -webkit-text-fill-color: transparent;
     opacity: 0;
}

.question-wrapper .checkbox-wrapper label::after,
.question-wrapper .radio-wrapper label::after {
     content: " ";
     width: 24px;
     height: 24px;
     border-radius: 2px;
     background-color: transparent;
     border: 2px solid #b5267b;
     position: absolute;
     left: 0;
     top: 0;
}

.question-wrapper .radio-wrapper input[type="radio"] {
     position: absolute;
     left: 0;
     top: 2px;
     opacity: 0;
     height: 0;
     width: 0;
     cursor: pointer;
}

.question-wrapper .radio-wrapper label::before {
     content: "\f111";
     font-size: 13px;
     top: 1px;
     left: 6px;
}

.question-wrapper .radio-wrapper label::after {
     border-radius: 24px;
}

We have added 34px of padding on the left of the label to allow space for the checkbox and radio element to show to the left of the label.

Using the ::after pseudo element we add the checkbox/radio container absolutely positioned to the left of the label.

Using the ::before pseudo element we add a checkmark (for checkboxes) or a bullet (for radio inputs) using Font Awesome (see this code snippet) and positioned it on top of the checkbox/radio container element. The z-index ensures this sits on top of the container and the opacity is initially set to 0 so it remains hidden until checked.

Let’s Check it Off

Now that we have all of the elements in place, we set up the CSS to show the checkmark (or radio) when the elements are checked.

.question-wrapper .checkbox-wrapper input[type="checkbox"]:checked + label::before,
.question-wrapper .radio-wrapper input[type="radio"]:checked + label::before {
     opacity: 1;
}

.question-wrapper .checkbox-wrapper input[type="checkbox"]:checked + label::after,
.question-wrapper .radio-wrapper input[type="radio"]:checked + label::after {
     background-color: #b5267b;
}

We need to adjust the “checked” state of both the ::before and ::after pseudo elements.

In the first portion of the CSS above we alter the opacity of the ::before pseudo element when its sibling label is in a :checked state.

In the second portion of the CSS above we change the background color of the ::after pseudo element when its sibling label is in a :checked state.

Notice we are using the + sign in the CSS to target the sibling element (the label that is a sibling to the input being checked).

Wrapping it up

And there it is. We have a fully customized, cross-browser, accessible checkbox and radio input. You can easily use some CSS to modify these to your own needs. Here is the full CSS for the example above:

p.question {
     font-size: 20px;
     font-weight: 600;
     margin-bottom: 10px;
}
     
.question-label {
     display: block;
     font-weight: 600;
     margin-bottom: 10px;
}
     
.question-wrapper .checkbox-wrapper,
.question-wrapper .radio-wrapper {
     position: relative;
     display: block;
     margin-bottom: 12px;
}

.question-wrapper .checkbox-wrapper label,
.question-wrapper .radio-wrapper label {
     padding-left: 34px;
     position: relative;
     z-index: 100;
     cursor: pointer;
     color: #b5267b;
     font-size: 16px;
     line-height: 24px;
     display: block;
     color: black;
}

.question-wrapper .checkbox-wrapper input[type="checkbox"],
.question-wrapper .radio-wrapper input[type="checkbox"] {
     position: absolute;
     left: 0;
     top: 0;
     opacity: 0;
     height: 0;
     width: 0;
}

.question-wrapper .checkbox-wrapper label::before,
.question-wrapper .radio-wrapper label::before {
     display: inline-block;
     font-style: normal;
     font-variant: normal;
     text-rendering: auto;
     -webkit-font-smoothing: antialiased;
     content: "\f00c";
     font-weight: 600;
     font-family: "Font Awesome 5 Pro";
     left: 4px;
     position: absolute;
     color: white;
     z-index: 10;
     top: 1px;
     background: white;
     -webkit-background-clip: text;
     -webkit-text-fill-color: transparent;
     opacity: 0;
}

.question-wrapper .checkbox-wrapper label::after,
.question-wrapper .radio-wrapper label::after {
     content: " ";
     width: 24px;
     height: 24px;
     border-radius: 2px;
     background-color: transparent;
     border: 2px solid #b5267b;
     position: absolute;
     left: 0;
     top: 0;
}

.question-wrapper .checkbox-wrapper input[type="checkbox"]:checked + label::before,
.question-wrapper .radio-wrapper input[type="radio"]:checked + label::before {
     opacity: 1;
}

.question-wrapper .checkbox-wrapper input[type="checkbox"]:checked + label::after,
.question-wrapper .radio-wrapper input[type="radio"]:checked + label::after {
     background-color: #b5267b;
}


.question-wrapper .radio-wrapper input[type="radio"] {
     position: absolute;
     left: 0;
     top: 2px;
     opacity: 0;
     height: 0;
     width: 0;
     cursor: pointer;
}

.question-wrapper .radio-wrapper label::before {
     content: "\f111";
     font-size: 16px;
     top: 1px;
     left: 4px;
}

.question-wrapper .radio-wrapper label::after {
     border-radius: 24px;
}

About Matt Whiteley

Matt has been building bespoke WordPress websites for over 10 years specializing in the Genesis Framework. He integrates Advanced Custom Fields heavily in all builds, especially with the new Gutenberg Block Editor, allowing clients to easily modify all aspects of the their website with ease.

He works with a wide variety of small businesses and agencies across the country providing development services as well as hosting and maintenance. When he isn't coding he enjoys spending time with his wife and two children, golfing and playing poker.

Reader Interactions

Leave a Reply

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