| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
/* ── Typography CSS-var helper ── */ |
| 5 |
var _typoKeys = { |
| 6 |
family:'font-family', weight:'font-weight', style:'font-style', |
| 7 |
transform:'text-transform', decoration:'text-decoration', |
| 8 |
sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', |
| 9 |
lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', |
| 10 |
letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', |
| 11 |
wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' |
| 12 |
}; |
| 13 |
var _typoUnits = { size:'sizeUnit', lineHeight:'lineHeightUnit', letterSpacing:'letterSpacingUnit', wordSpacing:'wordSpacingUnit' }; |
| 14 |
var _typoUnitDefaults = { size:'px', lineHeight:'', letterSpacing:'px', wordSpacing:'px' }; |
| 15 |
function typoCssVarsForEl(el, obj, prefix) { |
| 16 |
if (!obj || typeof obj !== 'object') return; |
| 17 |
Object.keys(_typoKeys).forEach(function (k) { |
| 18 |
var v = obj[k]; if (v === undefined || v === '') return; |
| 19 |
var prop = _typoKeys[k]; |
| 20 |
var base = k.replace(/Desktop|Tablet|Mobile/, ''); |
| 21 |
var uKey = _typoUnits[base]; |
| 22 |
if (uKey && typeof v === 'number') v = v + (obj[uKey] || _typoUnitDefaults[base] || ''); |
| 23 |
el.style.setProperty(prefix + prop, v); |
| 24 |
}); |
| 25 |
} |
| 26 |
|
| 27 |
function clamp(n, lo, hi) { return Math.max(lo, Math.min(hi, n)); } |
| 28 |
|
| 29 |
function hexToRgb(hex) { |
| 30 |
hex = String(hex || '').replace(/^#/, ''); |
| 31 |
if (hex.length === 3) hex = hex.split('').map(function(c){ return c+c; }).join(''); |
| 32 |
if (hex.length !== 6 || /[^0-9a-fA-F]/.test(hex)) return null; |
| 33 |
return { r: parseInt(hex.slice(0,2),16), g: parseInt(hex.slice(2,4),16), b: parseInt(hex.slice(4,6),16) }; |
| 34 |
} |
| 35 |
|
| 36 |
function rgbToHex(r,g,b) { |
| 37 |
return '#' + [r,g,b].map(function(x){ return clamp(Math.round(x),0,255).toString(16).padStart(2,'0'); }).join(''); |
| 38 |
} |
| 39 |
|
| 40 |
function rgbToHsl(r,g,b) { |
| 41 |
r/=255; g/=255; b/=255; |
| 42 |
var max=Math.max(r,g,b), min=Math.min(r,g,b), l=(max+min)/2, h=0, s=0; |
| 43 |
if (max!==min) { |
| 44 |
var d=max-min; |
| 45 |
s = l>0.5 ? d/(2-max-min) : d/(max+min); |
| 46 |
switch(max) { |
| 47 |
case r: h=((g-b)/d + (g<b?6:0))/6; break; |
| 48 |
case g: h=((b-r)/d + 2)/6; break; |
| 49 |
default: h=((r-g)/d + 4)/6; |
| 50 |
} |
| 51 |
} |
| 52 |
return { h: Math.round(h*360), s: Math.round(s*100), l: Math.round(l*100) }; |
| 53 |
} |
| 54 |
|
| 55 |
function hslToRgb(h,s,l) { |
| 56 |
s/=100; l/=100; h/=360; |
| 57 |
function hue2rgb(p,q,t){ if(t<0)t+=1; if(t>1)t-=1; if(t<1/6)return p+(q-p)*6*t; if(t<1/2)return q; if(t<2/3)return p+(q-p)*(2/3-t)*6; return p; } |
| 58 |
var r,g,b; |
| 59 |
if (s===0) { r=g=b=l; } else { |
| 60 |
var q2=l<0.5?l*(1+s):l+s-l*s, p2=2*l-q2; |
| 61 |
r=hue2rgb(p2,q2,h+1/3); g=hue2rgb(p2,q2,h); b=hue2rgb(p2,q2,h-1/3); |
| 62 |
} |
| 63 |
return { r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }; |
| 64 |
} |
| 65 |
|
| 66 |
function initColorConverter(app) { |
| 67 |
var opts = {}; |
| 68 |
try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch(e) {} |
| 69 |
|
| 70 |
var cardBg = opts.cardBg || '#ffffff'; |
| 71 |
var swatchBorder = opts.swatchBorder || '#e5e7eb'; |
| 72 |
var inputBorder = opts.inputBorderColor || '#e5e7eb'; |
| 73 |
var copyBg = opts.copyBg || '#f3f4f6'; |
| 74 |
var copyColor = opts.copyColor || '#374151'; |
| 75 |
var labelColor = opts.labelColor || '#374151'; |
| 76 |
var titleColor = opts.titleColor || '#1f2937'; |
| 77 |
var subtitleColor= opts.subtitleColor || '#6b7280'; |
| 78 |
var sectionBg = opts.sectionBg || ''; |
| 79 |
var maxWidth = opts.maxWidth || 540; |
| 80 |
var cardRadius = opts.cardRadius || 16; |
| 81 |
var inputRadius = opts.inputRadius || 8; |
| 82 |
var titleSize = opts.titleSize || 28; |
| 83 |
var swatchHeight = opts.swatchHeight || 120; |
| 84 |
var padTop = opts.paddingTop || 60; |
| 85 |
var padBot = opts.paddingBottom || 60; |
| 86 |
var showTitle = opts.showTitle !== false; |
| 87 |
var showSub = opts.showSubtitle !== false; |
| 88 |
var showSwatch = opts.showSwatchLarge !== false; |
| 89 |
var showCopy = opts.showCopyBtns !== false; |
| 90 |
|
| 91 |
var currentHex = opts.defaultHex || '#6c3fb5'; |
| 92 |
if (!hexToRgb(currentHex)) currentHex = '#6c3fb5'; |
| 93 |
|
| 94 |
// Outer |
| 95 |
app.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'; |
| 96 |
if (sectionBg) app.style.background = sectionBg; |
| 97 |
app.style.paddingTop = padTop + 'px'; |
| 98 |
app.style.paddingBottom = padBot + 'px'; |
| 99 |
|
| 100 |
typoCssVarsForEl(app, opts.typoTitle, '--bkbg-ccv-tt-'); |
| 101 |
typoCssVarsForEl(app, opts.typoSubtitle, '--bkbg-ccv-st-'); |
| 102 |
|
| 103 |
var card = document.createElement('div'); |
| 104 |
card.className = 'bkbg-clr-card'; |
| 105 |
card.style.cssText = 'background:' + cardBg + ';border-radius:' + cardRadius + 'px;padding:36px 32px;max-width:' + maxWidth + 'px;margin:0 auto;'; |
| 106 |
app.appendChild(card); |
| 107 |
|
| 108 |
// Header |
| 109 |
if (showTitle && opts.title) { |
| 110 |
var titleEl = document.createElement('div'); |
| 111 |
titleEl.className = 'bkbg-ccv-title'; |
| 112 |
titleEl.style.cssText = 'color:' + titleColor + ';margin-bottom:6px;'; |
| 113 |
titleEl.textContent = opts.title; |
| 114 |
card.appendChild(titleEl); |
| 115 |
} |
| 116 |
if (showSub && opts.subtitle) { |
| 117 |
var subEl = document.createElement('div'); |
| 118 |
subEl.className = 'bkbg-ccv-subtitle'; |
| 119 |
subEl.style.cssText = 'color:' + subtitleColor + ';margin-bottom:20px;'; |
| 120 |
subEl.textContent = opts.subtitle; |
| 121 |
card.appendChild(subEl); |
| 122 |
} |
| 123 |
|
| 124 |
// Swatch |
| 125 |
var swatch; |
| 126 |
if (showSwatch) { |
| 127 |
swatch = document.createElement('div'); |
| 128 |
swatch.className = 'bkbg-clr-swatch'; |
| 129 |
swatch.style.cssText = 'height:' + swatchHeight + 'px;border-radius:' + inputRadius + 'px;border:2px solid ' + swatchBorder + ';margin-bottom:24px;background:' + currentHex + ';'; |
| 130 |
card.appendChild(swatch); |
| 131 |
} |
| 132 |
|
| 133 |
function makeCopyBtn(getText) { |
| 134 |
if (!showCopy) return null; |
| 135 |
var btn = document.createElement('button'); |
| 136 |
btn.className = 'bkbg-clr-copy-btn'; |
| 137 |
btn.style.background = copyBg; |
| 138 |
btn.style.color = copyColor; |
| 139 |
btn.textContent = 'Copy'; |
| 140 |
btn.addEventListener('click', function() { |
| 141 |
try { navigator.clipboard.writeText(getText()); } catch(e) {} |
| 142 |
btn.textContent = 'Copied!'; |
| 143 |
btn.classList.add('copied'); |
| 144 |
setTimeout(function(){ btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1500); |
| 145 |
}); |
| 146 |
return btn; |
| 147 |
} |
| 148 |
|
| 149 |
function makeInput(opts2) { |
| 150 |
var inp = document.createElement('input'); |
| 151 |
inp.className = 'bkbg-clr-input'; |
| 152 |
inp.type = opts2.type || 'text'; |
| 153 |
if (opts2.min !== undefined) { inp.min = opts2.min; inp.max = opts2.max; } |
| 154 |
inp.style.borderRadius = inputRadius + 'px'; |
| 155 |
inp.style.borderColor = inputBorder; |
| 156 |
return inp; |
| 157 |
} |
| 158 |
|
| 159 |
function makeLabel(txt, isSmall) { |
| 160 |
var lbl = document.createElement('label'); |
| 161 |
lbl.style.display = 'block'; |
| 162 |
lbl.style.fontWeight = '600'; |
| 163 |
lbl.style.color = labelColor; |
| 164 |
lbl.style.marginBottom = '5px'; |
| 165 |
if (isSmall) { |
| 166 |
lbl.style.fontSize = '11px'; |
| 167 |
} else { |
| 168 |
lbl.style.fontSize = '12px'; |
| 169 |
lbl.style.textTransform = 'uppercase'; |
| 170 |
lbl.style.letterSpacing = '.04em'; |
| 171 |
} |
| 172 |
lbl.textContent = txt; |
| 173 |
return lbl; |
| 174 |
} |
| 175 |
|
| 176 |
var hexInput, rInp, gInp, bInp, hInp, sInp, lInp; |
| 177 |
|
| 178 |
// HEX section |
| 179 |
var hexSec = document.createElement('div'); |
| 180 |
hexSec.className = 'bkbg-clr-section'; |
| 181 |
hexSec.appendChild(makeLabel('HEX')); |
| 182 |
hexInput = makeInput({ type: 'text' }); |
| 183 |
hexSec.appendChild(hexInput); |
| 184 |
if (showCopy) hexSec.appendChild(makeCopyBtn(function(){ return hexInput.value; })); |
| 185 |
card.appendChild(hexSec); |
| 186 |
|
| 187 |
// RGB section |
| 188 |
var rgbSec = document.createElement('div'); |
| 189 |
rgbSec.className = 'bkbg-clr-section'; |
| 190 |
rgbSec.appendChild(makeLabel('RGB')); |
| 191 |
var rgbGrid = document.createElement('div'); |
| 192 |
rgbGrid.className = 'bkbg-clr-rgb-grid'; |
| 193 |
[['R', 'r'], ['G', 'g'], ['B', 'b']].forEach(function(pair) { |
| 194 |
var col = document.createElement('div'); |
| 195 |
col.appendChild(makeLabel(pair[0], true)); |
| 196 |
var inp = makeInput({ type:'number', min:0, max:255 }); |
| 197 |
if (pair[1]==='r') rInp=inp; |
| 198 |
else if (pair[1]==='g') gInp=inp; |
| 199 |
else bInp=inp; |
| 200 |
col.appendChild(inp); |
| 201 |
rgbGrid.appendChild(col); |
| 202 |
}); |
| 203 |
rgbSec.appendChild(rgbGrid); |
| 204 |
if (showCopy) rgbSec.appendChild(makeCopyBtn(function(){ return 'rgb(' + rInp.value + ', ' + gInp.value + ', ' + bInp.value + ')'; })); |
| 205 |
card.appendChild(rgbSec); |
| 206 |
|
| 207 |
// HSL section |
| 208 |
var hslSec = document.createElement('div'); |
| 209 |
hslSec.appendChild(makeLabel('HSL')); |
| 210 |
var hslGrid = document.createElement('div'); |
| 211 |
hslGrid.className = 'bkbg-clr-rgb-grid'; |
| 212 |
[['H°', 'h', 360], ['S%', 's', 100], ['L%', 'l', 100]].forEach(function(t) { |
| 213 |
var col = document.createElement('div'); |
| 214 |
col.appendChild(makeLabel(t[0], true)); |
| 215 |
var inp = makeInput({ type:'number', min:0, max:t[2] }); |
| 216 |
if (t[1]==='h') hInp=inp; |
| 217 |
else if (t[1]==='s') sInp=inp; |
| 218 |
else lInp=inp; |
| 219 |
col.appendChild(inp); |
| 220 |
hslGrid.appendChild(col); |
| 221 |
}); |
| 222 |
hslSec.appendChild(hslGrid); |
| 223 |
if (showCopy) hslSec.appendChild(makeCopyBtn(function(){ return 'hsl(' + hInp.value + ', ' + sInp.value + '%, ' + lInp.value + '%)'; })); |
| 224 |
card.appendChild(hslSec); |
| 225 |
|
| 226 |
var updating = false; |
| 227 |
|
| 228 |
function applyHex(hex) { |
| 229 |
if (updating) return; |
| 230 |
var rgb = hexToRgb(hex); if (!rgb) return; |
| 231 |
var hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); |
| 232 |
updating = true; |
| 233 |
hexInput.value = hex; |
| 234 |
rInp.value = rgb.r; gInp.value = rgb.g; bInp.value = rgb.b; |
| 235 |
hInp.value = hsl.h; sInp.value = hsl.s; lInp.value = hsl.l; |
| 236 |
if (swatch) swatch.style.background = hex; |
| 237 |
updating = false; |
| 238 |
} |
| 239 |
|
| 240 |
hexInput.addEventListener('input', function() { |
| 241 |
var v = hexInput.value.trim(); |
| 242 |
if (!v.startsWith('#')) v = '#' + v; |
| 243 |
if (hexToRgb(v)) applyHex(v); |
| 244 |
}); |
| 245 |
|
| 246 |
function fromRgb() { |
| 247 |
if (updating) return; |
| 248 |
var hex = rgbToHex(parseInt(rInp.value)||0, parseInt(gInp.value)||0, parseInt(bInp.value)||0); |
| 249 |
applyHex(hex); |
| 250 |
} |
| 251 |
rInp.addEventListener('input', fromRgb); |
| 252 |
gInp.addEventListener('input', fromRgb); |
| 253 |
bInp.addEventListener('input', fromRgb); |
| 254 |
|
| 255 |
function fromHsl() { |
| 256 |
if (updating) return; |
| 257 |
var rgb = hslToRgb(parseInt(hInp.value)||0, parseInt(sInp.value)||0, parseInt(lInp.value)||0); |
| 258 |
applyHex(rgbToHex(rgb.r, rgb.g, rgb.b)); |
| 259 |
} |
| 260 |
hInp.addEventListener('input', fromHsl); |
| 261 |
sInp.addEventListener('input', fromHsl); |
| 262 |
lInp.addEventListener('input', fromHsl); |
| 263 |
|
| 264 |
applyHex(currentHex); |
| 265 |
} |
| 266 |
|
| 267 |
document.querySelectorAll('.bkbg-clr-app').forEach(initColorConverter); |
| 268 |
})(); |
| 269 |
|