This seems like an obvious statement; styling should be done with CSS. But unfortunately in the case of checkboxes, there's basically no support for directly styling the checkbox.
Because of that, I've seen several JavaScript implementations to change between displaying an unchecked box and a checked box based on state in order to fake a styled checkbox.
But let's not go so quickly to JS to solve a fundamentally styling problem! It can be done with just CSS, we just need to shift our thinking a little.
The goals
- Style the checkbox
- Style the label
- Require no additional DOM nodes or configuration (including icons/images)
- Require 0 JavaScript
The results
The strategy
Since we cannot style the checkbox input directly with something like checkbox-style: awesome;, we'll need to take a different approach:
- Hide the actual checkbox input
- Show a styled element that looks like an empty checkbox when the input is unchecked
- Show a styled element that looks like a checked checkbox when the input is checked
How to get there
The CSS selectors used
Type selector
type- selects all elements of the giventype(e.g.inputwill select all<input ... />nodes)Attribute selector
[attribute="value"]- selects an element withattributewhere its value is exactlyvaluePsuedo-class
:checked- selects checkbox/radio input types oroptions in aselectthat are selected/checked/on/activePsuedo-element
::before- styleable element that doesn't actually exist in the DOM; considered the first child of the selected elementUniversal selector
*- selects anything/everythingChild combinator
>- combines two selectors; narrowing the selection on the right-hand side to only those elements that are direct descendants of elements selected by the left-hand side.Adjacent sibling combinator
+- combines two selectors; narrowing the selection on the right-hand side to only those elements that are the siblings immediately after the elements on the left-hand side
Important CSS styles used
content- used in the::beforepsuedo-element to set its contentdisplay- specificallynoneto hide elements andinline-blockto make our otherwise inline checkbox able to have a consistent width and heightwidth/height- does what you think: sets width and height of the elementcolor- sets the color of the texttext-align/vertical-align- used for adjusting the position of our check/checkbox to its labelborderstyles - How we'll form and color the checkboxbackground- sets the background color (used to fill in the checkbox when it is checked)
Starting out: the HTML
Let's set up our checkbox as a child of its label element with a sibling of a span of the label text:
<label>
<input type="checkbox" name="key" value="value" />
<span>I am a checkbox</span>
</label>
This structure allows clicking on the label text (I am a checkbox) to toggle the checkbox without needing for or unique id attributes. Placing the text in a span directly after the input will allow us to select it in CSS.
First step: hide the unstyleable checkbox
Going back to our strategy: since we can't do anything with the native checkbox, we'll have to hide it and do our own thing.
label > input[type="checkbox"