TechEarl

CSS accent-color: Brand-Color Checkboxes, Radios, and Form Controls

Tint native checkboxes, radio buttons, range sliders, and progress bars to your brand color with one line of CSS. accent-color, the controls it affects, the auto-contrast checkmark, and the graceful fallback for old browsers.

Ishan Karunaratne⏱️ 8 min readUpdated
Share thisCopied
Tint native checkboxes, radios, range sliders, and progress bars to your brand color with one line of CSS using accent-color, with automatic checkmark contrast and a graceful fallback.

If you just want your checkboxes and radios to match your brand color instead of the operating system's blue, you no longer have to rebuild the controls from scratch. One CSS property does it:

css
:root {
  accent-color: #6d28d9;
}

That single declaration on :root recolors every native checkbox, radio button, range slider, and progress bar on the page to #6d28d9. No hidden inputs, no SVG checkmarks, no JavaScript, no appearance: none plumbing. The browser keeps painting its own accessible, keyboard-navigable controls; you are only changing the tint.

For years the "color my checkbox" answer was a depressing pile of appearance: none, a sibling <label>, a pseudo-element checkmark, and a focus-ring you had to reimplement by hand. accent-color retires almost all of that for the common case of "I just want the brand color."

What accent-color actually tints

The property only touches the form controls the browser draws for you. As of 2026 that is exactly four elements:

  • <input type="checkbox">: the box fill and the checkmark.
  • <input type="radio">: the dot and its ring.
  • <input type="range">: the filled portion of the track and the thumb.
  • <progress>: the filled bar.
css
/* brand-wide, set once */
:root {
  accent-color: #6d28d9;
}

/* or scope it to one control where you need a different tint */
.danger-zone input[type="checkbox"] {
  accent-color: crimson;
}

It does not touch text inputs, selects, buttons, or borders. It is a control-tint property, not a general theming property. If you set it on a <div> it inherits down to any controls inside, which is why putting it on :root is the usual move: one line, whole site.

The auto value, and letting the OS decide

The initial value is auto, which means "use the browser or operating system's own accent." On recent systems that can mean the control picks up the accent color the user chose in their OS settings, which is a genuinely nice default for utilitarian apps. You only need to set an explicit color when brand consistency matters more than matching the user's chrome:

css
input[type="checkbox"] {
  accent-color: auto; /* the default: defer to the platform */
}

The checkmark contrast is handled for you

The reason you do not pass a second color for the checkmark is that the browser computes one. When you set accent-color, the engine picks a contrasting color for the mark inside the control (the tick in a checkbox, the dot in a radio) so it stays legible. Set a very dark accent and the checkmark goes light; set a pale accent and it goes dark. This is built into the implementation specifically to stop people from shipping a brand-blue checkbox with a brand-blue (invisible) checkmark on top of it. You get the accessibility for free, which is most of the point.

That said, accent-color is not a license to ignore contrast entirely. The control still sits on your page background, so an accent that is too close to the surrounding color can be hard to perceive even with a correct checkmark. Pick a tint that has real contrast against the form's background, the same way you would for any interactive element.

Pairing with color-scheme for dark mode

accent-color cooperates with color-scheme. If you opt a page into both light and dark rendering, the accent applies in both, and the auto-contrast checkmark recomputes per scheme:

css
:root {
  color-scheme: light dark;
  accent-color: #6d28d9;
}

With color-scheme: light dark the browser renders form controls (and scrollbars, and the default canvas) appropriately for whichever mode the user is in, while your single accent-color keeps the brand tint consistent across both. If your accent reads well in light mode but muddy in dark, that is the case for a prefers-color-scheme media query that swaps the accent value, rather than fighting the control internals.

The range slider takes two colors

<input type="range"> is the one control where the accent splits across two parts: the filled portion of the track on one side of the thumb, and the thumb itself. A single accent-color colors both with the same value, which is the sane default. There is no standard property to color the filled track and the thumb differently through accent-color alone; if you need that level of control on a slider, you are back to ::-webkit-slider-thumb / ::-moz-range-thumb territory, which is the old per-engine pseudo-element work. For the overwhelmingly common "make the slider brand-colored" case, the one line is enough.

Browser support and the fallback

accent-color is Baseline: Chrome and Edge shipped it in 93 (2021), Firefox in 92 (2021), and Safari completed the set in 15.4 in March 2022. Since every current engine supports it, you can use it in production today with no feature query and no polyfill.

The fallback is the best kind: nothing. A browser that does not understand accent-color simply ignores the declaration and paints its default control, exactly as it would have if you had never written the line. There is no broken layout, no missing checkmark, no invisible input. The control just stays the platform color. So even when you cared about a long tail of ancient browsers, shipping accent-color cost you nothing: modern visitors get the brand tint, everyone else gets a perfectly usable default control.

Because the degradation is silent and safe, you do not need @supports here. (Where you genuinely need to branch CSS on whether a feature exists, see CSS feature queries with @supports; this just is not one of those cases.)

Where accent-color stops

accent-color is deliberately narrow. It tints the four native controls and nothing else. It will not:

  • Change the control's size, shape, border-radius, or spacing.
  • Style text inputs, selects, or buttons.
  • Recolor the checkmark independently of the accent (the browser owns that).
  • Give you a custom toggle-switch look.

The moment you want to change the shape of a checkbox, not just its color, you are back to appearance: none plus a custom-built control, and you take on rebuilding the focus and checked states yourself. When you do that, keep the keyboard focus ring honest with :focus-visible, and reach for selectors like :has() and the sibling/parent matches it enables to drive label and wrapper styling off the control's checked state. The trade is real: accent-color gives you the brand color and keeps all the native accessibility; a fully custom control gives you total visual control and hands you back the responsibility for everything the browser was doing for you. For most projects, "match the brand color, keep the native control" is the right answer, and it is one line.

FAQ

See also

  • CSS feature queries with @supports: how to branch CSS on whether a feature exists, for the cases where graceful degradation is not enough.
  • CSS :focus-visible: keep an accessible focus ring on controls, especially custom ones you build when accent-color is not enough.
  • The CSS :has() selector: style a label or wrapper based on whether the checkbox or radio inside it is checked, which pairs naturally with an accent-tinted control.

Sources

Authoritative references this article was fact-checked against.

Tagsaccent-colorCSSform controlscheckbox stylingradio buttonrange slidercolor-scheme

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts