Styles for custom checkbox in CSS

- 👤 Andrés Cruz

🇪🇸 En español

Styles for custom checkbox in CSS

Customizing checkbox styles with CSS is one of those tasks that seem simple... until you want something truly modern, animated, and reusable. In this article, I will show how to create an animated toggle switch checkbox using only CSS, without JavaScript, starting from a real and fully functional <input type="checkbox">.

The goal is not just to change colors, but to understand the complete pattern so that you can then adapt it to any design, even with SVG or advanced HTML.

Of course, we also used CSS transitions for smooth changes in this experiment.

What are custom checkboxes and why use pure CSS

By default, HTML checkboxes come with an appearance imposed by the browser. This severely limits the design and makes each browser look different. Therefore, when we talk about custom checkboxes, we mean ignoring the native visual appearance and creating our own with CSS.

When I started designing more elaborate interfaces, I realized that relying on the default checkbox was unviable. With pure CSS you can:

  • Control colors, sizes, and animations.
  • Maintain the native behavior of the input.
  • Avoid unnecessary JavaScript dependencies.

Native checkbox vs custom checkbox: key differences

A native checkbox:

  • Is accessible and functional.
  • But can hardly be styled.

A custom checkbox:

  • Keeps the real input.
  • Hides its rendering.
  • Uses CSS to visually represent its state.

The key is not to "replace" the checkbox, but to use it as a state controller.

In this post, we will see how to create custom checkboxes using only CSS, meaning that the default rendering performed by browsers is not used:

And in its place will be a self-made design 100% customizable with any number of HTML elements and CSS rules.

The result we want to obtain is the following:

 
 
 

You might consider the design above to be simple, but you can adapt it to your needs and even use images or SVG/HTML as we will see in future posts; today we just want to show the general idea of how to create custom checkboxes; let's see.

Custom or not, the checkbox tag is used

Perhaps the title seems like a contradiction to what was mentioned initially; that is, although the default rendering performed for any HTML element that makes up a web page (in this case, the checkbox) is not used, checkboxes are used but they are "hidden" or not visible using the opacity property; with the following HTML:

    <div class="custom-checkbox">
        <input type="checkbox" />
        <div class="custom-checkbox-switch-0"></div>
        <div class="custom-checkbox-switch-1"></div>  
        <div class="custom-checkbox-ball"></div>  
    </div>

The structure is simple; a custom-checkbox-switch-1 div for when it is on, another custom-checkbox-switch-0 div for when it is off, and the custom-checkbox-ball switch.

Explanation of each element in the container

  • input checkbox: the actual state.
  • switch-0: background of the off state.
  • switch-1: background of the on state.
  • ball: the movable switch.

Hiding the checkbox without losing interactivity

We are interested in using the states of the checkbox so that we can easily alter the behavior of our CSS when they are active or not through the :checked selector and its negation, which we can obtain with :not(:checked).

You must keep in mind that even if the checkboxes are hidden with opacity=0, you can interact with them through the UI offered by the browser

See the explanation given previously in this practical example:

 
 
 

With opacity=0.

 
 
 

With opacity=1.

Then, by varying which div custom-checkbox-switch-0 and custom-checkbox-switch-1 are shown according to the checkbox state, the checkbox states are easily emulated; see the CSS corresponding to each of the divs that represent the checkbox state:

  [class*=custom-checkbox-switch] {
                position: relative;
                width: 100%;
                height:  100%;
                border-radius:40px;
                margin:0;
                transition:1s all;
            }
            .custom-checkbox input ~ .custom-checkbox-switch-0 {
                border:4px solid rgb(0,0,0);
                background: rgba(0,0,0,.3);
                top:-50px;
            }
            .custom-checkbox input ~ .custom-checkbox-switch-1 {
                border:4px solid rgb(0,255,0);
                background: rgba(0,255,0,.3);
                top:-108px;
            }
            .custom-checkbox input:checked ~ .custom-checkbox-switch-0 {
                opacity:0;
            }
            .custom-checkbox input:not(:checked) ~ .custom-checkbox-switch-1 {
                opacity:0;
            }

And the CSS for the switch:

            .custom-checkbox input ~ .custom-checkbox-ball {
                position: relative;
                background:#CCC;
                border-radius:25px;
                top:-162px;
                width: 50px;
                height: 50px;
                transform: translateX(54px);
                transition:1s all;
                -webkit-transform: translateX(54px);
                -webkit-transition:1s all;
                -moz-transform: translateX(54px);
                -moz-transition:1s all;
            }
            .custom-checkbox input:not(:checked) ~ .custom-checkbox-ball {
                background:rgb(0,0,0);
            }
            .custom-checkbox input:checked ~ .custom-checkbox-ball {
                background:rgb(0,255,0);
            }

I explain the CSS styles for the toggle switch:

First we define the common styles:

[class*=custom-checkbox-switch] {
   position: relative;
   width: 100%;
   height: 100%;
   border-radius: 40px;
   transition: 1s all;
}

Off-state styles:

.custom-checkbox input ~ .custom-checkbox-switch-0 {
   border: 4px solid rgb(0,0,0);
   background: rgba(0,0,0,.3);
}

On-state styles

.custom-checkbox input ~ .custom-checkbox-switch-1 {
   border: 4px solid rgb(0,255,0);
   background: rgba(0,255,0,.3);
}

Switch animation with transform and transition:

.custom-checkbox input ~ .custom-checkbox-switch-1 {
   border: 4px solid rgb(0,255,0);
   background: rgba(0,255,0,.3);
}

Controlling states with :checked and :not(:checked):

.custom-checkbox input ~ .custom-checkbox-ball {
   position: relative;
   width: 50px;
   height: 50px;
   border-radius: 25px;
   background: #ccc;
   transform: translateX(54px);
   transition: 1s all;
}

Sibling selector ~ applied to checkbox

This selector allows the input state to control other elements. It is one of the most powerful resources for creating custom checkboxes.

Showing and hiding elements according to the state

  • There are no JS conditionals.
  • Everything is declarative.
  • The CSS remains clean and predictable.

Customizing and extending the checkbox toggle

Once the pattern is understood, you can:

  • Change sizes and colors.
  • Adjust the animation speed.
  • Replace the switch with SVG.
  • Use icons or text inside the toggle.

In real projects, I usually start from this base and then adapt it according to the client's design.

Common mistakes when styling checkboxes with CSS

  • Hiding the input with display: none.
  • Not using a label when there is text.
  • Forcing unnecessary JavaScript.
  • Not thinking about focus and accessibility.

Avoiding these mistakes makes the difference between a demo and a reusable component.

Conclusion

Creating styles for checkboxes with CSS is not only possible, but recommended when you seek total control over the design. By using a real checkbox, hiding it correctly, and leveraging :checked, you can build modern, accessible, and JavaScript-free animated toggles.

This pattern is simple, scalable, and a solid foundation for any design system.

Frequently Asked Questions about CSS Checkbox Styles

  • Can a checkbox toggle be created with CSS only?
    • Yes. Using :checked, transform, and transition, you can create fully functional toggles.
  • Is appearance: none better or opacity: 0?
    • For complex toggles, opacity: 0 is usually more flexible and reliable.
  • Does this affect accessibility?
    • No, as long as the input remains present and correctly associated.

You can find the complete code in the following links:

The next experiment on the list is the hover effect on images using CSS.

I agree to receive announcements of interest about this Blog.

In this entry we will see how to create a custom checkbox using only CSS, meaning that the default rendering done by browsers is not used.

| 👤 Andrés Cruz

🇪🇸 En español