Originally posted on my-blog
The Old Story
Back in the days, HTML components like checkbox
were pretty tricky to style the way we wanted to. Often these components stud out of the overall page design which wasn't so good for the user experience.
Not to mention that each browser had (and still has) its own appearance for the control thus contributing to the difference in the look and feel across various browsers.
To overcome these situations developers used to do a lot of hacking like hiding the input, creating images and icons for the checkmark and adding a bunch of JavaScript code to handle the checking/unchecking. If you ask me, this is not that pretty and it seems like a lot of work to achieve something simple.
The New Story
The times described above are long gone and we are closer than ever to the universal way of styling the checkboxes in a manner when they will look and feel the same across all browsers, especially with the news that the Microsoft is building a chromium-based browser. You can read about it here.
The new story, from my perspective, is that we can style the checkbox without hiding it and without adding SVG images and JavaScript code. This can be done by using the following instead:
- The CSS
appearance
property - The HTML
check mark
symbol (✓)
Appearance Property
The appearance property is used to display an element using a platform-native styling based on the users operating system's theme.
This property supports many values, but the one interesting for us is the value none
. Basically, we want to remove all the native styling and apply the custom one. In the end, our checkbox will have nice looking colors and transitions and the most important thing, it will look and feel the same in all major browsers.
Example usage:
.my-class {
-webkit-appearance: value;
-moz-appearance: value;
/* -o-appearance: value; - Not required since the new version of Opera uses -
webkit prefix for this property, but we've added it nevertheless just to be aware
of it
*/
appearance: value;
}
Ok, let's dive into the code.
HTML
Our HTML markup is pretty straight-forward. We have a label
wrapping our input
and a span
to hold the text within. It looks like this:
<label class="checkbox">
<input type="checkbox" />
<span>Check Me</span>
</label>
Nothing too fancy here. We used a wrapper element to make it easier to align the inner items vertically. This is done with the flexbox layout which we will see in the CSS section.
CSS
The CSS styling looks like this:
.checkbox {
display: inline-flex;
cursor: pointer;
position: relative;
}
.checkbox > span {
color: #34495E;
padding: 0.5rem 0.25rem;
}
.checkbox > input {
height: 25px;
width: 25px;
-webkit-appearance: none;
-moz-appearance: none;
-o-appearance: none;
appearance: none;
border: 1px solid #34495E;
border-radius: 4px;
outline: none;
transition-duration: 0.3s;
background-color: #41B883;
cursor: pointer;
}
.checkbox > input:checked {
border: 1px solid #41B883;
background-color: #34495E;
}
.checkbox > input:checked + span::before {
content: '\2713';
display: block;
text-align: center;
color: #41B883;
position: absolute;
left: 0.7rem;
top: 0.2rem;
}
.checkbox > input:active {
border: 2px solid #34495E;
}
If you think this is still a lot of CSS let me remind you that we do not need the flexbox layout or the transitions in order to implement this styling. This is added to make it more elegant. If we remove the extra CSS all we need to do is to remove the default styling by setting the appearance
to none
, add borders and coloring and set the HTML symbol.
Let us break down the important parts to back up the statement above. The first step is to use the appearance
property and remove the default styling:
...
-webkit-appearance: none;
-moz-appearance: none;
-o-appearance: none;
appearance: none;
...
Hopefully, this property will become a standard soon and we would be able to use it without the browser specific prefixes.
Next, we need to provide our custom borders and backgrounds:
...
border: 1px solid #34495E;
border-radius: 4px;
outline: none;
background-color: #41B883;
cursor: pointer;
...
And last, we will use the ::before
pseudo-class to style the HTML symbol. The CSS below will display the HTML symbol nicely colored and positioned after we check the input field.
...
content: '\2713';
display: block;
text-align: center;
color: #41B883;
position: absolute;
left: 0.7rem;
top: 0.2rem;
...
And that is it! It is really that simple. No more JavaScript overkill to achieve these styles in order to match the checkbox design with the rest of the page. We can safely achieve it with the CSS provided here.
Here is a live fiddle to play around with the code:
Further Reading
If you're interested in CSS variables, check out this or this post published on my blog.
See the official docs of the appearance property
Oldest comments (17)
Can we access these with a keyboard? I couldn't seem to focus on it unless I was missing something.
I did something different markup wise!
Create custom keyboard accessible checkboxes
Lindsey Kopacz ・ Nov 27 '18 ・ 5 min read
I haven't tried it, but don't see a reason why not. Maybe we should try with the tabindex...
This looks very weird on my Firefox... I get some styled checkbox under your styled checkbox...
Which version do you have? It is fine on my end.
67.0B16 (developer edition), It's real weird
Hm...mine is 66.0.5 (64-bit). Maybe something has changed in the newer version...Let's wait for the official version and see if the issue persists
Hello! This works great, except the checkmark unicode is not showing up no matter what I do. Can you help?
Hi, what browser are you using? Does the fiddle work for you, or the checkmark is not showing there as well?
FYI the problem here is that the checkmark is
position: absolute
. Works in your JSFiddle because the box itself is in the top left corner of the window. Becomes a problem if your button is elsewhere on the page.But thank you so much for throwing this together!!! Super useful.
Thanks, glad you find it useful :)
I've just tried to change the position and and added
margin-top: 150px; margin-left: 150px
to the.checkbox
class and it worked fine. I tried it in chrome.Did you move it in any other way?
The
position:absolute
should not be a problem because we have relative positioning on the parent.Very cool! I'm loving this
webkit-appearance
stuff. I can't believe I only just found out about it.Thanks! Glad you find it useful :) CSS is evolving, that's for sure
This works really well ...!
Mange Takk! :-)
No problem, glad I could help! Sorry for the late reply :)
That was very helpful, many thanks!
Thanks for this.
I have 2 problems with this:
it doesn't work on iOS, there are shwon 2 checkboxes, the one by brwoser udn the styled new one. What to do?
Layoutproblems with longer Text beside checkbox. The text then flows around the checkbox, which is ugly and I#ld like to have textblock (like in lists).
No icons or unnecessary DOM CSS-only fully stylable checkbox CSS-only fully stylable checkbox CSS-only fully stylable checkbox CSS-only fully stylable checkbox CSS-only fully stylable checkbox CSS-only fully stylable checkbox CSS-only fully stylable checkbox CSS-only fully stylable checkbox
it flows around the checkbox which is ugly. If I set
input[type="checkbox"] + * {display: inline-flex;}
It's a nice textblock, completly (every line) on the right hand of the checkbox. But then (only) the unchecked checkbox gets much to small. If checked it gets in its correct size. Didn't understand this.
Oh, and I must mention, that for reasons of the CMS-Plugin, I do nat have a or other tag available to directly style it. Its just sitting plain inside the -tag.
I can't figure out, how to style the uncheked box properly. Any Ideas?
Thanks!
Best regards from Germany
Tim
Not what I would call "super easy" and "really that simple". But thanks for the info.