Rafael Camargo

Customizing checkboxes and radio buttons without hacks

For the longest time, I thought it was impossible to style native checkboxes and radio buttons without pulling off some kind of creative stunt — like what the big component libraries do, such as Material UI, Ant Design and VuetifyJS. But guess what? It's totally possible.

Checkboxes and Radio Buttons can be customized easily without hacks.

Here are the three steps to give a native checkbox a slick, consistent look across the major browsers (Chrome, Safari, Edge, and Firefox).

Remove the default appearance

First thing you gotta do is strip away the browser's default appearance:

input[type=checkbox] {
  appearance: none;
}

Add your custom style

Now style the input however you want. Just don't forget to handle the unchecked, checked, focus, and disabled states. Here's an example:

input[type=checkbox] {
  margin: 5px 6px 5px 0;
  width: 22px;
  height: 22px;
  background-color: #fff;
  border-width: 1px;
  border-style: solid;
  border-color: #C6C6C6;
  border-radius: 6px;
  box-sizing: border-box;
  box-shadow: 0 0 0 #CDE6FE;
  appearance: none;
  outline: 0;
  transition-property: box-shadow, border-color;
  transition-duration: 200ms;
  transition-timing-function: ease-in-out;
}

input[type=checkbox]:focus,
input[type=checkbox]:active {
  box-shadow: 0 0 0 4px #CDE6FE;
  border-color: #3A80EC;
}

input[type=checkbox]:checked {
  background-color: #6699FF;
  background-size: 12px 12px;
  background-position: center center;
  background-repeat: no-repeat;
}

input[type=checkbox]:checked:not(:disabled) {
  border-color: #6699FF;
}

input[type=checkbox]:disabled {
  background-color: #C6C6C6;
}

Add a checkmark

Finally, create an SVG icon for the checkmark used when selected, and add it to your stylesheet using a base64-encoded URL:

input[type=checkbox]:checked {
  background-color: #6699FF;
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDEyIDEyIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTEuNiwyLjNsLTYuOCw4LjhjMCwwLS4xLjItLjMuMnMtLjIsMC0uMy0uMVMuNCw3LjQuNCw3LjRoMGMwLS4xLDAtLjIsMC0uMnMwLS4xLDAtLjJjMCwwLDAsMCwwLDAsLjQtLjQsMS4xLTEuMiwxLjItMS4zLDAsMCwuMS0uMS4yLS4xcy4yLjEuMy4yYzAsMCwyLjIsMi4xLDIuMiwyLjFMOS44LjhzLjEsMCwuMiwwLC4xLDAsLjIsMGwxLjUsMS4yczAsLjEsMCwuMmMwLDAsMCwuMSwwLC4yWiIvPjwvc3ZnPgo=');
  background-size: 12px 12px;
  background-position: center center;
  background-repeat: no-repeat;
}

Voilà! No hacks, no magic tricks. Even better, the checkbox keeps satisfying all a11y requirements: you can navigate/select it with a keyboard, and it keeps its role="checkbox", which is super helpful when testing your UI with something like Testing Library. Follow exactly the same steps to customize radio buttons.

Support

Chrome 109+Firefox 115+Safari 9.1+Edge 109+Samsung 5.0+
macOS 10.11+
Windows 8+
Android 7.0+
iOS 11+

Hands-on: In this Gist, you'll find an HTML file with all the code you need to run the example shown in the video above.

Learn more: If you liked this post, you might also enjoy learning how to style elements with multiple borders using pure CSS.


If you enjoyed the content, consider dropping a tip. Your contribution helps me keep the website running and fuels it with fresh, quality content. Thank you!


Great everyday programming tips every month.

You can also stay in the loop via RSS