How to build custom checkbox with HTML and CSS without JavaScript #1
On one of my first interviews I’ve had interesting task on layouts:
Using only HTML and CSS build a custom checkbox, so its’ original image is not displayed but it still had functionality (checked — unchecked). Instead of original checkbox image insert custom image or text that shows current checkbox state and giving proper functionality.
My subconscious told me something about pseudoelements but even googling didn’t help me to do a task in 5 minutes, after which interviewer ended call saying “Wait for our feedback”. But I didn’t get upset. Instead, I improved my skill, so here it is: 2 ways to create custom checkbox.
First way
It is suitable for layout where input
connected with label
trough an id and for properties.
<input class="custom-checkbox"
id="myCheckbox"
type="checkbox" />
<label for="myCheckbox">Checkbox</label>
Order of the elements is important because CSS selectors depend on it.
Hiding <input/>
.custom-checkbox {
position: absolute;
z-index: -1;
opacity: 0;
}
Important thing about using opacity: 0
instead of display: none
is that with opacity we can get focus state of the input element for styling.
Creating fake checkbox
.custom-checkbox + label {
cursor: pointer;
display: inline-flex;
align-items: center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.custom-checkbox + label::before {
content: '';
display: inline-block;
width: 1em;
height: 1em;
flex-shrink: 0;
flex-grow: 0;
border: 1px solid #c3c3c3;
border-radius: 0.25em;
margin-right: 0.5em;
background-repeat: no-repeat;
background-position: center center;
background-size: 50% 50%;
}
Firstly, we verticaly aligning flag with align-items: center
for flex containers.
With pseudoelement ::before
we create imitation of a checkbox. For visibility paint its’ borders. Properties background-repeat
, -position
and -size
define position on flag in checked state.
Pseudoelement styles in :checked
state
.custom-checkbox:checked + label::before {
border-color: blue;
background-color: blue;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
}
Styles for :hover
, :active
, :focus
and :disabled
states
.custom-checkbox:not(:disabled):not(:checked) + label:hover::before {
border-color: rgba(0, 0, 255, 0.33);
}
.custom-checkbox:not(:disabled):active + label::before {
background-color: rgba(0, 0, 255, 0.66);
}
.custom-checkbox:focus + label::before {
box-shadow: 0 0 0 0.2rem rgba(0, 0, 255, 0.125);
}
.custom-checkbox:focus:not(:checked) + label::before {
border-color: #c3c3c3;
}
.custom-checkbox:disabled + label::before {
background-color: black;
}
Second way
Second way is suitable for a layout where input
in placed inside label
:
<label class="custom-checkbox">
<input type="checkbox">
</label>
With this layout we should add span
element right after input
:
<label class="container">Checkbox
<input type="checkbox" checked="checked">
<span class="checkmark"></span>
</label>
Let’s create a simple checkbox without accounting for different states and with a fixed image position.
label
(container) styles:
.container {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
Hiding default input
:
.container input {
position: absolute;
z-index: -1;
opacity: 0;
}
Drawing custom checkbox inside span
element:
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
background-color: #eee;
}
Styling span
in :hover
and :checked
states:
.container:hover input ~ .checkmark {
background-color: #ccc;
}
.container input:checked ~ .checkmark {
background-color: #2196F3;
}
Adding pseudoelement for checkmark:
.checkmark:after {
content: "";
position: absolute;
display: none;
}
.container input:checked ~ .checkmark:after {
display: block;
}
.container .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
Examples
input
outside label
, type="checkbox"
input
outside label
, type="radio"
input
inside label
, type="checkbox"
input
inside label
, type="radio"
input
inside label
, type="checkbox"
, different style
input
inside label
, type="radio"
, different style