How to change the look of a checkbox and a radio button
Short answer
One property, accent-color, changes the color of a checked checkbox and radio button:
<style>
input { accent-color: rebeccapurple; width: 18px; height: 18px; }
</style>
<label><input type="checkbox" checked> Send me news</label><br>
<label><input type="radio" name="plan" checked> Monthly</label>
<label><input type="radio" name="plan"> Yearly</label>Width and height enlarge the checkbox itself — by default it is tiny and hard to hit with a finger.
A fully custom look
appearance: none removes the default rendering, and the element can be drawn from scratch. The :checked pseudo-class shows the check mark:
<style>
.box { appearance: none; width: 20px; height: 20px; margin: 0; border: 2px solid gray; border-radius: 6px; display: inline-grid; place-content: center; vertical-align: middle; }
.box:checked { border-color: seagreen; background: seagreen; }
.box:checked::before { content: "✓"; color: white; font-size: 14px; }
.box:focus-visible { outline: 2px solid orange; outline-offset: 2px; }
</style>
<label><input class="box" type="checkbox" checked> I agree</label>The element stays a real checkbox: it works from the keyboard, is submitted with the form and is understood by screen readers.
Common mistake
The checkbox is hidden with display: none, and a check mark image is drawn next to it. Such a checkbox cannot be reached with Tab or toggled with Space, and a screen reader does not see it. Use appearance: none on the <input> itself — or hide it visually, but do not remove it.
Functions used in this recipe
- <input type="checkbox"> — A checkbox is an independent yes/no choice; a radio button picks one option from a group.
- accent-color — The accent color of built-in form controls: checkboxes, radio buttons, sliders and progress bars.
- appearance — Turns off the built-in look of form controls so they can be styled from scratch.
- :checked — Matches a checked checkbox or radio button, and a selected `<option>`.
- <label> — A label for a form field.