DEV Community

Milos Protic
Milos Protic

Posted on

CSS Checkbox Styling HTML Checkboxes Is Super Easy

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

Top comments (17)

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

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

Collapse
 
tim_dreipass_b998402cb44a profile image
Tim Dreipass

Thanks for this.
I have 2 problems with this:

  1. it doesn't work on iOS, there are shwon 2 checkboxes, the one by brwoser udn the styled new one. What to do?

  2. 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

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

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 • Edited

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
 
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

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

Collapse
 
aycanogut profile image
bleedeleventh

That was very helpful, many thanks!

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 • Edited

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

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
 
afterdiv profile image
afterdiv

This works really well ...!

Mange Takk! :-)

Collapse
 
proticm profile image
Milos Protic

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

Collapse
 
drodsou profile image
drodsou

Not what I would call "super easy" and "really that simple". But thanks for the info.