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
:checked
pseudo-class.
Example HTML
<label class="custom-checkbox"> <input type="checkbox" /> <span class="checkmark"></span> Remember Me </label>
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.