Style Native Form Controls With accent-color
A checkbox can pick up a site’s colour without becoming a custom component. CSS accent-color changes the accent used by supported native controls while leaving their HTML behaviour in place.
That is often enough for a form whose only visual mismatch is the browser’s default accent. Rebuilding the control would be a much larger change than the design requires.
Set accent-color on the form or document
A document-wide default needs one declaration:
:root {
accent-color: #6b4eff;
}
The accent-color reference lists checkboxes, radio buttons, range inputs, and progress elements as controls that supporting browsers can accent. The property is inherited, so descendants can use the document’s value.
The result is still browser-controlled. The accent does not colour every part of every state, and the same declaration need not produce identical controls across platforms.
A particular form can override the inherited colour:
form {
accent-color: #e84a5f;
}
Choose the scope that owns the colour. There is little reason to assign the same value separately to every input when the whole form shares it.
Keep a labelled checkbox
The HTML can stay small:
<label>
<input type="checkbox" name="newsletter">
Subscribe to the newsletter
</label>
The native checkbox supplies its checked state and interaction model. Wrapping it in the label also associates the visible text with the control.
Changing its accent does not require replacing the checkmark with an image or adding a script to toggle a visual state. Keep the native appearance if colouring the control solves the design problem.
Give radio buttons a real group
Radios sharing a name express a mutually exclusive choice:
<fieldset>
<legend>Plan</legend>
<label><input type="radio" name="plan" value="basic" checked> Basic</label>
<label><input type="radio" name="plan" value="pro"> Pro</label>
</fieldset>
The inherited accent can apply to the selected radio without a separate colour rule. Keep the labels and grouping meaningful; an accent cannot explain what the choices represent.
The radio input documentation covers the shared-name grouping and native interaction. Those responsibilities remain with HTML.
Ranges and progress need names too
A range input can use the same accent for parts of its native slider:
<label for="volume">Volume</label>
<input id="volume" type="range" min="0" max="100" value="60">
A progress element can also receive the accent in supporting browsers:
<label for="upload">Upload progress</label>
<progress id="upload" max="100" value="70">70%</progress>
MDN’s progress reference distinguishes a label from the fallback text inside the element. The visible label gives the indicator a name; 70% is not a substitute for it.
The exact slider and progress styling varies by implementation. Treat the accent as a colour preference for the native widget, rather than a specification for every pixel.
Give light-dark() an actual colour scheme
A theme-dependent accent needs more than two colours. The light-dark() function selects from the element’s used colour scheme:
:root {
color-scheme: light dark;
accent-color: #3157ff;
accent-color: light-dark(#3157ff, #8da0ff);
}
The first accent declaration is a fallback for browsers that cannot use the function. The second can choose a different accent for the active scheme.
Declaring both schemes also tells the browser the page supports them. Custom backgrounds and text colours need to agree with that choice; the accent rule alone does not create a complete dark theme.
Keep focus and state changes visible
A colour change does not remove the need for a visible keyboard focus indicator. Keeping the browser’s indicator is a reasonable starting point.
If the design supplies one, scope it to the controls and check its contrast:
input:focus-visible {
outline: 2px solid currentColor;
outline-offset: 3px;
}
The focus-visible selector follows the browser’s determination that focus should be visibly indicated. Check selected, unselected, and disabled states too, rather than judging the accent from one checked checkbox.
The property cannot provide a completely custom checkbox shape or a pixel-identical control on every platform. When a brand colour is enough, that limited scope keeps the native widget doing most of the work.
Filed under CSS, HTML and Accessibility on