$ cat docs/color-picker.md
updated Recently · 14 min read · published
Color Picker Color selection components from the native <input type="color"> to custom swatch grids, HSL-based pickers, hex/rgb input fields, and gradient generators \u2014 all with live previews.
Native Color Input
The native <input type="color"> opens the browser's built-in color picker. It returns a hex value and provides a simple, accessible way to select colors with zero JavaScript. The value is always in #RRGGBB format.
native-color HTML CSS JS Live
Copy 1 <div class="native-demo"> 2 <div class="nd-field"> 3 <label for="color-native"> 4 Pick a color 5 </label> 6 <div class="nd-row"> 7 <input type="color" id="color-native" value="#00FF41" class="color-input"/> 8 <span class="nd-value" id="nd-val"> 9 #00FF41 10 </span> 11 </div> 12 </div> 13 </div>
Copy 1 .native-demo { 2 display:flex; 3 flex-direction:column; 4 gap:16px; 5 padding:32px; 6 align-items:center; 7 font-family:system-ui, 8 sans-serif 9 } 10 .nd-field { 11 display:flex; 12 flex-direction:column; 13 gap:6px; 14 max-width:280px; 15 width:100% 16 } 17 .nd-field label { 18 font-size:12px; 19 font-weight:600; 20 color:#E0E0E0 21 } 22 .nd-row { 23 display:flex; 24 align-items:center; 25 gap:12px 26 } 27 .color-input { 28 width:48px; 29 height:36px; 30 border:2px solid #2A2A3E; 31 border-radius:8px; 32 cursor:pointer; 33 background:transparent; 34 padding:2px 35 } 36 .color-input::-webkit-color-swatch-wrapper { 37 padding:0 38 } 39 .color-input::-webkit-color-swatch { 40 border:none; 41 border-radius:4px 42 } 43 .color-input::-moz-color-swatch { 44 border:none; 45 border-radius:4px 46 } 47 .nd-value { 48 font-size:13px; 49 color:#00FF41; 50 font-weight:600; 51 font-family:monospace; 52 font-variant-numeric:tabular-nums 53 }
untitled.js wrap JavaScript
Copy 1 const input=document.getElementById('color-native');const val=document.getElementById('nd-val');input.addEventListener('input',()=>val.textContent=input.value.toUpperCase())
preview
Native Color Input Styling
The native color input exposes pseudo-elements for customizing the swatch appearance. Use ::-webkit-color-swatch-wrapper for padding and ::-webkit-color-swatch for the color preview itself.
color-input-styling.css wrap CSS
Copy 1 /* Remove default padding */ 2 .color-input::-webkit-color-swatch-wrapper { 3 padding: 0; 4 } 5 6 /* Style the color swatch */ 7 .color-input::-webkit-color-swatch { 8 border: none; 9 border-radius: 6px; 10 } 11 12 /* Firefox uses different pseudo-elements */ 13 .color-input::-moz-color-swatch { 14 border: none; 15 border-radius: 6px; 16 } 17 18 /* Container styling */ 19 .color-input { 20 width: 48px; 21 height: 36px; 22 border: 2px solid #2A2A3E; 23 border-radius: 8px; 24 cursor: pointer; 25 background: transparent; 26 padding: 2px; 27 }
Color Swatches
A grid of preset color swatches for quick selection. Clicking a swatch selects it and highlights the active choice with a ring. Ideal for theme pickers, design tools, and branded interfaces where the color palette is curated.
Copy 1 <div class="swatch-demo"> 2 <div class="sd-header"> 3 <span class="sd-title"> 4 Theme Color 5 </span> 6 <span class="sd-hex" id="sd-hex"> 7 #00FF41 8 </span> 9 </div> 10 <div class="swatch-grid" id="swatch-grid"> 11 <button class="swatch active" data-color="#00FF41" style="background:#00FF41" aria-label="Green"> 12 </button> 13 <button class="swatch" data-color="#3B82F6" style="background:#3B82F6" aria-label="Blue"> 14 </button> 15 <button class="swatch" data-color="#EF4444" style="background:#EF4444" aria-label="Red"> 16 </button> 17 <button class="swatch" data-color="#F59E0B" style="background:#F59E0B" aria-label="Amber"> 18 </button> 19 <button class="swatch" data-color="#8B5CF6" style="background:#8B5CF6" aria-label="Purple"> 20 </button> 21 <button class="swatch" data-color="#EC4899" style="background:#EC4899" aria-label="Pink"> 22 </button> 23 <button class="swatch" data-color="#14B8A6" style="background:#14B8A6" aria-label="Teal"> 24 </button> 25 <button class="swatch" data-color="#F97316" style="background:#F97316" aria-label="Orange"> 26 </button> 27 <button class="swatch" data-color="#E0E0E0" style="background:#E0E0E0" aria-label="Light"> 28 </button> 29 <button class="swatch" data-color="#808080" style="background:#808080" aria-label="Gray"> 30 </button> 31 <button class="swatch" data-color="#444444" style="background:#444444" aria-label="Dark"> 32 </button> 33 <button class="swatch" data-color="#000000" style="background:#000000;border:1px solid #333" aria-label="Black"> 34 </button> 35 </div> 36 </div>
Copy 1 .swatch-demo { 2 max-width:340px; 3 margin:16px auto; 4 padding:16px 20px; 5 background:#111; 6 border:1px solid #222; 7 border-radius:10px; 8 font-family:system-ui, 9 sans-serif 10 } 11 .sd-header { 12 display:flex; 13 justify-content:space-between; 14 align-items:center; 15 margin-bottom:14px 16 } 17 .sd-title { 18 font-size:12px; 19 font-weight:600; 20 color:#E0E0E0 21 } 22 .sd-hex { 23 font-size:12px; 24 color:#00FF41; 25 font-weight:600; 26 font-family:monospace 27 } 28 .swatch-grid { 29 display:grid; 30 grid-template-columns:repeat(6, 31 1fr); 32 gap:8px 33 } 34 .swatch { 35 width:100%; 36 aspect-ratio:1; 37 border-radius:8px; 38 border:2px solid transparent; 39 cursor:pointer; 40 transition:all .2s; 41 padding:0 42 } 43 .swatch:hover { 44 transform:scale(1.1) 45 } 46 .swatch.active { 47 border-color:#E0E0E0; 48 box-shadow:0 0 12px rgba(255, 49 255, 50 255, 51 .15); 52 transform:scale(1.05) 53 }
untitled.js wrap JavaScript
Copy 1 const hex=document.getElementById('sd-hex');document.getElementById('swatch-grid').addEventListener('click',e=>{const s=e.target.closest('.swatch');if(!s)return;document.querySelectorAll('.swatch').forEach(x=>x.classList.remove('active'));s.classList.add('active');hex.textContent=s.dataset.color})
preview
Hex & RGB Inputs
Manual color entry with hex and RGB input fields. Changes in any field update all others and the live preview swatch. Supports both #RRGGBB and shorthand #RGB formats. The RGB inputs clamp values to 0-255.
Copy 1 <div class="hex-rgb-demo"> 2 <div class="hr-preview" id="hr-preview" style="background:#00FF41"> 3 </div> 4 <div class="hr-fields"> 5 <div class="hr-group"> 6 <label> 7 HEX 8 </label> 9 <input type="text" class="hr-input hex" id="hr-hex" value="#00FF41" maxlength="7"/> 10 </div> 11 <div class="hr-group"> 12 <label> 13 R 14 </label> 15 <input type="number" class="hr-input rgb" id="hr-r" min="0" max="255" value="0"/> 16 </div> 17 <div class="hr-group"> 18 <label> 19 G 20 </label> 21 <input type="number" class="hr-input rgb" id="hr-g" min="0" max="255" value="255"/> 22 </div> 23 <div class="hr-group"> 24 <label> 25 B 26 </label> 27 <input type="number" class="hr-input rgb" id="hr-b" min="0" max="255" value="65"/> 28 </div> 29 </div> 30 <div class="hr-output"> 31 <span class="hr-label"> 32 CSS: 33 </span> 34 <code id="hr-css"> 35 rgb(0, 255, 65) 36 </code> 37 </div> 38 </div>
Copy 1 .hex-rgb-demo { 2 max-width:360px; 3 margin:16px auto; 4 padding:16px 20px; 5 background:#111; 6 border:1px solid #222; 7 border-radius:10px; 8 font-family:system-ui, 9 sans-serif 10 } 11 .hr-preview { 12 width:100%; 13 height:48px; 14 border-radius:8px; 15 margin-bottom:14px; 16 border:1px solid rgba(255, 17 255, 18 255, 19 .05); 20 transition:background .15s ease 21 } 22 .hr-fields { 23 display:flex; 24 gap:8px; 25 margin-bottom:12px 26 } 27 .hr-group { 28 flex:1; 29 display:flex; 30 flex-direction:column; 31 gap:4px 32 } 33 .hr-group:first-child { 34 flex:2 35 } 36 .hr-group label { 37 font-size:9px; 38 color:#666; 39 text-transform:uppercase; 40 letter-spacing:.5px 41 } 42 .hr-input { 43 padding:8px; 44 border-radius:6px; 45 border:1px solid #2A2A3E; 46 background:#0A0A0A; 47 color:#E0E0E0; 48 font-size:12px; 49 font-family:monospace; 50 outline:none; 51 text-align:center 52 } 53 .hr-input:focus { 54 border-color:#00FF41 55 } 56 .hr-input.hex { 57 text-align:left; 58 padding-left:10px 59 } 60 .hr-output { 61 display:flex; 62 align-items:center; 63 gap:8px; 64 padding:8px 10px; 65 background:#0A0A0A; 66 border-radius:6px; 67 border:1px solid #222 68 } 69 .hr-label { 70 font-size:10px; 71 color:#666 72 } 73 .hr-output code { 74 font-size:11px; 75 color:#00FF41; 76 font-family:monospace 77 }
untitled.js wrap JavaScript
Copy 1 const hexIn=document.getElementById('hr-hex');const rIn=document.getElementById('hr-r');const gIn=document.getElementById('hr-g');const bIn=document.getElementById('hr-b');const preview=document.getElementById('hr-preview');const cssOut=document.getElementById('hr-css');function hexToRgb(h){h=h.replace('#','');if(h.length===3)h=h[0]+h[0]+h[1]+h[1]+h[2]+h[2];return{r:parseInt(h.substring(0,2),16),g:parseInt(h.substring(2,4),16),b:parseInt(h.substring(4,6),16)}}function rgbToHex(r,g,b){return '#'+[r,g,b].map(x=>Math.max(0,Math.min(255,x)).toString(16).padStart(2,'0')).join('')}function update(r,g,b){preview.style.background='rgb('+r+','+g+','+b+')';cssOut.textContent='rgb('+r+', '+g+', '+b+')';hexIn.value=rgbToHex(r,g,b).toUpperCase();rIn.value=r;gIn.value=g;bIn.value=b}hexIn.addEventListener('input',()=>{try{const c=hexToRgb(hexIn.value);update(c.r,c.g,c.b)}catch(e){}});[rIn,gIn,bIn].forEach(inp=>inp.addEventListener('input',()=>update(parseInt(rIn.value)||0,parseInt(gIn.value)||0,parseInt(bIn.value)||0)))
preview
HSL Picker
An HSL-based color picker with hue, saturation, and lightness sliders. HSL is more intuitive for designers \u2014 adjusting hue rotates around the color wheel, while saturation and lightness control intensity and brightness. Each slider's track gradient updates dynamically.
Copy 1 <div class="hsl-demo"> 2 <div class="hsl-preview" id="hsl-preview" style="background:hsl(140,100%,50%)"> 3 </div> 4 <div class="hsl-fields"> 5 <div class="hsl-group"> 6 <div class="hsl-header"> 7 <label> 8 Hue 9 </label> 10 <span class="hsl-val" id="hsl-h-val"> 11 140° 12 </span> 13 </div> 14 <input type="range" class="hsl-slider hue" id="hsl-h" min="0" max="360" value="140"/> 15 </div> 16 <div class="hsl-group"> 17 <div class="hsl-header"> 18 <label> 19 Saturation 20 </label> 21 <span class="hsl-val" id="hsl-s-val"> 22 100% 23 </span> 24 </div> 25 <input type="range" class="hsl-slider sat" id="hsl-s" min="0" max="100" value="100"/> 26 </div> 27 <div class="hsl-group"> 28 <div class="hsl-header"> 29 <label> 30 Lightness 31 </label> 32 <span class="hsl-val" id="hsl-l-val"> 33 50% 34 </span> 35 </div> 36 <input type="range" class="hsl-slider light" id="hsl-l" min="0" max="100" value="50"/> 37 </div> 38 </div> 39 <div class="hsl-output"> 40 <code id="hsl-css"> 41 hsl(140, 100%, 50%) 42 </code> 43 <button class="hsl-copy" id="hsl-copy"> 44 Copy 45 </button> 46 </div> 47 </div>
Copy 1 .hsl-demo { 2 max-width:340px; 3 margin:16px auto; 4 padding:16px 20px; 5 background:#111; 6 border:1px solid #222; 7 border-radius:10px; 8 font-family:system-ui, 9 sans-serif 10 } 11 .hsl-preview { 12 width:100%; 13 height:48px; 14 border-radius:8px; 15 margin-bottom:14px; 16 border:1px solid rgba(255, 17 255, 18 255, 19 .05); 20 transition:background .1s ease 21 } 22 .hsl-fields { 23 display:flex; 24 flex-direction:column; 25 gap:14px; 26 margin-bottom:14px 27 } 28 .hsl-group { 29 display:flex; 30 flex-direction:column; 31 gap:4px 32 } 33 .hsl-header { 34 display:flex; 35 justify-content:space-between; 36 align-items:center 37 } 38 .hsl-header label { 39 font-size:11px; 40 color:#808080; 41 font-weight:500 42 } 43 .hsl-val { 44 font-size:11px; 45 color:#00FF41; 46 font-weight:600; 47 font-variant-numeric:tabular-nums 48 } 49 .hsl-slider { 50 width:100%; 51 height:8px; 52 border-radius:4px; 53 appearance:none; 54 outline:none; 55 cursor:pointer 56 } 57 .hsl-slider::-webkit-slider-thumb { 58 appearance:none; 59 width:16px; 60 height:16px; 61 border-radius:50%; 62 background:#E0E0E0; 63 cursor:pointer; 64 border:2px solid #0A0A0A; 65 box-shadow:0 0 6px rgba(0, 66 0, 67 0, 68 .4) 69 } 70 .hsl-slider::-moz-range-thumb { 71 width:16px; 72 height:16px; 73 border-radius:50%; 74 background:#E0E0E0; 75 cursor:pointer; 76 border:2px solid #0A0A0A 77 } 78 .hue { 79 background:linear-gradient(90deg, 80 hsl(0, 81 100%, 82 50%), 83 hsl(60, 84 100%, 85 50%), 86 hsl(120, 87 100%, 88 50%), 89 hsl(180, 90 100%, 91 50%), 92 hsl(240, 93 100%, 94 50%), 95 hsl(300, 96 100%, 97 50%), 98 hsl(360, 99 100%, 100 50%)) 101 } 102 .sat { 103 background:linear-gradient(90deg, 104 #808080, 105 #00FF41) 106 } 107 .light { 108 background:linear-gradient(90deg, 109 #000, 110 #808080, 111 #fff) 112 } 113 .hsl-output { 114 display:flex; 115 align-items:center; 116 gap:8px; 117 padding:8px 10px; 118 background:#0A0A0A; 119 border-radius:6px; 120 border:1px solid #222 121 } 122 .hsl-output code { 123 font-size:11px; 124 color:#00FF41; 125 font-family:monospace; 126 flex:1 127 } 128 .hsl-copy { 129 padding:4px 10px; 130 border-radius:4px; 131 font-size:10px; 132 font-weight:600; 133 background:transparent; 134 color:#808080; 135 border:1px solid #333; 136 cursor:pointer; 137 transition:all .2s; 138 font-family:system-ui, 139 sans-serif 140 } 141 .hsl-copy:hover { 142 color:#E0E0E0; 143 border-color:#555 144 } 145 .hsl-copy.copied { 146 color:#00FF41; 147 border-color:#00FF41 148 }
untitled.js wrap JavaScript
Copy 1 const h=document.getElementById('hsl-h');const s=document.getElementById('hsl-s');const l=document.getElementById('hsl-l');const preview=document.getElementById('hsl-preview');const cssOut=document.getElementById('hsl-css');const hVal=document.getElementById('hsl-h-val');const sVal=document.getElementById('hsl-s-val');const lVal=document.getElementById('hsl-l-val');function updateHSL(){const hv=h.value;const sv=s.value;const lv=l.value;const col='hsl('+hv+','+sv+'%,'+lv+'%)';preview.style.background=col;cssOut.textContent='hsl('+hv+', '+sv+'%, '+lv+'%)';hVal.textContent=hv+'°';sVal.textContent=sv+'%';lVal.textContent=lv+'%';const satGrad='linear-gradient(90deg,hsl('+hv+',0%,'+lv+'%),hsl('+hv+',100%,'+lv+'%))';s.style.background=satGrad;const lightGrad='linear-gradient(90deg,hsl('+hv+','+sv+'%,0%),hsl('+hv+','+sv+'%,50%),hsl('+hv+','+sv+'%,100%))';l.style.background=lightGrad}[h,s,l].forEach(el=>el.addEventListener('input',updateHSL));document.getElementById('hsl-copy').addEventListener('click',function(){navigator.clipboard.writeText(cssOut.textContent);this.textContent='Copied!';this.classList.add('copied');setTimeout(()=>{this.textContent='Copy';this.classList.remove('copied')},1500)})
preview
Palette Generator
A base color input that generates a 5-shade palette by adjusting lightness. Each shade shows its hex value and can be copied with a click \u2014 useful for design systems, theme configuration, and creating color scales from a brand color.
Copy 1 <div class="palette-demo"> 2 <div class="pd-input"> 3 <label> 4 Base Color 5 </label> 6 <input type="color" value="#3B82F6" id="pal-color" class="pd-picker"/> 7 </div> 8 <div class="pd-shades" id="pd-shades"> 9 <div class="pd-shade" style="background:hsl(217,91%,60%)"> 10 <span class="pd-label"> 11 500 12 </span> 13 <span class="pd-hex"> 14 #3B82F6 15 </span> 16 </div> 17 <div class="pd-shade" style="background:hsl(217,91%,40%)"> 18 <span class="pd-label"> 19 600 20 </span> 21 <span class="pd-hex"> 22 #2563EB 23 </span> 24 </div> 25 <div class="pd-shade" style="background:hsl(217,91%,30%)"> 26 <span class="pd-label"> 27 700 28 </span> 29 <span class="pd-hex"> 30 #1D4ED8 31 </span> 32 </div> 33 <div class="pd-shade" style="background:hsl(217,91%,70%)"> 34 <span class="pd-label"> 35 400 36 </span> 37 <span class="pd-hex"> 38 #60A5FA 39 </span> 40 </div> 41 <div class="pd-shade" style="background:hsl(217,91%,85%)"> 42 <span class="pd-label"> 43 200 44 </span> 45 <span class="pd-hex"> 46 #BFDBFE 47 </span> 48 </div> 49 </div> 50 </div>
Copy 1 .palette-demo { 2 max-width:380px; 3 margin:16px auto; 4 padding:16px 20px; 5 background:#111; 6 border:1px solid #222; 7 border-radius:10px; 8 font-family:system-ui, 9 sans-serif 10 } 11 .pd-input { 12 display:flex; 13 align-items:center; 14 gap:10px; 15 margin-bottom:14px 16 } 17 .pd-input label { 18 font-size:12px; 19 font-weight:600; 20 color:#E0E0E0 21 } 22 .pd-picker { 23 width:32px; 24 height:28px; 25 border:2px solid #2A2A3E; 26 border-radius:6px; 27 cursor:pointer; 28 background:transparent; 29 padding:1px 30 } 31 .pd-picker::-webkit-color-swatch-wrapper { 32 padding:0 33 } 34 .pd-picker::-webkit-color-swatch { 35 border:none; 36 border-radius:3px 37 } 38 .pd-shades { 39 display:flex; 40 gap:6px 41 } 42 .pd-shade { 43 flex:1; 44 height:56px; 45 border-radius:8px; 46 display:flex; 47 flex-direction:column; 48 align-items:center; 49 justify-content:center; 50 gap:2px; 51 cursor:pointer; 52 transition:transform .2s; 53 position:relative 54 } 55 .pd-shade:hover { 56 transform:scale(1.05) 57 } 58 .pd-label { 59 font-size:9px; 60 font-weight:700; 61 color:rgba(255, 62 255, 63 255, 64 .8); 65 text-shadow:0 1px 2px rgba(0, 66 0, 67 0, 68 .5) 69 } 70 .pd-hex { 71 font-size:8px; 72 color:rgba(255, 73 255, 74 255, 75 .6); 76 font-family:monospace; 77 text-shadow:0 1px 2px rgba(0, 78 0, 79 0, 80 .5) 81 }
untitled.js wrap JavaScript
Copy 1 function hexToHSL(H){let r=0,g=0,b=0;if(H.length===4){r=parseInt(H[1]+H[1],16);g=parseInt(H[2]+H[2],16);b=parseInt(H[3]+H[3],16)}else if(H.length===7){r=parseInt(H.slice(1,3),16);g=parseInt(H.slice(3,5),16);b=parseInt(H.slice(5,7),16)}r/=255;g/=255;b/=255;const max=Math.max(r,g,b),min=Math.min(r,g,b);let h=0,s=0,l=(max+min)/2;if(max!==min){const d=max-min;s=l>.5?d/(2-max-min):d/(max+min);switch(max){case r:h=((g-b)/d+(g<b?6:0))/6;break;case g:h=((b-r)/d+2)/6;break;case b:h=((r-g)/d+4)/6;break}}return{h:Math.round(h*360),s:Math.round(s*100),l:Math.round(l*100)}}function hslToHex(h,s,l){l/=100;const a=s*Math.min(l,1-l)/100;const f=n=>{const k=(n+h/30)%12;const c=l-a*Math.max(Math.min(k-3,9-k,1),-1);return Math.round(255*c).toString(16).padStart(2,'0')};return'#'+f(0)+f(8)+f(4)}document.getElementById('pal-color').addEventListener('input',function(){const hsl=hexToHSL(this.value);const shades=[{n:'500',l:hsl.l},{n:'700',l:Math.max(hsl.l-20,10)},{n:'600',l:Math.max(hsl.l-10,15)},{n:'400',l:Math.min(hsl.l+10,90)},{n:'200',l:Math.min(hsl.l+30,95)}];shades.sort((a,b)=>a.n-b.n);const grid=document.getElementById('pd-shades');grid.innerHTML='';shades.forEach(s=>{const hex=hslToHex(hsl.h,hsl.s,s.l);const div=document.createElement('div');div.className='pd-shade';div.style.background='hsl('+hsl.h+','+hsl.s+'%,'+s.l+'%)';div.innerHTML='<span class="pd-label">'+s.n+'</span><span class="pd-hex">'+hex.toUpperCase()+'</span>';div.addEventListener('click',()=>{navigator.clipboard.writeText(hex.toUpperCase());div.style.outline='2px solid #fff';setTimeout(()=>div.style.outline='',800)});grid.appendChild(div)})})
preview
Gradient Picker
A gradient builder with two color stops and angle control. The preview shows the live CSS gradient, and the output displays the ready-to-use linear-gradient() CSS value. Drag the angle slider to rotate the gradient direction.
gradient-picker HTML CSS JS Live
Copy 1 <div class="grad-demo"> 2 <div class="grad-preview" id="grad-preview" style="background:linear-gradient(135deg,#3B82F6,#8B5CF6)"> 3 </div> 4 <div class="grad-fields"> 5 <div class="grad-row"> 6 <label> 7 Start 8 </label> 9 <input type="color" value="#3B82F6" id="grad-start" class="grad-color"/> 10 <span class="grad-hex" id="grad-start-hex"> 11 #3B82F6 12 </span> 13 </div> 14 <div class="grad-row"> 15 <label> 16 End 17 </label> 18 <input type="color" value="#8B5CF6" id="grad-end" class="grad-color"/> 19 <span class="grad-hex" id="grad-end-hex"> 20 #8B5CF6 21 </span> 22 </div> 23 <div class="grad-row full"> 24 <div class="grad-angle-header"> 25 <label> 26 Angle 27 </label> 28 <span class="grad-angle-val" id="grad-angle-val"> 29 135° 30 </span> 31 </div> 32 <input type="range" class="grad-slider" id="grad-angle" min="0" max="360" value="135"/> 33 </div> 34 </div> 35 <div class="grad-output"> 36 <code id="grad-css"> 37 linear-gradient(135deg, #3B82F6, #8B5CF6) 38 </code> 39 <button class="grad-copy" id="grad-copy"> 40 Copy 41 </button> 42 </div> 43 </div>
Copy 1 .grad-demo { 2 max-width:340px; 3 margin:16px auto; 4 padding:16px 20px; 5 background:#111; 6 border:1px solid #222; 7 border-radius:10px; 8 font-family:system-ui, 9 sans-serif 10 } 11 .grad-preview { 12 width:100%; 13 height:48px; 14 border-radius:8px; 15 margin-bottom:14px; 16 border:1px solid rgba(255, 17 255, 18 255, 19 .05); 20 transition:background .15s ease 21 } 22 .grad-fields { 23 display:flex; 24 flex-wrap:wrap; 25 gap:10px; 26 margin-bottom:12px 27 } 28 .grad-row { 29 display:flex; 30 align-items:center; 31 gap:8px; 32 flex:1; 33 min-width:120px 34 } 35 .grad-row.full { 36 flex-basis:100% 37 } 38 .grad-row label { 39 font-size:10px; 40 color:#808080; 41 font-weight:500; 42 min-width:32px 43 } 44 .grad-color { 45 width:28px; 46 height:24px; 47 border:1px solid #2A2A3E; 48 border-radius:4px; 49 cursor:pointer; 50 background:transparent; 51 padding:1px 52 } 53 .grad-color::-webkit-color-swatch-wrapper { 54 padding:0 55 } 56 .grad-color::-webkit-color-swatch { 57 border:none; 58 border-radius:2px 59 } 60 .grad-hex { 61 font-size:10px; 62 color:#00FF41; 63 font-family:monospace 64 } 65 .grad-angle-header { 66 display:flex; 67 justify-content:space-between; 68 width:100%; 69 margin-bottom:2px 70 } 71 .grad-angle-val { 72 font-size:10px; 73 color:#00FF41; 74 font-weight:600 75 } 76 .grad-slider { 77 width:100%; 78 height:4px; 79 border-radius:2px; 80 appearance:none; 81 background:linear-gradient(90deg, 82 #3B82F6, 83 #8B5CF6); 84 outline:none; 85 cursor:pointer 86 } 87 .grad-slider::-webkit-slider-thumb { 88 appearance:none; 89 width:14px; 90 height:14px; 91 border-radius:50%; 92 background:#E0E0E0; 93 border:2px solid #0A0A0A; 94 cursor:pointer 95 } 96 .grad-slider::-moz-range-thumb { 97 width:14px; 98 height:14px; 99 border-radius:50%; 100 background:#E0E0E0; 101 border:2px solid #0A0A0A; 102 cursor:pointer 103 } 104 .grad-output { 105 display:flex; 106 align-items:center; 107 gap:8px; 108 padding:8px 10px; 109 background:#0A0A0A; 110 border-radius:6px; 111 border:1px solid #222 112 } 113 .grad-output code { 114 font-size:10px; 115 color:#00FF41; 116 font-family:monospace; 117 flex:1; 118 word-break:break-all 119 } 120 .grad-copy { 121 padding:4px 10px; 122 border-radius:4px; 123 font-size:10px; 124 font-weight:600; 125 background:transparent; 126 color:#808080; 127 border:1px solid #333; 128 cursor:pointer; 129 transition:all .2s; 130 font-family:system-ui, 131 sans-serif; 132 flex-shrink:0 133 } 134 .grad-copy:hover { 135 color:#E0E0E0; 136 border-color:#555 137 }
untitled.js wrap JavaScript
Copy 1 const start=document.getElementById('grad-start');const end=document.getElementById('grad-end');const angle=document.getElementById('grad-angle');const preview=document.getElementById('grad-preview');const cssOut=document.getElementById('grad-css');const sHex=document.getElementById('grad-start-hex');const eHex=document.getElementById('grad-end-hex');const aVal=document.getElementById('grad-angle-val');function updateGrad(){const v=angle.value;const c='linear-gradient('+v+'deg, '+start.value+', '+end.value+')';preview.style.background=c;cssOut.textContent=c;sHex.textContent=start.value.toUpperCase();eHex.textContent=end.value.toUpperCase();aVal.textContent=v+'°'}[start,end,angle].forEach(el=>el.addEventListener('input',updateGrad));document.getElementById('grad-copy').addEventListener('click',function(){navigator.clipboard.writeText(cssOut.textContent);this.textContent='Copied!';setTimeout(()=>this.textContent='Copy',1500)})
preview
HTML Structure
The color picker pairs a hidden <input type="color"> with visible hex/RGB inputs and a preview swatch. Sync all inputs bidirectionally so changes in any field update the rest. Include a "Copy CSS" button for convenience.
color-picker-structure.html wrap HTML
Copy 1 <div class="color-picker"> 2 <!-- Live preview swatch --> 3 <div class="preview-swatch" 4 style="background: #3B82F6"></div> 5 6 <!-- Native color input (hidden label) --> 7 <input type="color" value="#3B82F6" 8 aria-label="Color selector" /> 9 10 <!-- Manual hex input --> 11 <label for="hex-input">HEX</label> 12 <input type="text" id="hex-input" 13 value="#3B82F6" maxlength="7" /> 14 15 <!-- RGB inputs --> 16 <label for="rgb-r">R</label> 17 <input type="number" id="rgb-r" min="0" max="255" /> 18 19 <!-- CSS output --> 20 <code>rgb(59, 130, 246)</code> 21 </div>
Best Practices
ℹ info
Support multiple color formats (hex, RGB, HSL) and keep them synchronized. Different users think in different formats \u2014 designers often prefer HSL, developers prefer hex, and CSS authors prefer rgb() or hsl() .
⚠ warning
Always validate hex input before parsing \u2014 accept both #RRGGBB and #RGB shorthand, but reject invalid characters. Use parseInt(value, 16) with error handling to prevent NaN values.
✓ best practice
Include a "Copy CSS" button that copies the color in the most useful format for the context \u2014 hsl() for design tokens, hex for configuration files, rgb for inline styles.
Ensure sufficient contrast between the selected color and the preview text \u2014 use white text on dark colors and dark text on light colors dynamically. For swatch grids, always include a neutral/black swatch \u2014 users frequently need to select non-color or default options. Use aria-label on color swatch buttons since color alone is not accessible to screen readers. Consider generating analogous, complementary, and triadic palettes from the selected base color for design exploration. Debounce rapid color changes when syncing with external previews to avoid layout thrashing and excessive re-renders.