How to style a checkbox using CSS?
Browsers typically render checkboxes using their native UI, which makes direct styling of <input type="checkbox"> very limited. The most common approach is to hide the original checkbox (visually) and replace it with a styled element (like a <span>) that reflects the checked state using CSS or pseudo-elements. Alternatively, modern browsers support the accent-color property for some simple color customization, though it’s not fully consistent or as flexible as a custom replacement approach.
Below are the key methods:
1. Simple Color Customization with accent-color (Modern Browsers)
Some up-to-date browsers (Chrome, Edge, Firefox 92+, etc.) allow changing the checkbox’s color via accent-color:
input[type="checkbox"] {
accent-color: teal;
}
- Pros: Minimal code, easy to maintain.
- Cons: Not all browsers support it (older Safari, older versions of Firefox). Also limited to color changes; you can’t replace the shape or style details.
2. Full Custom Style (Hide Native Checkbox + Replacement)
A widely used pattern is:
- Hide the real checkbox (visually, but keep it accessible for screen readers and form submission).
- Use a label or a pseudo-element to show a styled “box.”
- Synchronize the box’s checked state with the real checkbox using the
:checkedpseudo-class.
Example HTML
<label class="custom-checkbox">
<input type="checkbox" />
<span class="checkmark"></span>
Remember Me
</label>
Recommended Courses
Example CSS
.custom-checkbox {
display: inline-flex;
align-items: center;
cursor: pointer;
user-select: none; /* avoid text highlight on click */
}
/* Hide the native checkbox visually, but keep it accessible */
.custom-checkbox input[type="checkbox"] {
position: absolute;
opacity: 0;
pointer-events: none;
}
/* The "box" that replaces the checkbox UI */
.checkmark {
width: 20px;
height: 20px;
background-color: #eee;
border: 2px solid #ccc;
border-radius: 3px;
display: inline-block;
margin-right: 8px;
position: relative;
transition: background-color 0.2s;
}
/* When the checkbox is checked, style .checkmark accordingly */
.custom-checkbox input[type="checkbox"]:checked + .checkmark {
background-color: #2196F3;
border-color: #2196F3;
}
/* Optional: Add a checkmark icon using a pseudo-element */
.custom-checkbox input[type="checkbox"]:checked + .checkmark::after {
content: "";
position: absolute;
left: 6px;
top: 2px;
width: 6px;
height: 12px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
- Workflow:
- The real
<input>is there for accessibility and form submission, but it’s transparent (opacity: 0) and non-interactive (pointer-events: none). - The
<span class="checkmark">is the visually styled box. When the user clicks, it actually clicks on the label, which toggles the hidden checkbox. - Using the sibling selector (
+ .checkmark) after:checked, we style the box differently if the checkbox is toggled on.
- The real
3. Using -webkit-appearance: none; (Older Trick / WebKit Browsers)
Some older guides mention -webkit-appearance: none; or setting appearance: none; to remove the default checkbox style in Safari/Chrome. Then you can style the element’s box:
input[type="checkbox"] {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #eee;
border: 2px solid #ccc;
}
input[type="checkbox"]:checked {
background: #2196F3;
}
- Pros: It modifies the actual
<input>without hiding it. - Cons: Inconsistent support across browsers, some still show partial default styling or lack full control over shapes. Typically you have to handle focus/active/hover states carefully.
4. Accessibility Considerations
- Ensure the checkbox is focusable and keyboard accessible.
- Always use a
<label>(oraria-label) so screen readers and users know the checkbox’s purpose. - Hiding the native checkbox visually is fine, but don’t remove it from the accessibility tree.
opacity: 0;,clip: rect(0,0,0,0);, orheight: 0; width: 0;is okay, as long as it remains in the document flow.
5. Summary
- Accent-Color: Easiest for minor color changes, if well-supported by your target browsers.
- Hide & Replace: The most common approach for complete customization; hide the native checkbox and use a styled element triggered by
:checked. -webkit-appearance: none;: An older approach that can partially style the real checkbox in certain browsers, but not all.- Accessibility: Always keep the
<input>in the DOM, never remove it, and ensure it’s labeled.
Key Takeaway
For a fully custom checkbox in pure CSS, your best bet is to overlay a styled box and tie its state to the real checkbox’s :checked pseudo-class. This is the most cross-browser method while preserving form functionality and accessibility.