loading...

CSS Checkbox Styling HTML Checkboxes Is Super Easy

proticm profile image Milos Protic ・4 min read

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:

  1. The CSS appearance property
  2. 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;
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;

...
Enter fullscreen mode Exit fullscreen mode

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;

...
Enter fullscreen mode Exit fullscreen mode

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;

...
Enter fullscreen mode Exit fullscreen mode

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

Discussion

pic
Editor guide
Collapse
lkopacz profile image
Lindsey Kopacz

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!

Collapse
proticm profile image
Milos Protic Author

I haven't tried it, but don't see a reason why not. Maybe we should try with the tabindex...

Collapse
bennadel profile image
Ben Nadel

Very cool! I'm loving this webkit-appearance stuff. I can't believe I only just found out about it.

Collapse
proticm profile image
Milos Protic Author

Thanks! Glad you find it useful :) CSS is evolving, that's for sure

Collapse
epse profile image
Stef Pletinck

This looks very weird on my Firefox... I get some styled checkbox under your styled checkbox...

Collapse
proticm profile image
Milos Protic Author

Which version do you have? It is fine on my end.

Collapse
epse profile image
Stef Pletinck

67.0B16 (developer edition), It's real weird

Thread Thread
proticm profile image
Milos Protic Author

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

Collapse
reflectingwalls profile image
reflectingwalls

Hello! This works great, except the checkmark unicode is not showing up no matter what I do. Can you help?

Collapse
proticm profile image
Milos Protic Author

Hi, what browser are you using? Does the fiddle work for you, or the checkmark is not showing there as well?

Collapse
stephanietuerk profile image
Stephanie Tuerk

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.

Thread Thread
proticm profile image
Milos Protic Author

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.

Becomes a problem if your button is elsewhere on the page.

Did you move it in any other way?

The position:absolute should not be a problem because we have relative positioning on the parent.

Collapse
deluxeyum profile image
TT

This works really well ...!

Mange Takk! :-)

Collapse
proticm profile image
Milos Protic Author

No problem, glad I could help! Sorry for the late reply :)