This article covers building a colour picker with CSS and HTML.
The short answer: if all you need is for someone to pick a colour, <input type="color"> is the whole answer. The browser and operating system supply the picker, and it works with a keyboard out of the box. Building your own is for when the appearance has to match a design exactly, or when the available colours need to be restricted.
Sponsored
Why you might build one
Colour pickers turn up in theme settings, image editors, graphics tools and chart configuration. In each case the goal is the same: let someone choose a colour without typing a hex value.
▼Existing libraries
A library is worth it when you need a full HSL wheel with alpha. For a swatch grid or a styled trigger, hand-writing it is less work than configuring one.
Using the native input
Start here. One element gives you a working picker.
<label for="color">Pick a colour</label>
<input type="color" id="color" value="#3498db">
The value is always a seven-character hex string, so reading it needs no parsing:
document.getElementById('color').addEventListener('input', (e) => {
document.body.style.backgroundColor = e.target.value;
});
Use the input event rather than change if you want live feedback while the user drags — change only fires once the picker closes.
Sponsored
Styling the trigger
The picker panel itself is drawn by the operating system and cannot be styled. What you can restyle is the swatch that opens it.
input[type="color"] {
appearance: none;
-webkit-appearance: none;
width: 44px;
height: 44px;
border: 2px solid #ccc;
border-radius: 8px;
padding: 0;
cursor: pointer;
background: none;
}
/* the coloured area inside the control */
input[type="color"]::-webkit-color-swatch {
border: none;
border-radius: 6px;
}
input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
input[type="color"]::-moz-color-swatch {
border: none;
border-radius: 6px;
}
The pseudo-elements are vendor-specific, so both are needed. Anything beyond the swatch — the panel, the eyedropper, the sliders — belongs to the platform and is out of reach.
Building a swatch grid instead
When the point is to restrict the choice rather than open it up, a set of radio inputs is simpler and more accessible than a custom widget.
<fieldset class="swatches">
<legend>Theme colour</legend>
<input type="radio" name="theme" id="c1" value="#e74c3c">
<label for="c1" style="--swatch: #e74c3c"><span class="sr-only">Red</span></label>
<input type="radio" name="theme" id="c2" value="#3498db">
<label for="c2" style="--swatch: #3498db"><span class="sr-only">Blue</span></label>
</fieldset>
.swatches input {
position: absolute;
opacity: 0;
width: 0;
}
.swatches label {
display: inline-block;
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--swatch);
border: 3px solid transparent;
cursor: pointer;
}
.swatches input:checked + label {
border-color: #333;
}
/* show a focus ring when navigating by keyboard */
.swatches input:focus-visible + label {
outline: 2px solid #1976d2;
outline-offset: 2px;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
}
Radio inputs give you keyboard support for free. Arrow keys move between options and the group behaves as one control, which is exactly right for a colour choice.
Never rely on colour alone to show which one is selected. The border in the CSS above is what makes the state visible to someone who cannot distinguish the swatches. The visually hidden label text is what makes each option announceable.
Sponsored
Checking contrast
If the chosen colour will sit behind text, the combination has to stay readable.
Letting users pick any colour means some of those colours will fail against your text. Either constrain the palette to combinations you have checked, or adjust the text colour based on the choice.
The WebAIM Color Contrast Checker is the quickest way to verify a pair. WCAG asks for a ratio of at least 4.5:1 for body text.
Summary
Reach for <input type="color"> first, a swatch grid when the choice should be limited, and a library only when you genuinely need a full colour wheel.
Building a picker from scratch means rebuilding keyboard support, focus indication and screen reader labelling — all of which the native control and radio inputs already provide.
The same appearance: none reasoning applies to other form controls. Styling checkboxes and radio buttons with CSS covers what you take on when you switch off the native rendering.