UNPKG

colorizr

Version:
1,725 lines (1,667 loc) 65.6 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // src/converters/index.ts var converters_exports = {}; __export(converters_exports, { hex2hsl: () => hex2hsl, hex2oklab: () => hex2oklab, hex2oklch: () => hex2oklch, hex2rgb: () => hex2rgb, hsl2hex: () => hsl2hex, hsl2oklab: () => hsl2oklab, hsl2oklch: () => hsl2oklch, hsl2rgb: () => hsl2rgb, oklab2hex: () => oklab2hex, oklab2hsl: () => oklab2hsl, oklab2oklch: () => oklab2oklch, oklab2rgb: () => oklab2rgb, oklch2hex: () => oklch2hex, oklch2hsl: () => oklch2hsl, oklch2oklab: () => oklch2oklab, oklch2rgb: () => oklch2rgb, rgb2hex: () => rgb2hex, rgb2hsl: () => rgb2hsl, rgb2oklab: () => rgb2oklab, rgb2oklch: () => rgb2oklch }); // src/modules/constants.ts var COLOR_KEYS = { hsl: ["h", "s", "l"], oklab: ["l", "a", "b"], oklch: ["l", "c", "h"], rgb: ["r", "g", "b"] }; var CLMS_TO_OKLAB = [ [0.210454268309314, 0.7936177747023054, -0.0040720430116193], [1.9779985324311684, -2.42859224204858, 0.450593709617411], [0.0259040424655478, 0.7827717124575296, -0.8086757549230774] ]; var DEG2RAD = Math.PI / 180; var LMS_TO_LRGB = [ [4.0767416621, -3.3077115913, 0.2309699292], [-1.2684380046, 2.6097574011, -0.3413193965], [-0.0041960863, -0.7034186147, 1.707614701] ]; var LRGB_TO_LMS = [ [0.4122214708, 0.5363325363, 0.0514459929], [0.2119034982, 0.6806995451, 0.1073969566], [0.0883024619, 0.2817188376, 0.6299787005] ]; var OKLAB_TO_CLMS = [ [1, 0.3963377773761749, 0.2158037573099136], [1, -0.1055613458156586, -0.0638541728258133], [1, -0.0894841775298119, -1.2914855480194092] ]; var P3_TO_SRGB = [ [1.22494017628056, -0.22494017628055996, 0], [-0.04205695470968812, 1.0420569547096883, 0], [-0.019637554590334422, -0.07863604555063179, 1.0982736001409665] ]; var P3_TO_XYZ = [ [0.4865709486482162, 0.26566769316909306, 0.1982172852343625], [0.22897456406974884, 0.6917385218365064, 0.079286914093745], [0, 0.04511338185890264, 1.043944368900976] ]; var SRGB_TO_P3 = [ [0.8224270476, 0.1775729524, 0], [0.0331008087, 0.9668991913, 0], [0.0170720188, 0.0723477973, 0.9105801839] ]; var XYZ_TO_SRGB = [ [3.2409699419045226, -1.537383177570094, -0.4986107602930034], [-0.9692436362808796, 1.8759675015077202, 0.0415550574071756], [0.0556300796969936, -0.2039769588889765, 1.0569715142428786] ]; var GAMUT_EPSILON = 1e-6; var PRECISION = 5; var RAD2DEG = 180 / Math.PI; var MESSAGES = { alpha: "alpha must be a number between 0 and 1", alphaAdjustment: "alpha must be a number between -1 and 1", amount: "amount must be a number between 0 and 100", colorRequired: "color is required", degreesRange: "degrees must be a number between -360 and 360", hueArgs: "point, chroma and h are required", hueRange: "hue must be a number between 0 and 360", input: "input is required", inputHex: "input is required and must be a hex", inputNumber: "input is required and must be a number", inputString: "input is required and must be a string", invalid: "invalid input", invalidColor: "invalid color", invalidCSS: "invalid CSS string", invalidHex: "invalid hex", invalidKey: "invalid key", invalidModel: "invalid model", invalidRange: "color value out of range", left: "left is required and must be a string", lightnessRange: "lightness must be a number between 0 and 1", options: "invalid options", paletteSize: "palette size must be at least 2", ratioRange: "ratio must be a number between 0 and 1", right: "right is required and must be a string", threshold: "threshold must be a number between 0 and 255", thresholdNormalized: "threshold must be a number between 0 and 1" }; var MONOCHROMATIC_LIGHTNESS_MAX = 80; // src/modules/invariant.ts function invariant(condition, message) { if (condition) { return; } if (process.env.NODE_ENV !== "production" && message === void 0) { throw new Error("invariant requires an error message argument"); } const error = !message ? new Error( "Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings." ) : new Error(message); error.name = "colorizr"; throw error; } // src/modules/css-colors.ts var cssColors = { aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aqua: "#00ffff", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", black: "#000000", blanchedalmond: "#ffebcd", blue: "#0000ff", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed", cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkkhaki: "#bdb76b", darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dimgrey: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222", floralwhite: "#fffaf0", forestgreen: "#228b22", fuchsia: "#ff00ff", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", gold: "#ffd700", goldenrod: "#daa520", gray: "#808080", grey: "#808080", green: "#008000", greenyellow: "#adff2f", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", lavender: "#e6e6fa", lavenderblush: "#fff0f5", lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrodyellow: "#fafad2", lightgray: "#d3d3d3", lightgreen: "#90ee90", lightgrey: "#d3d3d3", lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightslategrey: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", lime: "#00ff00", limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", maroon: "#800000", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db", mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa", mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", navy: "#000080", oldlace: "#fdf5e6", olive: "#808000", olivedrab: "#6b8e23", orange: "#ffa500", orangered: "#ff4500", orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f", pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", purple: "#800080", rebeccapurple: "#663399", red: "#ff0000", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072", sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", silver: "#c0c0c0", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", slategrey: "#708090", snow: "#fffafa", springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", teal: "#008080", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3", white: "#ffffff", whitesmoke: "#f5f5f5", yellow: "#ffff00", yellowgreen: "#9acd32" }; // src/modules/validators.ts var hexRegex = /^#(?:[\da-f]{3,4}|[\da-f]{6,8})$/i; function hasValidMatches(input) { return Array.isArray(input) && input.length === 6; } function isHex(input) { if (!isString(input)) { return false; } return hexRegex.test(input); } function isHSL(input) { if (!isPlainObject(input)) { return false; } const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "h") { return value >= 0 && value <= 360; } if (key === "alpha") { return value >= 0 && value <= 1; } return COLOR_KEYS.hsl.includes(key) && value >= 0 && value <= 100; }); } function isLAB(input) { if (!isPlainObject(input)) { return false; } const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "l") { return value >= 0 && value <= 100; } if (key === "alpha") { return value >= 0 && value <= 1; } return COLOR_KEYS.oklab.includes(key) && value >= -1 && value <= 1; }); } function isLCH(input) { if (!isPlainObject(input)) { return false; } const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "l") { return value >= 0 && value <= 100; } if (key === "alpha") { return value >= 0 && value <= 1; } return COLOR_KEYS.oklch.includes(key) && value >= 0 && value <= (key === "h" ? 360 : 1); }); } function isNamedColor(input) { return isString(input) && Object.keys(cssColors).includes(input.toLowerCase()); } function isNumber(input) { return typeof input === "number" && !Number.isNaN(input); } function isNumberInRange(input, min, max) { return isNumber(input) && input >= min && input <= max; } function isPlainObject(input) { if (!input) { return false; } const { toString } = Object.prototype; const prototype = Object.getPrototypeOf(input); return toString.call(input) === "[object Object]" && (prototype === null || prototype === Object.getPrototypeOf({})); } function isRGB(input) { if (!isPlainObject(input)) { return false; } const entries = Object.entries(input); return !!entries.length && entries.every(([key, value]) => { if (key === "alpha") { return value >= 0 && value <= 1; } return COLOR_KEYS.rgb.includes(key) && value >= 0 && value <= 255; }); } function isString(input, validate = true) { const isValid = typeof input === "string"; if (validate) { return isValid && !!input.trim().length; } return isValid; } function isValidColorModel(input) { return isHSL(input) || isRGB(input) || isLAB(input) || isLCH(input); } // src/format-hex.ts function formatHex(input) { invariant(isString(input), MESSAGES.inputString); let color = input.startsWith("#") ? input.slice(1) : input; invariant(isHex(`#${color}`), MESSAGES.inputHex); if (color.length === 3 || color.length === 4) { const values = [...color]; color = ""; values.forEach((d) => { color += `${d}${d}`; }); } const hex = `#${color}`; invariant(isHex(hex), MESSAGES.invalidHex); return hex; } // src/modules/utils.ts function clamp(value, min = 0, max = 100) { return Math.min(Math.max(value, min), max); } function constrainDegrees(input, amount) { invariant(isNumber(input), MESSAGES.inputNumber); return ((input + amount) % 360 + 360) % 360; } function normalizeOkLightness(color) { if (color.l > 1) { return { ...color, l: parseFloat((color.l / 100).toPrecision(15)) }; } return color; } function parseInput(input, model) { const keys = COLOR_KEYS[model]; const validator = { hsl: isHSL, oklab: isLAB, oklch: isLCH, rgb: isRGB }; invariant(isPlainObject(input) || Array.isArray(input), MESSAGES.invalid); const value = Array.isArray(input) ? { [keys[0]]: input[0], [keys[1]]: input[1], [keys[2]]: input[2] } : input; invariant(validator[model](value), `${MESSAGES.invalidColor}: ${model}`); return value; } function restrictValues(input, precision, forcePrecision = true) { if (precision == null) { return input; } const output = new Map(Object.entries(input)); for (const [key, value] of output.entries()) { output.set(key, round(value, precision, forcePrecision)); } return Object.fromEntries(output); } function round(input, precision = 2, forcePrecision = true) { if (!isNumber(input) || input === 0) { return 0; } if (forcePrecision) { const factor2 = 10 ** precision; return Math.round(input * factor2) / factor2; } const integer = Math.trunc(input); let digits = 0; if (integer) { digits = Math.floor(Math.log10(Math.abs(integer))) + 1; } const factor = 10 ** (precision - digits); return Math.floor(input * factor + 0.5) / factor; } function warn(message) { if (process.env.NODE_ENV !== "production") { console.warn(`[colorizr] ${message}`); } } var STEP_KEYS = { 3: [100, 500, 900], 4: [100, 400, 600, 900], 5: [100, 300, 500, 700, 900], 6: [100, 200, 400, 600, 800, 900], 7: [100, 200, 400, 500, 600, 800, 900], 8: [100, 200, 300, 500, 600, 700, 800, 900], 9: [100, 200, 300, 400, 500, 600, 700, 800, 900], 10: [50, 100, 200, 300, 400, 500, 600, 700, 800, 900], 11: [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], 12: [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 950], 13: [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950], 14: [50, 100, 150, 200, 250, 300, 400, 500, 600, 700, 800, 850, 900, 950], 15: [50, 100, 150, 200, 250, 300, 400, 500, 600, 700, 750, 800, 850, 900, 950], 16: [50, 100, 150, 200, 250, 300, 350, 400, 500, 600, 700, 750, 800, 850, 900, 950], 17: [50, 100, 150, 200, 250, 300, 350, 400, 500, 600, 650, 700, 750, 800, 850, 900, 950], 18: [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 600, 650, 700, 750, 800, 850, 900, 950], 19: [ 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950 ], 20: [ 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1e3 ] }; function getScaleStepKeys(steps) { const value = clamp(Math.round(steps), 3, 20); return STEP_KEYS[value]; } // src/modules/alpha.ts function addAlpha(input, alpha) { invariant(isValidColorModel(input), MESSAGES.invalid); const value = normalizeAlpha(alpha); if (value === void 0 || value === 1) { return input; } return { ...input, alpha: value }; } function addAlphaToHex(input, alpha) { invariant(isHex(input), MESSAGES.inputHex); invariant(isNumberInRange(alpha, 0, 1), MESSAGES.alpha); if (alpha >= 1) { return removeAlphaFromHex(input); } return `${removeAlphaFromHex(input)}${convertAlphaToHex(alpha)}`; } function convertAlphaToHex(input) { invariant(isNumber(input), MESSAGES.inputNumber); const alpha = normalizeAlpha(input); return Math.round(alpha * 255).toString(16).padStart(2, "0"); } function extractAlpha(input) { if (Array.isArray(input)) { return void 0; } return input.alpha; } function extractAlphaFromHex(input) { invariant(isHex(input), MESSAGES.inputString); const alpha = input.substring(7, 9); if (!alpha) { return 1; } return round(parseInt(alpha, 16) / 255); } function normalizeAlpha(value) { if (value === void 0) { return void 0; } return Math.min(1, Math.max(0, value > 1 ? value / 100 : value)); } function removeAlphaFromHex(input) { invariant(isHex(input), MESSAGES.inputHex); if (input.length === 5) { return input.substring(0, 4); } return input.substring(0, 7); } // src/converters/hex2rgb.ts function hex2rgb(input) { invariant(isHex(input), MESSAGES.inputHex); const hex = formatHex(input).slice(1); const alpha = extractAlphaFromHex(input); return addAlpha( { r: parseInt(hex.charAt(0) + hex.charAt(1), 16), g: parseInt(hex.charAt(2) + hex.charAt(3), 16), b: parseInt(hex.charAt(4) + hex.charAt(5), 16) }, alpha ); } // src/converters/rgb2hsl.ts function rgb2hsl(input) { const value = parseInput(input, "rgb"); const alpha = extractAlpha(input); const rLimit = clamp(value.r, 0, 255) / 255; const gLimit = clamp(value.g, 0, 255) / 255; const bLimit = clamp(value.b, 0, 255) / 255; const min = Math.min(rLimit, gLimit, bLimit); const max = Math.max(rLimit, gLimit, bLimit); const delta = max - min; let h = 0; let s; const l = (max + min) / 2; let rate; switch (max) { case rLimit: rate = !delta ? 0 : (gLimit - bLimit) / delta; h = 60 * rate; break; case gLimit: rate = (bLimit - rLimit) / delta; h = 60 * rate + 120; break; case bLimit: rate = (rLimit - gLimit) / delta; h = 60 * rate + 240; break; /* v8 ignore next 2 -- @preserve */ default: break; } if (h < 0) { h = 360 + h; } if (min === max) { s = 0; } else { s = l < 0.5 ? delta / (2 * l) : delta / (2 - 2 * l); } return addAlpha( { h: Math.abs(+(h % 360).toFixed(2)), s: +(s * 100).toFixed(2), l: +(l * 100).toFixed(2) }, alpha ); } // src/converters/hex2hsl.ts function hex2hsl(input) { invariant(isHex(input), MESSAGES.inputHex); const alpha = extractAlphaFromHex(input); return addAlpha(rgb2hsl(hex2rgb(input)), alpha); } // src/modules/gamma.ts function srgbGammaDecode(input) { const abs = Math.abs(input); if (abs < 0.04045) { return input / 12.92; } return (Math.sign(input) || 1) * ((abs + 0.055) / 1.055) ** 2.4; } function srgbGammaEncode(input) { const abs = Math.abs(input); const sign = input < 0 ? -1 : 1; if (abs > 31308e-7) { return sign * (abs ** (1 / 2.4) * 1.055 - 0.055); } return input * 12.92; } // src/converters/rgb2oklab.ts var { cbrt } = Math; function rgb2oklab(input, precision) { const value = parseInput(input, "rgb"); const alpha = extractAlpha(input); const [lr, lg, lb] = [ srgbGammaDecode(value.r / 255), srgbGammaDecode(value.g / 255), srgbGammaDecode(value.b / 255) ]; const l = cbrt(LRGB_TO_LMS[0][0] * lr + LRGB_TO_LMS[0][1] * lg + LRGB_TO_LMS[0][2] * lb); const m = cbrt(LRGB_TO_LMS[1][0] * lr + LRGB_TO_LMS[1][1] * lg + LRGB_TO_LMS[1][2] * lb); const s = cbrt(LRGB_TO_LMS[2][0] * lr + LRGB_TO_LMS[2][1] * lg + LRGB_TO_LMS[2][2] * lb); const lab = restrictValues( { l: CLMS_TO_OKLAB[0][0] * l + CLMS_TO_OKLAB[0][1] * m + CLMS_TO_OKLAB[0][2] * s, a: CLMS_TO_OKLAB[1][0] * l + CLMS_TO_OKLAB[1][1] * m + CLMS_TO_OKLAB[1][2] * s, b: CLMS_TO_OKLAB[2][0] * l + CLMS_TO_OKLAB[2][1] * m + CLMS_TO_OKLAB[2][2] * s }, precision ); return addAlpha(lab, alpha); } // src/converters/hex2oklab.ts function hex2oklab(input, precision) { invariant(isHex(input), MESSAGES.inputHex); const alpha = extractAlphaFromHex(input); return addAlpha(rgb2oklab(hex2rgb(input), precision), alpha); } // src/converters/oklab2oklch.ts var { atan2, sqrt } = Math; function oklab2oklch(input, precision) { const { l, a, b } = parseInput(input, "oklab"); const alpha = extractAlpha(input); const c = sqrt(a ** 2 + b ** 2); let h = (atan2(b, a) * RAD2DEG + 360) % 360; if (c < 1e-6) { h = 0; } return addAlpha(restrictValues({ l, c, h }, precision), alpha); } // src/converters/rgb2oklch.ts function rgb2oklch(input, precision) { const value = parseInput(input, "rgb"); const alpha = extractAlpha(input); return addAlpha(oklab2oklch(rgb2oklab(value), precision), alpha); } // src/converters/hex2oklch.ts function hex2oklch(input, precision) { invariant(isHex(input), MESSAGES.inputHex); const alpha = extractAlphaFromHex(input); return addAlpha(rgb2oklch(hex2rgb(input), precision), alpha); } // src/modules/hue2rgb.ts function hue2rgb(point, chroma2, h) { invariant(isNumber(point) && isNumber(chroma2) && isNumber(h), MESSAGES.hueArgs); let hue = h; if (hue < 0) { hue += 1; } if (hue > 1) { hue -= 1; } if (hue < 1 / 6) { return point + (chroma2 - point) * 6 * hue; } if (hue < 1 / 2) { return chroma2; } if (hue < 2 / 3) { return point + (chroma2 - point) * (2 / 3 - hue) * 6; } return point; } // src/converters/hsl2rgb.ts function hsl2rgb(input) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); const h = value.h / 360; const s = value.s / 100; const l = value.l / 100; let r; let g; let b; let point; let chroma2; if (s < 1e-4) { r = l; g = l; b = l; } else { chroma2 = l < 0.5 ? l * (1 + s) : l + s - l * s; point = 2 * l - chroma2; r = hue2rgb(point, chroma2, h + 1 / 3); g = hue2rgb(point, chroma2, h); b = hue2rgb(point, chroma2, h - 1 / 3); } return addAlpha( { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) }, alpha ); } // src/converters/rgb2hex.ts function rgb2hex(input) { const rgb = parseInput(input, "rgb"); const alpha = extractAlpha(input); const hex = `#${[rgb.r, rgb.g, rgb.b].map((d) => `0${Math.floor(d).toString(16)}`.slice(-2)).join("")}`; return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } // src/converters/hsl2hex.ts function hsl2hex(input) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); const hex = rgb2hex(hsl2rgb(value)); return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } // src/converters/hsl2oklab.ts function hsl2oklab(input, precision) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); return addAlpha(rgb2oklab(hsl2rgb(value), precision), alpha); } // src/converters/hsl2oklch.ts function hsl2oklch(input, precision) { const value = parseInput(input, "hsl"); const alpha = extractAlpha(input); return addAlpha(rgb2oklch(hsl2rgb(value), precision), alpha); } // src/converters/oklab2rgb.ts function oklab2rgb(input, precision = 0) { const { l: L, a: A, b: B } = parseInput(input, "oklab"); const alpha = extractAlpha(input); const l = (OKLAB_TO_CLMS[0][0] * L + OKLAB_TO_CLMS[0][1] * A + OKLAB_TO_CLMS[0][2] * B) ** 3; const m = (OKLAB_TO_CLMS[1][0] * L + OKLAB_TO_CLMS[1][1] * A + OKLAB_TO_CLMS[1][2] * B) ** 3; const s = (OKLAB_TO_CLMS[2][0] * L + OKLAB_TO_CLMS[2][1] * A + OKLAB_TO_CLMS[2][2] * B) ** 3; const r = 255 * srgbGammaEncode(LMS_TO_LRGB[0][0] * l + LMS_TO_LRGB[0][1] * m + LMS_TO_LRGB[0][2] * s); const g = 255 * srgbGammaEncode(LMS_TO_LRGB[1][0] * l + LMS_TO_LRGB[1][1] * m + LMS_TO_LRGB[1][2] * s); const b = 255 * srgbGammaEncode(LMS_TO_LRGB[2][0] * l + LMS_TO_LRGB[2][1] * m + LMS_TO_LRGB[2][2] * s); return addAlpha( { r: clamp(round(r, precision), 0, 255), g: clamp(round(g, precision), 0, 255), b: clamp(round(b, precision), 0, 255) }, alpha ); } // src/converters/oklab2hex.ts function oklab2hex(input) { const value = parseInput(input, "oklab"); const alpha = extractAlpha(input); const hex = rgb2hex(oklab2rgb(value)); return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } // src/converters/oklab2hsl.ts function oklab2hsl(input) { const value = parseInput(input, "oklab"); const alpha = extractAlpha(input); return addAlpha(rgb2hsl(oklab2rgb(value)), alpha); } // src/converters/oklch2oklab.ts var { sin, cos } = Math; function oklch2oklab(input, precision) { let { l, c, h } = parseInput(input, "oklch"); const alpha = extractAlpha(input); if (Number.isNaN(h) || h < 0) { h = 0; } return addAlpha( restrictValues({ l, a: c * cos(h * DEG2RAD), b: c * sin(h * DEG2RAD) }, precision), alpha ); } // src/converters/oklch2rgb.ts function oklch2rgb(input, precision = 0) { const value = parseInput(input, "oklch"); const alpha = extractAlpha(input); return addAlpha(oklab2rgb(oklch2oklab(value), precision), alpha); } // src/converters/oklch2hex.ts function oklch2hex(input) { const value = parseInput(input, "oklch"); const alpha = extractAlpha(input); const hex = rgb2hex(oklch2rgb(value)); return alpha !== void 0 && alpha < 1 ? addAlphaToHex(hex, alpha) : hex; } // src/converters/oklch2hsl.ts function oklch2hsl(input) { const value = parseInput(input, "oklch"); const alpha = extractAlpha(input); return addAlpha(rgb2hsl(oklch2rgb(value)), alpha); } // src/format-css.ts function getColorModel(input) { if (isHex(input) || isNamedColor(input)) { return "hex"; } if (isHSL(input)) { return "hsl"; } if (isLAB(input)) { return "oklab"; } if (isLCH(input)) { return "oklch"; } if (isRGB(input)) { return "rgb"; } throw new Error(MESSAGES.invalid); } function getColorValue(input, output) { const value = isNamedColor(input) ? cssColors[input.toLowerCase()] : input; const from = getColorModel(value); if (from === output) { return value; } const converterKey = `${from}2${output}`; const converter = converters_exports[converterKey]; if (!converter) { throw new Error(`Converter not found for ${from} to ${output}`); } return converter(value); } function formatCSS(input, formatOrOptions) { invariant(isHex(input) || isValidColorModel(input), MESSAGES.invalid); const { alpha, format, precision = PRECISION, separator: baseSeparator = " " } = isString(formatOrOptions, false) ? { format: formatOrOptions } : formatOrOptions ?? {}; const colorFormat = format || getColorModel(input); const normalizedAlpha = isNumber(alpha) ? normalizeAlpha(alpha) : void 0; const opacity2 = normalizedAlpha !== void 0 && normalizedAlpha !== 1 ? `${round(normalizedAlpha * 100)}%` : null; let params = []; let separator = baseSeparator; switch (colorFormat) { case "hsl": { const { h, s, l } = getColorValue(input, "hsl"); params = [h, `${s}%`, `${l}%`]; break; } case "oklab": { separator = " "; const { l, a, b } = restrictValues(getColorValue(input, "oklab"), precision, false); params = [`${round(l * 100, precision, false)}%`, a, b]; break; } case "oklch": { separator = " "; const { l, c, h } = restrictValues(getColorValue(input, "oklch"), precision, false); params = [`${round(l * 100, precision, false)}%`, c, c === 0 ? "none" : h]; break; } case "rgb": { const { r, g, b } = getColorValue(input, "rgb"); params = [r, g, b]; break; } default: { const hex = removeAlphaFromHex(getColorValue(input, "hex")); if (normalizedAlpha !== void 0 && normalizedAlpha !== 1) { return `${hex}${convertAlphaToHex(normalizedAlpha)}`; } return hex; } } return `${colorFormat}(${params.join(separator)}${opacity2 ? ` / ${opacity2}` : ""})`; } // src/extract-color-parts.ts var MODEL = "(rgb|hsl|oklab|oklch)a?"; var SEP = "(?:\\s*[,/]\\s*|\\s+)"; var VALUE = "(none|[\\d%.-]+(?:deg|grad|rad|turn)?)"; var colorRegex = new RegExp( `${MODEL}\\s*\\(\\s*${VALUE}${SEP}${VALUE}${SEP}${VALUE}(?:${SEP}${VALUE})?\\s*\\)`, "i" ); function parseAngle(value) { const number_ = parseFloat(value); let result; if (value.endsWith("grad")) { result = number_ * 0.9; } else if (value.endsWith("rad")) { result = number_ * (180 / Math.PI); } else if (value.endsWith("turn")) { result = number_ * 360; } else { result = number_; } return Math.round(result * 1e5) / 1e5; } function extractColorParts(input) { invariant(isString(input), MESSAGES.inputString); if (isHex(input)) { const keys2 = COLOR_KEYS.rgb; const { r, g, b } = hex2rgb(input); const alpha2 = extractAlphaFromHex(input); return { model: "rgb", [keys2[0]]: r, [keys2[1]]: g, [keys2[2]]: b, alpha: alpha2 < 1 ? alpha2 : void 0 }; } const matches = colorRegex.exec(input); invariant(hasValidMatches(matches), MESSAGES.invalidCSS); let rawAlpha = 1; if (matches[5]) { rawAlpha = matches[5] === "none" ? 0 : parseFloat(matches[5]); } const model = matches[1]; const keys = COLOR_KEYS[model]; const alpha = normalizeAlpha(rawAlpha); const parseValue = (value, index) => { if (value === "none") { return 0; } const isHue = model === "hsl" && index === 0 || model === "oklch" && index === 2; if (isHue) { return parseAngle(value); } const parsedValue = parseFloat(value); const isPercent = value.includes("%"); if (!isPercent) { return parsedValue; } if (model === "oklch") { if (index === 1) { return parsedValue * 0.4 / 100; } } else if (model === "oklab" && (index === 1 || index === 2)) { return parsedValue * 0.4 / 100; } return parsedValue; }; const values = [parseValue(matches[2], 0), parseValue(matches[3], 1), parseValue(matches[4], 2)]; if (model === "oklab") { invariant(values[1] >= -0.4 && values[1] <= 0.4, MESSAGES.invalidRange); invariant(values[2] >= -0.4 && values[2] <= 0.4, MESSAGES.invalidRange); } else if (model === "oklch") { invariant(values[1] >= 0 && values[1] <= 0.4, MESSAGES.invalidRange); } return { model, [keys[0]]: values[0], [keys[1]]: values[1], [keys[2]]: values[2], alpha: alpha < 1 ? alpha : void 0 }; } // src/modules/parsed-color.ts var toHexConverters = { hsl: hsl2hex, oklab: oklab2hex, oklch: oklch2hex, rgb: rgb2hex }; var fromHexConverters = { hsl: hex2hsl, oklab: hex2oklab, oklch: hex2oklch, rgb: hex2rgb }; var modelConverters = { hsl: { oklab: oklab2hsl, oklch: oklch2hsl, rgb: rgb2hsl }, oklab: { hsl: hsl2oklab, oklch: oklch2oklab, rgb: rgb2oklab }, oklch: { hsl: hsl2oklch, oklab: oklab2oklch, rgb: rgb2oklch }, rgb: { hsl: hsl2rgb, oklab: oklab2rgb, oklch: oklch2rgb } }; var ParsedColorImpl = class { constructor(type, value, alpha) { __publicField(this, "__parsed", true); __publicField(this, "type"); __publicField(this, "alpha"); __publicField(this, "_sourceValue"); __publicField(this, "_cache", {}); this.type = type; this.alpha = alpha; this._sourceValue = value; this._cache[type] = value; } get hex() { return this._getAs("hex"); } get hsl() { return this._getAs("hsl"); } get rgb() { return this._getAs("rgb"); } get oklab() { return this._getAs("oklab"); } get oklch() { return this._getAs("oklch"); } toCSS(options) { const format = options?.format ?? this.type; const alpha = options?.alpha ?? (this.alpha < 1 ? this.alpha : void 0); if (format === "hex") { const hex = removeAlphaFromHex(this.hex); if (alpha !== void 0 && alpha < 1) { return `${hex}${convertAlphaToHex(alpha)}`; } return hex; } return formatCSS(this[format], { ...options, format, alpha }); } _getAs(target) { if (this._cache[target]) { return this._cache[target]; } let result; if (this.type === "hex") { result = target === "hex" ? this._sourceValue : fromHexConverters[target](this._sourceValue); } else if (target === "hex") { const tuple = Object.values(this._sourceValue); result = toHexConverters[this.type](tuple); if (this.alpha < 1) { result = `${result}${convertAlphaToHex(this.alpha)}`; } } else { const converter = modelConverters[target]?.[this.type]; if (!converter) { result = fromHexConverters[target](this.hex); } else { const tuple = Object.values(this._sourceValue); result = converter(tuple); } } this._cache[target] = result; return result; } }; function isParsedColor(input) { return typeof input === "object" && input !== null && "__parsed" in input && input.__parsed === true; } function resolveColor(input) { if (isParsedColor(input)) { return input; } if (isPlainObject(input)) { const { alpha: rawAlpha = 1 } = input; const alpha2 = clamp(normalizeAlpha(rawAlpha), 0, 1); if (isHSL(input)) { return new ParsedColorImpl( "hsl", { h: clamp(input.h, 0, 360), s: clamp(input.s), l: clamp(input.l) }, alpha2 ); } if (isRGB(input)) { return new ParsedColorImpl( "rgb", { r: clamp(input.r, 0, 255), g: clamp(input.g, 0, 255), b: clamp(input.b, 0, 255) }, alpha2 ); } if (isLAB(input)) { return new ParsedColorImpl("oklab", { l: input.l, a: input.a, b: input.b }, alpha2); } if (isLCH(input)) { return new ParsedColorImpl("oklch", { l: input.l, c: input.c, h: input.h }, alpha2); } } const value = isNamedColor(input) ? cssColors[input.toLowerCase()] : input; if (isHex(value)) { const alpha2 = extractAlphaFromHex(value); const hex = removeAlphaFromHex(value); return new ParsedColorImpl("hex", hex, alpha2); } const parts = extractColorParts(value); const { alpha, model, ...color } = parts; const colorValue = ["oklab", "oklch"].includes(model) ? normalizeOkLightness(color) : color; const colorType = model; return new ParsedColorImpl(colorType, colorValue, alpha ?? 1); } // src/convertCSS.ts function convertCSS(input, format) { const parsed = resolveColor(input); return formatCSS(parsed[format], { format, alpha: parsed.alpha < 1 ? parsed.alpha : void 0 }); } // src/apca.ts var APCA_VERSION = "0.0.98G-4g"; var mainTRC = 2.4; var normBG = 0.56; var normTXT = 0.57; var revBG = 0.65; var revTXT = 0.62; var sRco = 0.2126729; var sGco = 0.7151522; var sBco = 0.072175; var blkThreshold = 0.022; var blkClamp = 1.414; var scaleBoW = 1.14; var scaleWoB = 1.14; var loBoWOffset = 0.027; var loWoBOffset = 0.027; var loClip = 0.1; var deltaYmin = 5e-4; function softClamp(Y) { return Y > blkThreshold ? Y : Y + (blkThreshold - Y) ** blkClamp; } function sRGBtoY(r, g, b) { return sRco * (r / 255) ** mainTRC + sGco * (g / 255) ** mainTRC + sBco * (b / 255) ** mainTRC; } function apcaContrast(background, foreground) { invariant(isString(background), MESSAGES.inputString); invariant(isString(foreground), MESSAGES.inputString); const bg = resolveColor(background).rgb; const fg = resolveColor(foreground).rgb; const txtY = softClamp(sRGBtoY(fg.r, fg.g, fg.b)); const bgY = softClamp(sRGBtoY(bg.r, bg.g, bg.b)); if (Math.abs(bgY - txtY) < deltaYmin) { return 0; } const SAPC = bgY > txtY ? (bgY ** normBG - txtY ** normTXT) * scaleBoW : (bgY ** revBG - txtY ** revTXT) * scaleWoB; if (Math.abs(SAPC) < loClip) { return 0; } const Lc = SAPC > 0 ? (SAPC - loBoWOffset) * 100 : (SAPC + loWoBOffset) * 100; return round(Lc, 5); } // src/brightness-difference.ts function brightnessDifference(left, right, precision = PRECISION) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const RGBLeft = resolveColor(left).rgb; const RGBRight = resolveColor(right).rgb; const brightnessLeft = (RGBLeft.r * 299 + RGBLeft.g * 587 + RGBLeft.b * 114) / 1e3; const brightnessRight = (RGBRight.r * 299 + RGBRight.g * 587 + RGBRight.b * 114) / 1e3; return round(Math.abs(brightnessRight - brightnessLeft), precision); } // src/chroma.ts function chroma(input) { invariant(isString(input), MESSAGES.inputString); const { r, g, b } = resolveColor(input).rgb; const max = Math.max(r, g, b); const min = Math.min(r, g, b); return round((max - min) / 255, 4); } // src/color-difference.ts function colorDifference(left, right) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const RGBLeft = resolveColor(left).rgb; const RGBRight = resolveColor(right).rgb; return Math.max(RGBLeft.r, RGBRight.r) - Math.min(RGBLeft.r, RGBRight.r) + (Math.max(RGBLeft.g, RGBRight.g) - Math.min(RGBLeft.g, RGBRight.g)) + (Math.max(RGBLeft.b, RGBRight.b) - Math.min(RGBLeft.b, RGBRight.b)); } // src/luminance.ts function luminance(input) { invariant(isString(input), MESSAGES.inputString); const { r, g, b } = resolveColor(input).rgb; const lr = srgbGammaDecode(r / 255); const lg = srgbGammaDecode(g / 255); const lb = srgbGammaDecode(b / 255); return round(0.2126 * lr + 0.7152 * lg + 0.0722 * lb, 4); } // src/contrast.ts function contrast(left, right) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const LuminanceLeft = luminance(left); const LuminanceRight = luminance(right); return round( LuminanceLeft >= LuminanceRight ? (LuminanceLeft + 0.05) / (LuminanceRight + 0.05) : (LuminanceRight + 0.05) / (LuminanceLeft + 0.05) ); } // src/compare.ts function compare(left, right) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const colorThreshold = 500; const brightnessThreshold = 125; const colorDifference2 = colorDifference(left, right); const contrast2 = contrast(left, right); const brightnessDifference2 = brightnessDifference(left, right); const isBright = brightnessDifference2 >= brightnessThreshold; const hasEnoughDifference = colorDifference2 >= colorThreshold; let compliant = 0; if (isBright && hasEnoughDifference) { compliant = 2; } else if (isBright || hasEnoughDifference) { compliant = 1; } return { brightnessDifference: brightnessDifference2, colorDifference: colorDifference2, compliant, contrast: contrast2, largeAA: contrast2 >= 3, largeAAA: contrast2 >= 4.5, normalAA: contrast2 >= 4.5, normalAAA: contrast2 >= 7 }; } // src/modules/updater.ts function updater(key, operator, format) { return (input, amount) => { invariant(isString(input) || isParsedColor(input), MESSAGES.inputString); invariant(isNumberInRange(amount, 0, 100), MESSAGES.amount); const parsed = resolveColor(input); const { hsl } = parsed; const output = format ?? parsed.type; return formatCSS( { ...hsl, [key]: clamp(hsl[key] + (operator === "+" ? amount : -amount), 0, 100) }, { format: output, alpha: parsed.alpha < 1 ? parsed.alpha : void 0 } ); }; } // src/darken.ts function darken(input, amount, format) { return updater("l", "-", format)(input, amount); } // src/delta-e.ts var DELTA_E_JND = 0.02; function deltaE(left, right, precision = PRECISION) { invariant(isString(left), MESSAGES.left); invariant(isString(right), MESSAGES.right); const lab1 = resolveColor(left).oklab; const lab2 = resolveColor(right).oklab; return round( Math.sqrt((lab1.l - lab2.l) ** 2 + (lab1.a - lab2.a) ** 2 + (lab1.b - lab2.b) ** 2), precision ); } // src/desaturate.ts function desaturate(input, amount, format) { return updater("s", "-", format)(input, amount); } // src/grayscale.ts function grayscale(input, format) { invariant(isString(input), MESSAGES.inputString); const parsed = resolveColor(input); const lch = parsed.oklch; const output = format ?? parsed.type; return formatCSS( { ...lch, c: 0 }, { format: output, alpha: parsed.alpha < 1 ? parsed.alpha : void 0 } ); } // src/rotate.ts function rotate(input, degrees, format) { invariant(isString(input), MESSAGES.inputString); invariant(isNumberInRange(degrees, -360, 360), MESSAGES.degreesRange); const parsed = resolveColor(input); const color = parsed.hsl; const output = format ?? parsed.type; return formatCSS( { ...color, h: constrainDegrees(color.h, degrees) }, { format: output, alpha: parsed.alpha < 1 ? parsed.alpha : void 0 } ); } // src/invert.ts function invert(input) { invariant(isString(input), MESSAGES.inputString); return rotate(input, 180); } // src/lighten.ts function lighten(input, amount, format) { return updater("l", "+", format)(input, amount); } // src/mix.ts function interpolateAlpha(a1, a2, ratio) { return a1 + (a2 - a1) * ratio; } function interpolateHue(h1, h2, c1, c2, ratio, mode = "shorter") { if (c1 < 1e-4) { return h2; } if (c2 < 1e-4) { return h1; } let diff = h2 - h1; switch (mode) { case "shorter": { if (diff > 180) { diff -= 360; } else if (diff < -180) { diff += 360; } break; } case "longer": { if (diff > 0 && diff < 180) { diff -= 360; } else if (diff > -180 && diff <= 0) { diff += 360; } break; } case "increasing": { if (diff < 0) { diff += 360; } break; } case "decreasing": { if (diff > 0) { diff -= 360; } break; } } const result = h1 + diff * ratio; return (result % 360 + 360) % 360; } function mixInHSL(c1, c2, ratio, hueMode) { return { h: interpolateHue(c1.h, c2.h, c1.s, c2.s, ratio, hueMode), s: c1.s + (c2.s - c1.s) * ratio, l: c1.l + (c2.l - c1.l) * ratio }; } function mixInOkLab(c1, c2, ratio) { return { l: c1.l + (c2.l - c1.l) * ratio, a: c1.a + (c2.a - c1.a) * ratio, b: c1.b + (c2.b - c1.b) * ratio }; } function mixInOkLCH(c1, c2, ratio, hueMode) { return { l: c1.l + (c2.l - c1.l) * ratio, c: c1.c + (c2.c - c1.c) * ratio, h: interpolateHue(c1.h, c2.h, c1.c, c2.c, ratio, hueMode) }; } function mixInRGB(c1, c2, ratio) { return { r: Math.round(c1.r + (c2.r - c1.r) * ratio), g: Math.round(c1.g + (c2.g - c1.g) * ratio), b: Math.round(c1.b + (c2.b - c1.b) * ratio) }; } function mix(color1, color2, ratio = 0.5, formatOrOptions) { invariant(isString(color1), MESSAGES.inputString); invariant(isString(color2), MESSAGES.inputString); invariant(isNumberInRange(ratio, 0, 1), MESSAGES.ratioRange); const { format, hue = "shorter", space = "oklch" } = isString(formatOrOptions, false) ? { format: formatOrOptions } : formatOrOptions ?? {}; const parsed1 = resolveColor(color1); const parsed2 = resolveColor(color2); const output = format ?? parsed1.type; let color; switch (space) { case "hsl": { color = mixInHSL(parsed1.hsl, parsed2.hsl, ratio, hue); break; } case "oklab": { color = mixInOkLab(parsed1.oklab, parsed2.oklab, ratio); break; } case "rgb": { color = mixInRGB(parsed1.rgb, parsed2.rgb, ratio); break; } case "oklch": default: { color = mixInOkLCH(parsed1.oklch, parsed2.oklch, ratio, hue); break; } } const alpha = interpolateAlpha(parsed1.alpha, parsed2.alpha, ratio); return formatCSS(color, { format: output, alpha: alpha < 1 ? alpha : void 0 }); } // src/opacify.ts function opacify(input, alpha, format) { invariant(isString(input), MESSAGES.inputString); invariant(isNumberInRange(alpha, 0, 1), MESSAGES.alpha); const parsed = resolveColor(input); return formatCSS(parsed.oklch, { format: format ?? parsed.type, alpha }); } // src/opacity.ts function opacity(input) { invariant(isString(input), MESSAGES.inputString); return resolveColor(input).alpha; } // src/readable-color.ts function pickByContrast(darkContrast, lightContrast, darkColor, lightColor) { return darkContrast >= lightContrast ? darkColor : lightColor; } function readableColor(backgroundColor, methodOrOptions) { const { darkColor = "#000000", lightColor = "#ffffff", method = "yiq", threshold } = isString(methodOrOptions, false) ? { method: methodOrOptions } : methodOrOptions ?? {}; invariant(isString(backgroundColor), MESSAGES.inputString); const parsed = resolveColor(backgroundColor); const bgCSS = parsed.toCSS(); switch (method) { case "yiq": { const yiqThreshold = threshold ?? 128; invariant(yiqThreshold >= 0 && yiqThreshold <= 255, MESSAGES.threshold); const { r, g, b } = parsed.rgb; const yiq = (r * 299 + g * 587 + b * 114) / 1e3; return yiq >= yiqThreshold ? darkColor : lightColor; } case "wcag": { const wcagThreshold = threshold ?? 0.5; invariant(wcagThreshold >= 0 && wcagThreshold <= 1, MESSAGES.thresholdNormalized); const lum = luminance(bgCSS); return lum >= wcagThreshold ? darkColor : lightColor; } case "contrast": { const darkContrast = contrast(darkColor, bgCSS); const lightContrast = contrast(lightColor, bgCSS); return pickByContrast(darkContrast, lightContrast, darkColor, lightColor); } case "oklab": { const oklabThreshold = threshold ?? 0.5; invariant(oklabThreshold >= 0 && oklabThreshold <= 1, MESSAGES.thresholdNormalized); const { l } = parsed.oklab; return l >= oklabThreshold ? darkColor : lightColor; } case "apca": { const darkContrast = Math.abs(apcaContrast(bgCSS, darkColor)); const lightContrast = Math.abs(apcaContrast(bgCSS, lightColor)); return pickByContrast(darkContrast, lightContrast, darkColor, lightColor); } /* v8 ignore next 3 -- @preserve */ default: { throw new Error(`Unknown method: ${method}`); } } } // src/saturate.ts function saturate(input, amount, format) { return updater("s", "+", format)(input, amount); } // src/modules/linear-rgb.ts function multiplyMatrix(matrix, vector) { return [ matrix[0][0] * vector[0] + matrix[0][1] * vector[1] + matrix[0][2] * vector[2], matrix[1][0] * vector[0] + matrix[1][1] * vector[1] + matrix[1][2] * vector[2], matrix[2][0] * vector[0] + matrix[2][1] * vector[1] + matrix[2][2] * vector[2] ]; } function isInGamut(color) { return color.every((component) => component >= 0 - GAMUT_EPSILON && component <= 1 + GAMUT_EPSILON); } function oklabToLinearP3(L, a, b) { return multiplyMatrix(SRGB_TO_P3, oklabToLinearSRGB(L, a, b)); } function oklabToLinearSRGB(L, a, b) { const l = (OKLAB_TO_CLMS[0][0] * L + OKLAB_TO_CLMS[0][1] * a + OKLAB_TO_CLMS[0][2] * b) ** 3; const m = (OKLAB_TO_CLMS[1][0] * L + OKLAB_TO_CLMS[1][1] * a + OKLAB_TO_CLMS[1][2] * b) ** 3; const s = (OKLAB_TO_CLMS[2][0] * L + OKLAB_TO_CLMS[2][1] * a + OKLAB_TO_CLMS[2][2] * b) ** 3; return [ LMS_TO_LRGB[0][0] * l + LMS_TO_LRGB[0][1] * m + LMS_TO_LRGB[0][2] * s, LMS_TO_LRGB[1][0] * l + LMS_TO_LRGB[1][1] * m + LMS_TO_LRGB[1][2] * s, LMS_TO_LRGB[2][0] * l + LMS_TO_LRGB[2][1] * m + LMS_TO_LRGB[2][2] * s ]; } // src/to-gamut.ts function toGamut(input, format) { invariant(isString(input), MESSAGES.inputString); const parsed = resolveColor(input); const lch = parsed.oklch; const output = format ?? parsed.type; const alpha = parsed.alpha < 1 ? parsed.alpha : void 0; if (lch.l <= 0) { return formatCSS({ r: 0, g: 0, b: 0 }, { format: output, alpha }); } if (lch.l >= 1) { return formatCSS({ r: 255, g: 255, b: 255 }, { format: output, alpha }); } const lab = oklch2oklab(lch, 16); if (isInGamut(oklabToLinearSRGB(lab.l, lab.a, lab.b))) { return formatCSS(lch, { format: output, alpha }); } const epsilon = GAMUT_EPSILON; let low = 0; let high = lch.c; while (high - low > epsilon) { const mid = (low + high) / 2; const midLab = oklch2oklab({ l: lch.l, c: mid, h: lch.h }, 16); if (isInGamut(oklabToLinearSRGB(midLab.l, midLab.a, midLab.b))) { low = mid; } else { high = mid; } } return formatCSS({ l: lch.l, c: low, h: lch.h }, { format: output, alpha }); } // src/transparentize.ts function transparentize(input, alpha, format) { invariant(isString(input), MESSAGES.inputString); invariant(isNumberInRange(alpha, -1, 1), MESSAGES.alphaAdjustment); const parsed = resolveColor(input); const value = round(clamp(parsed.alpha - alpha, 0, 1)); return formatCSS(parsed.oklch, { format: format ?? parsed.type, alpha: value }); } // src/colorizr.ts var Colorizr = class { constructor(color, options = {}) { /** The alpha/opacity value (0-1). */ __publicField(this, "alpha"); __publicField(this, "hex"); __publicField(this, "hsl"); __publicField(this, "oklab"); __publicField(this, "oklch"); __publicField(this, "rgb"); __publicField(this, "type"); invariant(!!color, MESSAGES.colorRequired); const parsed = resolveColor(color); this.hex = parsed.hex; this.hsl = parsed.hsl; this.oklab = parsed.oklab; this.oklch = parsed.oklch; this.rgb = parsed.rgb; this.alpha = parsed.alpha; this.type = options.format ?? parsed.type; } /** * Get CSS string */ get css() { return this.currentColor; } /** * Get the red value */ get red() { return this.rgb.r; } /** * Get the green value */ get green() { return this.rgb.g; } /** * Get the blue value */ get blue() { return this.rgb.b; } /** * Get the hue value */ get hue() { return this.hsl.h; } /** * Get the saturation value */ get saturation() { return this.hsl.s; } /** * Get the lightness value */ get lightness() { return this.hsl.l; } /** * Get the luminance value */ get luminance() { return luminance(this.currentColor); } /** * Get the chroma value */ get chroma() { return chroma(this.currentColor); } /** * Get the opacity value. */ get opacity() { return opacity(this.currentColor); } /** * Get the most readable color (light or dark) for this color as a background. */ get readableColor() { return readableColor(this.currentColor); } get currentColor() { return formatCSS(this[this.type], { format: this.type, alpha: this.alpha }); } /** * Get the brightness difference between this color and another. * * @param input - The color to compare against. * @returns The brightness difference value. */ brightnessDifference(input) { return brightnessDifference(this.curren