#RGB、rgb()、hsl()、oklab()等といった連中を相互変換する的な何かです。やや中途半端な性能。oninput eventに反応する設計です
<!DOCTYPE html><html><head><meta charset=sjis><meta name=viewport content="width=device-width,initial-scale=1">
<title>CSS Color Sync</title><style>
:root{--bg:#111;--panel:#1d1d1d;--text:#f2f2f2;--border:#3a3a3a;--accent:#5ad1ff}
*{box-sizing:border-box;font-family:MS Gothic,monospace}
body{margin:0;background:linear-gradient(135deg,#0d0d0d,#1a1a1a);color:var(--text);min-height:100vh}
.wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:20px}
.panel{background:rgba(255,255,255,.05);border:1px solid var(--border);border-radius:18px;padding:18px;backdrop-filter:blur(8px)}
.preview{height:220px;border-radius:20px;border:2px solid rgba(255,255,255,.15);box-shadow:0 0 40px rgba(0,0,0,.4) inset}
.preview:empty:before{content:"preview";display:block;text-align:center;position:relative;top:45%}
.row{margin-top:14px}
label{display:block;margin-bottom:6px;font-weight:700;color:var(--accent)}
input[type=text]{width:100%;background:#101010;border:1px solid var(--border);color:#fff;padding:10px 12px;border-radius:10px;font-family:ui-monospace,monospace;font-size:14px}
input[type=color]{width:100%;height:60px;border:none;background:0 0;cursor:pointer}
.status{margin-top:12px;min-height:24px;font-size:14px;color:#ffb3b3}
.hint{opacity:.8;line-height:1.6}
</style></head><body>
<p class=hint>color(), hsl(), hwb(), lab(), lch(), oklab(), oklch(), rgb(), rgba(), #RGB/#RGBA を相互変換して同期します
<div class=wrap>
<div class=panel>
<div id=preview class=preview></div>
<div class=row>
<label>Color Picker</label>
<input id=picker type=color value="#0af">
</div>
<div class=status id=status></div>
</div>
<div class=panel id=inputs></div>
</div><script>
const formats=[
['hex','#RGB / #RGBA / #RRGGBB / #RRGGBBAA'],
['rgb','rgb()'],
['rgba','rgba()'],
['hsl','hsl()'],
['hwb','hwb()'],
['lab','lab()'],
['lch','lch()'],
['oklab','oklab()'],
['oklch','oklch()'],
['color','color(display-p3 ...)']
],gE=a=>document.getElementById(a),
container=gE('inputs'),
preview=gE('preview'),
picker=gE('picker'),
status=gE('status'),
inputs={};
for(let[k,l]of formats){
const row=document.createElement('div');
row.className='row';
row.innerHTML=`<label>${l}</label><input type=text id="${k}">`;
container.appendChild(row);
inputs[k]=row.querySelector('input')
}
function canvasColor(str){
const ctx=document.createElement('canvas').getContext('2d');
ctx.fillStyle='#000';
ctx.fillStyle=str;
return ctx.fillStyle
}
function validColor(str){
return canvasColor(str)!=='#000000'||str==='#000'||str==='#000000'||(str=str.toLowerCase())==='black'||str==='rgb(0 0 0)'
}
function parseRGB(css){
let d=document.createElement('div');
d.style.color=css;
document.body.appendChild(d);
d.remove(d=getComputedStyle(d).color.match(/\d+(?:\.\d+)?/g).map(Number));
return{r:d[0],g:d[1],b:d[2],a:d[3]??1}
}
function toHex({r,g,b,a=1}){
const hex=[r,g,b].map(v=>Math.round(v).toString(16).padStart(2,'0')).join('');
const aa=Math.round(a*255).toString(16).padStart(2,'0');
return a<1?'#'+hex+aa:'#'+hex;
}
function rgbToHsl(r,g,b){
r/=255;g/=255;b/=255;
const max=Math.max(r,g,b),min=Math.min(r,g,b);
let h,s,l=(max+min)/2;
if(max===min)return {h:0,s:0,l:l*100};
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);break;
case g:h=(b-r)/d+2;break;
case b:h=(r-g)/d+4;break;
}
h*=60;
return {h,s:s*100,l:l*100};
}
function hslToHwb(h,s,l){
s/=100;l/=100;
const v=l+s*Math.min(l,1-l);
const w=(2*l-v)*100;
const b=(1-v)*100;
return {h,w,b};
}
function pivotRgb(n){
n/=255;
return n<=0.04045?n/12.92:((n+0.055)/1.055)**2.4
}
function rgbToXyz(r,g,b){
r=pivotRgb(r);g=pivotRgb(g);b=pivotRgb(b);
return{
x:r*0.4124564+g*0.3575761+b*0.1804375,
y:r*0.2126729+g*0.7151522+b*0.0721750,
z:r*0.0193339+g*0.1191920+b*0.9503041
};
}
function pivotLab(n){
return n>0.008856?Math.cbrt(n):(903.3*n+16)/116;
}
function rgbToLab(r,g,b){
let xyz=rgbToXyz(r,g,b),x=pivotLab(xyz.x/0.95047),y=pivotLab(xyz.y),z=pivotLab(xyz.z/1.08883);
return{l:116*y-16,a:500*(x-y),b:200*(y-z)}
}
function labToLch(l,a,b){
return{l,c:Math.sqrt(a*a+b*b),h:(Math.atan2(b,a)*180/Math.PI+360)%360}
}
function rgbToOklab(r,g,b){
r=pivotRgb(r);g=pivotRgb(g);b=pivotRgb(b);
let l=Math.cbrt(0.4122214708*r+0.5363325363*g+0.0514459929*b),
m=Math.cbrt(0.2119034982*r+0.6806995451*g+0.1073969566*b),
s=Math.cbrt(0.0883024619*r+0.2817188376*g+0.6299787005*b);
return{
l:0.2104542553*l+0.7936177850*m-0.0040720468*s,
a:1.9779984951*l-2.4285922050*m+0.4505937099*s,
b:0.0259040371*l+0.7827717662*m-0.8086757660*s
}
}
function toFns(rgb){
const hex=toHex(rgb),probe=document.createElement('div'),
read=(prop,val)=>{
probe.style.color=val;
return getComputedStyle(probe).color;
};
document.body.appendChild(probe);
probe.style.color=hex;
const hsl=rgbToHsl(rgb.r,rgb.g,rgb.b),
hwb=hslToHwb(hsl.h,hsl.s,hsl.l),
lab=rgbToLab(rgb.r,rgb.g,rgb.b),
lch=labToLch(lab.l,lab.a,lab.b),
oklab=rgbToOklab(rgb.r,rgb.g,rgb.b),
oklch=labToLch(oklab.l,oklab.a,oklab.b),
out={
hex,
rgb:`rgb(${rgb.r} ${rgb.g} ${rgb.b})`,
rgba:`rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${(+rgb.a.toFixed(4))})`,
hsl:`hsl(${hsl.h.toFixed(1)} ${hsl.s.toFixed(1)}% ${hsl.l.toFixed(1)}%)`,
hwb:`hwb(${hwb.h.toFixed(1)} ${hwb.w.toFixed(1)}% ${hwb.b.toFixed(1)}%)`,
lab:`lab(${lab.l.toFixed(3)}% ${lab.a.toFixed(3)} ${lab.b.toFixed(3)})`,
lch:`lch(${lch.l.toFixed(3)}% ${lch.c.toFixed(3)} ${lch.h.toFixed(3)})`,
oklab:`oklab(${oklab.l.toFixed(5)} ${oklab.a.toFixed(5)} ${oklab.b.toFixed(5)})`,
oklch:`oklch(${oklch.l.toFixed(5)} ${oklch.c.toFixed(5)} ${oklch.h.toFixed(3)})`,
color:`color(display-p3 ${(rgb.r/255).toFixed(4)} ${(rgb.g/255).toFixed(4)} ${(rgb.b/255).toFixed(4)})`
};
probe.remove();
return out
}
let lock;
function sync(source,value){
if(lock)return;
lock=1;
try{
if(!validColor(value)){
status.textContent='無効な色です';
return lock=0
}
status.textContent='';
const rgb=parseRGB(value),vals=toFns(rgb);
for(const k in inputs)
if(k!==source)inputs[k].value=vals[k];
if(source in vals)inputs[source].value=value;
preview.style.background=value;
picker.value=vals.hex
}catch(e){status.textContent=e+''}
lock=0
}
for(const k in inputs)inputs[k].oninput=e=>sync(k,e.target.value);
picker.oninput=e=>sync('hex',e.target.value);
sync('hex','#00aaff')
</script>