// Color Match palette — a hand-curated set of calm-but-distinct colors. The board // builder picks a perceptually-spaced subset (see board.js) so two near-identical // shades never land on the same board. Each color is NAMED (drives the aria-label // and the optional colorblind "assist" glyph), and varies in LIGHTNESS as well as // hue so confusable hue pairs (e.g. green/teal) still differ by brightness. // // `assist` is a short letter shown in the corner when the player turns assist on — // kept to 1–2 chars, unique enough to disambiguate the swatch without reading as // a full label. Default play is pure color; assist is opt-in. const RAW = [ { key: 'color-rose', name: 'Rose', hex: '#d76a86', assist: 'Ro' }, { key: 'color-coral', name: 'Coral', hex: '#e07a52', assist: 'Co' }, { key: 'color-amber', name: 'Amber', hex: '#e3a32f', assist: 'Am' }, { key: 'color-gold', name: 'Gold', hex: '#cdb63c', assist: 'Go' }, { key: 'color-lime', name: 'Lime', hex: '#8fb24a', assist: 'Li' }, { key: 'color-green', name: 'Green', hex: '#4a9b5c', assist: 'Gr' }, { key: 'color-teal', name: 'Teal', hex: '#2f9c8e', assist: 'Te' }, { key: 'color-cyan', name: 'Cyan', hex: '#3aa6c4', assist: 'Cy' }, { key: 'color-sky', name: 'Sky', hex: '#5b8fd4', assist: 'Sk' }, { key: 'color-blue', name: 'Blue', hex: '#4f63c6', assist: 'Bl' }, { key: 'color-indigo', name: 'Indigo', hex: '#6a59b2', assist: 'In' }, { key: 'color-violet', name: 'Violet', hex: '#9460ba', assist: 'Vi' }, { key: 'color-plum', name: 'Plum', hex: '#b25a95', assist: 'Pl' }, { key: 'color-brown', name: 'Brown', hex: '#9c6b45', assist: 'Br' }, { key: 'color-sand', name: 'Sand', hex: '#cdb38c', assist: 'Sa' }, { key: 'color-slate', name: 'Slate', hex: '#5d6b78', assist: 'Sl' }, { key: 'color-charcoal', name: 'Charcoal', hex: '#3a4250', assist: 'Ch' }, { key: 'color-cream', name: 'Cream', hex: '#e6dfca', assist: 'Cm' }, ]; function srgbToLin(c) { c /= 255; return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); } function hexToLab(hex) { const r = srgbToLin(parseInt(hex.slice(1, 3), 16)); const g = srgbToLin(parseInt(hex.slice(3, 5), 16)); const b = srgbToLin(parseInt(hex.slice(5, 7), 16)); const X = r * 0.4124 + g * 0.3576 + b * 0.1805; const Y = r * 0.2126 + g * 0.7152 + b * 0.0722; const Z = r * 0.0193 + g * 0.1192 + b * 0.9505; const f = (t) => (t > 0.008856 ? Math.cbrt(t) : 7.787 * t + 16 / 116); const fx = f(X / 0.95047), fy = f(Y / 1), fz = f(Z / 1.08883); return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)]; } // CIE76 ΔE — adequate for "are these two swatches obviously different". export function deltaE(a, b) { return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]); } export const COLORS = RAW.map((c) => ({ ...c, lab: hexToLab(c.hex) })); export const COLOR_BY_KEY = Object.fromEntries(COLORS.map((c) => [c.key, c])); export const COLOR_KEYS = COLORS.map((c) => c.key);