Building Custom Checkbox and Radio Designs
- CSS Basics
- Selectors & Specificity
- Box Model & Units
- Typography & Text Styling
- Colors & Color Spaces
- Positioning & Stacking Context
- Flexbox
- Grid
- Grid Advanced
- Responsive Design
- Container Queries
- Pseudo-classes & Pseudo-elements
- Modern Selectors
- CSS Custom Properties
- CSS Functions
- Transitions & Animations
- Transforms
- Forms & Input Styling
- CSS Architecture & Methodologies
- Accessibility in CSS
- Performance & Optimization
- View Transitions & Modern APIs
- Cascade Layers & Scope
When accent-color Is Not Enough
For a fully custom shape or icon - not just a recolored native checkbox - the common technique is to visually hide the real input while keeping it functional and accessible, then build a fake control from other elements that reacts to its state.
The HTML Structure
<label class="custom-checkbox">
<input type="checkbox">
<span class="control"></span>
Subscribe to updates
</label>Hiding the Real Input Correctly
.custom-checkbox input {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
}The real checkbox must stay in the document and remain focusable and clickable by assistive technology - using display: none would remove it from the accessibility tree entirely, breaking screen reader support. This pattern keeps it technically present but visually invisible.
Styling the Visible Fake Control
.control {
width: 20px;
height: 20px;
border: 2px solid #999;
border-radius: 4px;
display: inline-block;
}
.custom-checkbox input:checked + .control {
background: #1d4ed8;
border-color: #1d4ed8;
}The :checked state of the real (hidden) input, combined with the adjacent sibling combinator, drives the visual appearance of the fake control right next to it - no JavaScript required.
Never Skip the Focus State
.custom-checkbox input:focus-visible + .control {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}Because the real input is visually hidden, its focus state must be manually forwarded onto the visible fake control - forgetting this step is the most common accessibility bug in custom form control implementations.