Color Picker Input — Free HTML CSS JS Snippet
Color Picker Input · Forms · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="demo">
<div class="picker-card">
<div class="preview-bar" id="preview-bar"></div>
<div class="picker-body">
<label>Brand color</label>
<div class="hex-row">
<span class="hash">#</span>
<input id="hex" class="hex-inp" value="6366F1" maxlength="6" oninput="fromHex(this.value)" />
<input type="color" id="native" value="#6366f1" oninput="fromNative(this.value)" class="native-input" />
<button class="eye-drop" title="Pick color" onclick="document.getElementById('native').click()">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M2 22l1-1h3l9-9"/><path d="M3 21v-3l9-9"/><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.4.4a2.1 2.1 0 1 1-3 3l-3.8-3.8"/></svg>
</button>
</div>
<div class="swatches">
<span class="sw-label">Presets</span>
<div class="sw-row" id="sw-row"></div>
</div>
<div class="opacity-row">
<label>Opacity <span id="op-val">100%</span></label>
<input type="range" id="opacity" min="0" max="100" value="100" oninput="setOpacity(this.value)" />
</div>
</div>
</div>
</div>* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #f8fafc; display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; }
.demo { width: 280px; }
.picker-card { background: #fff; border-radius: 14px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.1); border: 1px solid #e2e8f0; }
.preview-bar { height: 72px; background: #6366f1; transition: background 0.2s; }
.picker-body { padding: 16px; display: flex; flex-direction: column; gap: 14px; }
label { font-size: 12px; font-weight: 600; color: #475569; }
.hex-row { display: flex; align-items: center; background: #f8fafc; border: 1.5px solid #e2e8f0; border-radius: 8px; overflow: hidden; transition: border-color 0.15s; }
.hex-row:focus-within { border-color: #6366f1; }
.hash { padding: 0 8px; font-size: 13px; font-family: monospace; color: #94a3b8; }
.hex-inp { flex: 1; padding: 9px 0; font-size: 13px; font-family: monospace; border: none; outline: none; background: transparent; color: #1e293b; text-transform: uppercase; }
.native-input { opacity: 0; width: 0; height: 0; pointer-events: none; }
.eye-drop { width: 36px; height: 36px; background: none; border: none; border-left: 1px solid #e2e8f0; color: #94a3b8; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.12s, color 0.12s; }
.eye-drop:hover { background: #f1f5f9; color: #6366f1; }
.swatches { display: flex; flex-direction: column; gap: 8px; }
.sw-label { font-size: 12px; font-weight: 600; color: #475569; }
.sw-row { display: flex; gap: 6px; flex-wrap: wrap; }
.sw { width: 22px; height: 22px; border-radius: 6px; cursor: pointer; border: 2px solid transparent; transition: transform 0.12s, border-color 0.12s; }
.sw:hover { transform: scale(1.15); }
.sw.active { border-color: #1e293b; }
.opacity-row { display: flex; flex-direction: column; gap: 6px; }
.opacity-row label { display: flex; justify-content: space-between; }
input[type="range"] { width: 100%; height: 4px; -webkit-appearance: none; background: linear-gradient(to right, #6366f1 100%, #e2e8f0 100%); border-radius: 4px; outline: none; accent-color: #6366f1; }const PRESETS = ['#ef4444','#f97316','#f59e0b','#22c55e','#0ea5e9','#6366f1','#8b5cf6','#ec4899','#1e293b','#94a3b8'];
const bar = document.getElementById('preview-bar');
const hexInp = document.getElementById('hex');
const native = document.getElementById('native');
let opacity = 100;
function applyColor(hex) {
const r = parseInt(hex.slice(0,2),16), g = parseInt(hex.slice(2,4),16), b = parseInt(hex.slice(4,6),16);
bar.style.background = `rgba(${r},${g},${b},${opacity/100})`;
document.querySelector('input[type="range"]').style.background =
`linear-gradient(to right, #${hex} ${opacity}%, #e2e8f0 ${opacity}%)`;
}
function fromHex(v) {
if (v.length === 6 && /^[0-9a-fA-F]{6}$/.test(v)) {
native.value = '#' + v;
applyColor(v.toUpperCase());
}
}
function fromNative(v) {
const hex = v.slice(1).toUpperCase();
hexInp.value = hex;
applyColor(hex);
}
function setOpacity(v) {
opacity = v;
document.getElementById('op-val').textContent = v + '%';
applyColor(hexInp.value);
}
// Build swatches
const row = document.getElementById('sw-row');
PRESETS.forEach(c => {
const s = document.createElement('div');
s.className = 'sw' + (c === '#6366f1' ? ' active' : '');
s.style.background = c;
s.onclick = () => {
document.querySelectorAll('.sw').forEach(x => x.classList.remove('active'));
s.classList.add('active');
const hex = c.slice(1).toUpperCase();
hexInp.value = hex; native.value = c;
applyColor(hex);
};
row.appendChild(s);
});
applyColor('6366F1');Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Color Picker Input — Preset Swatches, Hex Input Sync, Opacity Slider & Native Picker

A colour picker input is a specialised form field that lets users select a colour through multiple interaction methods: clicking a preset swatch, typing a hex code directly, or opening the native OS colour picker via an eyedropper button. For a hue wheel instead, see the color wheel picker; for product variants, the color swatch selector. This snippet implements all three methods in a unified card component with a live preview bar, an opacity slider, and full two-way synchronisation between every input method.
The PRESETS swatch array
The PRESETS array contains 10 hex colour values covering the most common brand palette needs: reds, oranges, yellows, greens, blues, indigos, purples, pinks, and neutrals. The swatch rendering loop uses PRESETS.forEach(c => { const s = document.createElement('div'); s.className = 'sw'; s.style.background = c; s.onclick = () => { ... }; row.appendChild(s); }). When a swatch is clicked, it marks itself with the .active class (adding a dark border ring), updates the hex input value, and calls applyColor(hex) to update the preview bar.
The hex input with live validation
The hex input is prefixed by a # hash span (purely decorative, the hash is not part of the input value). The fromHex(v) function validates the input using /^[0-9a-fA-F]{6}$/.test(v) and only calls applyColor() when exactly 6 valid hex characters are present. This prevents the preview from flashing during typing. The input uses text-transform: uppercase in CSS to display hex values in the standard uppercase format.
The hidden native colour picker
A real <input type="color"> element is rendered at 0 width and height with opacity: 0; pointer-events: none so it is invisible but fully functional. The eyedropper button's onclick calls document.getElementById('native').click() to programmatically open the OS colour picker. When the user selects a colour in the OS picker, the oninput="fromNative(this.value)" handler fires, strips the leading hash, and syncs the hex input and preview bar. This technique opens the native picker without any visible input element in the UI.
The opacity slider with gradient track
The opacity range input controls a separate opacity variable. The setOpacity(v) function converts the 0–100 range value to a 0–1 decimal for the rgba() colour in the preview. The slider track is updated dynamically: linear-gradient(to right, #hex opacity%, #e2e8f0 opacity%) creates a visual fill effect showing how much of the track corresponds to the current opacity value.
The applyColor() function
applyColor(hex) is the single function that all three input methods call. It parses the 6-character hex into r, g, b integer values using parseInt(hex.slice(0,2), 16) and constructs an rgba(r,g,b,opacity/100) string applied to the preview bar's background. This single-source-of-truth approach ensures the preview is always consistent regardless of which input method was used.
Step by step
How to Use
- 1Click preset swatches to select a colourClick any of the 10 preset colour swatches below the hex input. The clicked swatch gets a dark active border ring, the hex input updates with the swatch's hex code, and the preview bar at the top of the card immediately changes to the selected colour.
- 2Type a hex code directly into the inputClick the hex input field and type a 6-character hex code like 3B82F6 (without the # prefix — the # is shown as a prefix label). The preview bar updates as soon as you type a valid 6-character hex value. The input validates with /^[0-9a-fA-F]{6}$/ before applying.
- 3Open the OS native colour picker via the eyedropper buttonClick the eyedropper icon button on the right side of the hex input row. This programmatically triggers a hidden <input type='color'> element, opening your operating system's native colour picker. When you select a colour and close the picker, the hex input and preview bar update to match your selection.
- 4Adjust the opacity sliderDrag the Opacity range slider to change transparency from 0% (fully transparent) to 100% (fully opaque). The preview bar background uses rgba() so it shows the colour at the selected opacity over the white card background. The slider track fills with the current colour to show the full opacity gradient.
- 5Customise the PRESETS array with your brand paletteIn the JS panel, replace the PRESETS array values with your own brand colour hex codes. The swatches are generated dynamically from this array, so adding, removing, or reordering entries automatically updates the rendered swatch row without any HTML changes.
- 6Read the selected colour value for form submission or CSS variable updatesAfter colour selection, read hexInp.value for the hex code (6 characters, no hash). For CSS custom property updates: document.documentElement.style.setProperty('--accent-color', '#' + hexInp.value). For form submission, include it as a hidden input or append it to your FormData object before submitting.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A hidden <input type='color' id='native'> element is rendered with opacity: 0; width: 0; height: 0; pointer-events: none so it is invisible but functional. The eyedropper button's onclick handler calls document.getElementById('native').click(), which programmatically opens the OS native colour picker. When the user selects a colour and the picker closes, the oninput='fromNative(this.value)' handler fires. fromNative() strips the leading # from the value, converts it to uppercase, sets hexInp.value to the stripped hex string, sets native.value to synchronise them, then calls applyColor() to update the preview bar and opacity slider track.
The fromHex(v) function checks two conditions before calling applyColor: v.length === 6 ensures the full 6-character code is entered (so the preview doesn't flash during typing of incomplete codes), and /^[0-9a-fA-F]{6}$/.test(v) ensures all characters are valid hexadecimal digits. If either check fails, the function returns early without updating the preview. When both pass, native.value is synchronised to '#' + v and applyColor(v.toUpperCase()) is called to update the preview bar.
The setOpacity(v) function stores the slider value in the opacity variable and calls applyColor(hexInp.value). Inside applyColor(hex), the function parses the hex string into r, g, b integer values using parseInt(hex.slice(0,2), 16) for each channel. It then constructs rgba(r, g, b, opacity/100) and applies it to bar.style.background. The slider track is simultaneously updated using linear-gradient(to right, #hex opacity%, #e2e8f0 opacity%) to create a visual fill effect that shows the current opacity as a proportional fill of the track.
Replace the PRESETS array with objects containing both the hex value and a name: const PRESETS = [{hex:'#ef4444',name:'Red'},{hex:'#6366f1',name:'Indigo'},...]. In the swatch-building loop, set s.style.background = c.hex, s.title = c.name (for native browser tooltip), and pass c.hex to the onclick handler's applyColor call. For a custom styled tooltip instead of the native title, add a tooltip span as a child of the swatch and show/hide it on mouseenter/mouseleave events.