I am trying to style a checkbox using the following:
The original question and answer are now ~5 years old. As such, this is a little bit of an update.
Firstly, there are a number of approaches when it comes to styling checkboxes. the basic tenet is:
You will need to hide the default checkbox control which is styled by your browser, and cannot be overridden in any meaningful way using CSS.
With the control hidden, you will still need to be able to detect and toggle its checked state
The checked state of the checkbox will need to be reflected by styling a new element
The above can be accomplished by a number of means - and you will often hear using CSS3 pseudo-elements is the right way. Actually, there is no real right or wrong way, it depends on the approach most suitable for the context you will be using it in. That said, I have a preferred one.
Wrap your checkbox in a label
element. This will mean that even when it is hidden, you can still toggle its checked state on clicking anywhere within the label.
Hide your checkbox
Add a new element after the checkbox which you will style accordingly. It must appear after the checkbox so it can be selected using CSS and styled dependent on the :checked
state. CSS cannot select 'backwards'.
label input {
visibility: hidden;/* <-- Hide the default checkbox. The rest is to hide and allow tabbing, which display:none prevents */
display: block;
height: 0;
width: 0;
position: absolute;
overflow: hidden;
}
label span {/* <-- Style the artificial checkbox */
height: 10px;
width: 10px;
border: 1px solid grey;
display: inline-block;
}
[type=checkbox]:checked + span {/* <-- Style its checked state */
background: black;
}
<label>
<input type='checkbox'>
<span></span>
Checkbox label text
</label>
But hey! I hear you shout. What about if I want to show a nice little tick or cross in the box? And I don't want to use background images!
Well, this is where CSS3's pseudo-elements can come into play. These support the content
property which allows you to inject Unicode icons representing either state. Alternatively, you could use a third party font icon source such as font awesome (though make sure you also set the relevant font-family
, e.g. to FontAwesome
)
label input {
display: none; /* Hide the default checkbox */
}
/* Style the artificial checkbox */
label span {
height: 10px;
width: 10px;
border: 1px solid grey;
display: inline-block;
position: relative;
}
/* Style its checked state...with a ticked icon */
[type=checkbox]:checked + span:before {
content: '\2714';
position: absolute;
top: -5px;
left: 0;
}
<label>
<input type='checkbox'>
<span></span>
Checkbox label text
</label>
You can simply use appearance: none
on modern browsers, so that there is no default styling and all your styles are applied properly:
input[type=checkbox] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
display: inline-block;
width: 2em;
height: 2em;
border: 1px solid gray;
outline: none;
vertical-align: middle;
}
input[type=checkbox]:checked {
background-color: blue;
}
Yikes! All these workarounds have led me to the conclusion that the HTML checkbox kind of sucks if you want to style it.
As a forewarning, this isn't a CSS implementation. I just thought I'd share the workaround I came up with in case anyone else might find it useful.
I used the HTML5 canvas
element.
The upside to this is that you don't have to use external images and can probably save some bandwidth.
The downside is that if a browser for some reason can't render it correctly, then there's no fallback. Though whether this remains an issue in 2017 is debatable.
I found the old code quite ugly, so I decided to give it a rewrite.
Object.prototype.create = function(args){
var retobj = Object.create(this);
retobj.constructor(args || null);
return retobj;
}
var Checkbox = Object.seal({
width: 0,
height: 0,
state: 0,
document: null,
parent: null,
canvas: null,
ctx: null,
/*
* args:
* name default desc.
*
* width 15 width
* height 15 height
* document window.document explicit document reference
* target this.document.body target element to insert checkbox into
*/
constructor: function(args){
if(args === null)
args = {};
this.width = args.width || 15;
this.height = args.height || 15;
this.document = args.document || window.document;
this.parent = args.target || this.document.body;
this.canvas = this.document.createElement("canvas");
this.ctx = this.canvas.getContext('2d');
this.canvas.width = this.width;
this.canvas.height = this.height;
this.canvas.addEventListener("click", this.ev_click(this), false);
this.parent.appendChild(this.canvas);
this.draw();
},
ev_click: function(self){
return function(unused){
self.state = !self.state;
self.draw();
}
},
draw_rect: function(color, offset){
this.ctx.fillStyle = color;
this.ctx.fillRect(offset, offset,
this.width - offset * 2, this.height - offset * 2);
},
draw: function(){
this.draw_rect("#CCCCCC", 0);
this.draw_rect("#FFFFFF", 1);
if(this.is_checked())
this.draw_rect("#000000", 2);
},
is_checked: function(){
return !!this.state;
}
});
Here's a working demo.
The new version uses prototypes and differential inheritance to create an efficient system for creating checkboxes. To create a checkbox:
var my_checkbox = Checkbox.create();
This will immediately add the checkbox to the DOM and hook up the events. To query whether a checkbox is checked:
my_checkbox.is_checked(); // True if checked, else false
Also important to note is that I got rid of the loop.
Something I neglected to mention in the last update is that using the canvas has more advantages than just making a checkbox that looks however you want it to look. You could also create multi-state checkboxes, if you wanted to.
Object.prototype.create = function(args){
var retobj = Object.create(this);
retobj.constructor(args || null);
return retobj;
}
Object.prototype.extend = function(newobj){
var oldobj = Object.create(this);
for(prop in newobj)
oldobj[prop] = newobj[prop];
return Object.seal(oldobj);
}
var Checkbox = Object.seal({
width: 0,
height: 0,
state: 0,
document: null,
parent: null,
canvas: null,
ctx: null,
/*
* args:
* name default desc.
*
* width 15 width
* height 15 height
* document window.document explicit document reference
* target this.document.body target element to insert checkbox into
*/
constructor: function(args){
if(args === null)
args = {};
this.width = args.width || 15;
this.height = args.height || 15;
this.document = args.document || window.document;
this.parent = args.target || this.document.body;
this.canvas = this.document.createElement("canvas");
this.ctx = this.canvas.getContext('2d');
this.canvas.width = this.width;
this.canvas.height = this.height;
this.canvas.addEventListener("click", this.ev_click(this), false);
this.parent.appendChild(this.canvas);
this.draw();
},
ev_click: function(self){
return function(unused){
self.state = !self.state;
self.draw();
}
},
draw_rect: function(color, offsetx, offsety){
this.ctx.fillStyle = color;
this.ctx.fillRect(offsetx, offsety,
this.width - offsetx * 2, this.height - offsety * 2);
},
draw: function(){
this.draw_rect("#CCCCCC", 0, 0);
this.draw_rect("#FFFFFF", 1, 1);
this.draw_state();
},
draw_state: function(){
if(this.is_checked())
this.draw_rect("#000000", 2, 2);
},
is_checked: function(){
return this.state == 1;
}
});
var Checkbox3 = Checkbox.extend({
ev_click: function(self){
return function(unused){
self.state = (self.state + 1) % 3;
self.draw();
}
},
draw_state: function(){
if(this.is_checked())
this.draw_rect("#000000", 2, 2);
if(this.is_partial())
this.draw_rect("#000000", 2, (this.height - 2) / 2);
},
is_partial: function(){
return this.state == 2;
}
});
I modified slightly the Checkbox
used in the last snippet so that it is more generic, making it possible to "extend" it with a checkbox that has 3 states. Here's a demo. As you can see, it already has more functionality than the built-in checkbox.
Something to consider when you're choosing between JavaScript and CSS.
Working Demo
First, set up a canvas
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
checked = 0; // The state of the checkbox
canvas.width = canvas.height = 15; // Set the width and height of the canvas
document.body.appendChild(canvas);
document.body.appendChild(document.createTextNode(' Togglable Option'));
Next, devise a way to have the canvas update itself.
(function loop(){
// Draws a border
ctx.fillStyle = '#ccc';
ctx.fillRect(0,0,15,15);
ctx.fillStyle = '#fff';
ctx.fillRect(1, 1, 13, 13);
// Fills in canvas if checked
if(checked){
ctx.fillStyle = '#000';
ctx.fillRect(2, 2, 11, 11);
}
setTimeout(loop, 1000/10); // Refresh 10 times per second
})();
The last part is to make it interactive. Luckily, it's pretty simple:
canvas.onclick = function(){
checked = !checked;
}
This is where you might have problems in IE, due to their weird event handling model in JavaScript.
I hope this helps someone; it definitely suited my needs.
input[type=checkbox].css-checkbox {
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
}
input[type=checkbox].css-checkbox + label.css-label {
padding-left: 20px;
height: 15px;
display: inline-block;
line-height: 15px;
background-repeat: no-repeat;
background-position: 0 0;
font-size: 15px;
vertical-align: middle;
cursor: pointer;
}
input[type=checkbox].css-checkbox:checked + label.css-label {
background-position: 0 -15px;
}
.css-label{
background-image:url(http://csscheckbox.com/checkboxes/dark-check-green.png);
}
input[type="checkbox"] {
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: 0;
background: lightgray;
height: 16px;
width: 16px;
border: 1px solid white;
}
input[type="checkbox"]:checked {
background: #2aa1c0;
}
input[type="checkbox"]:hover {
filter: brightness(90%);
}
input[type="checkbox"]:disabled {
background: #e6e6e6;
opacity: 0.6;
pointer-events: none;
}
input[type="checkbox"]:after {
content: '';
position: relative;
left: 40%;
top: 20%;
width: 15%;
height: 40%;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
display: none;
}
input[type="checkbox"]:checked:after {
display: block;
}
input[type="checkbox"]:disabled:after {
border-color: #7b7b7b;
}
<input type="checkbox"><br>
<input type="checkbox" checked><br>
<input type="checkbox" disabled><br>
<input type="checkbox" disabled checked><br>
Here is a simple CSS solution without any jQuery or JavaScript code.
I am using FontAwseome icons but you can use any image
input[type=checkbox] {
display: inline-block;
font-family: FontAwesome;
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
visibility: hidden;
font-size: 14px;
}
input[type=checkbox]:before {
content: @fa-var-square-o;
visibility: visible;
/*font-size: 12px;*/
}
input[type=checkbox]:checked:before {
content: @fa-var-check-square-o;
}