UNPKG

colorizr

Version:
1 lines 115 kB
{"version":3,"sources":["../src/modules/invariant.ts","../src/modules/constants.ts","../src/modules/css-colors.ts","../src/modules/validators.ts","../src/modules/utils.ts","../src/modules/hex-utils.ts","../src/converters/index.ts","../src/format-hex.ts","../src/converters/hex2rgb.ts","../src/converters/rgb2hsl.ts","../src/converters/hex2hsl.ts","../src/converters/rgb2oklab.ts","../src/converters/hex2oklab.ts","../src/converters/oklab2oklch.ts","../src/converters/rgb2oklch.ts","../src/converters/hex2oklch.ts","../src/modules/hue2rgb.ts","../src/converters/hsl2rgb.ts","../src/converters/rgb2hex.ts","../src/converters/hsl2hex.ts","../src/converters/hsl2oklab.ts","../src/converters/hsl2oklch.ts","../src/converters/oklab2rgb.ts","../src/converters/oklab2hex.ts","../src/converters/oklab2hsl.ts","../src/converters/oklch2oklab.ts","../src/converters/oklch2rgb.ts","../src/converters/oklch2hex.ts","../src/converters/oklch2hsl.ts","../src/extract-color-parts.ts","../src/parse-css.ts","../src/modules/parse-color.ts","../src/brightness-difference.ts","../src/chroma.ts","../src/color-difference.ts","../src/luminance.ts","../src/contrast.ts","../src/compare.ts","../src/format-css.ts","../src/modules/updater.ts","../src/darken.ts","../src/desaturate.ts","../src/rotate.ts","../src/invert.ts","../src/lighten.ts","../src/opacify.ts","../src/opacity.ts","../src/saturate.ts","../src/text-color.ts","../src/transparentize.ts","../src/colorizr.ts","../src/convert.ts","../src/is-valid-color.ts","../src/name.ts","../src/palette.ts","../src/p3.ts","../src/random.ts","../src/scheme.ts","../src/swatch.ts","../src/index.ts"],"sourcesContent":["export function invariant(condition: boolean, message: string): asserts condition {\n if (condition) {\n return;\n }\n\n if (process.env.NODE_ENV !== 'production') {\n if (message === undefined) {\n throw new Error('invariant requires an error message argument');\n }\n }\n\n const error = !message\n ? new Error(\n 'Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.',\n )\n : new Error(message);\n\n error.name = 'colorizr';\n\n throw error;\n}\n","import { ColorKeysTuple, ColorModelKey } from '~/types';\n\nexport const COLOR_KEYS: Record<ColorModelKey, ColorKeysTuple> = {\n hsl: ['h', 's', 'l'],\n oklab: ['l', 'a', 'b'],\n oklch: ['l', 'c', 'h'],\n rgb: ['r', 'g', 'b'],\n};\nexport const COLOR_MODELS: ColorModelKey[] = ['hsl', 'oklab', 'oklch', 'rgb'];\nexport const DEG2RAD = Math.PI / 180;\nexport const LAB_TO_LMS = {\n l: [0.3963377773761749, 0.2158037573099136],\n m: [-0.1055613458156586, -0.0638541728258133],\n s: [-0.0894841775298119, -1.2914855480194092],\n};\nexport const LRGB_TO_LMS = {\n l: [0.4122214708, 0.5363325363, 0.0514459929],\n m: [0.2119034982, 0.6806995451, 0.1073969566],\n s: [0.0883024619, 0.2817188376, 0.6299787005],\n};\nexport const LSM_TO_LAB = {\n l: [0.2104542553, 0.793617785, 0.0040720468],\n a: [1.9779984951, 2.428592205, 0.4505937099],\n b: [0.0259040371, 0.7827717662, 0.808675766],\n};\nexport const LSM_TO_RGB = {\n r: [4.0767416360759583, -3.3077115392580629, 0.2309699031821043],\n g: [-1.2684379732850315, 2.6097573492876882, -0.341319376002657],\n b: [-0.0041960761386756, -0.7034186179359362, 1.7076146940746117],\n};\nexport const SRGB_TO_P3 = [\n [0.8224270476, 0.1775729524, 0],\n [0.0331008087, 0.9668991913, 0],\n [0.0170720188, 0.0723477973, 0.9105801839],\n];\nexport const PRECISION = 5;\nexport const RAD2DEG = 180 / Math.PI;\n\nexport const MESSAGES = {\n alpha: 'amount must be a number between 0 and 1',\n hueRange: 'hue must be a number between 0 and 360',\n input: 'input is required',\n inputHex: 'input is required and must be a hex',\n inputNumber: 'input is required and must be a number',\n inputString: 'input is required and must be a string',\n invalid: 'invalid input',\n invalidCSS: 'invalid CSS string',\n left: 'left is required and must be a string',\n lightnessRange: 'lightness must be a number between 0 and 1',\n options: 'invalid options',\n right: 'right is required and must be a string',\n threshold: 'threshold must be a number between 0 and 255',\n} as const;\n","/**\n * CSS named colors\n */\n\nexport type CSSColor = keyof typeof cssColors;\n\nexport const cssColors = {\n aliceblue: '#f0f8ff',\n antiquewhite: '#faebd7',\n aqua: '#00ffff',\n aquamarine: '#7fffd4',\n azure: '#f0ffff',\n beige: '#f5f5dc',\n bisque: '#ffe4c4',\n black: '#000000',\n blanchedalmond: '#ffebcd',\n blue: '#0000ff',\n blueviolet: '#8a2be2',\n brown: '#a52a2a',\n burlywood: '#deb887',\n cadetblue: '#5f9ea0',\n chartreuse: '#7fff00',\n chocolate: '#d2691e',\n coral: '#ff7f50',\n cornflowerblue: '#6495ed',\n cornsilk: '#fff8dc',\n crimson: '#dc143c',\n cyan: '#00ffff',\n darkblue: '#00008b',\n darkcyan: '#008b8b',\n darkgoldenrod: '#b8860b',\n darkgray: '#a9a9a9',\n darkgreen: '#006400',\n darkkhaki: '#bdb76b',\n darkmagenta: '#8b008b',\n darkolivegreen: '#556b2f',\n darkorange: '#ff8c00',\n darkorchid: '#9932cc',\n darkred: '#8b0000',\n darksalmon: '#e9967a',\n darkseagreen: '#8fbc8f',\n darkslateblue: '#483d8b',\n darkslategray: '#2f4f4f',\n darkslategrey: '#2f4f4f',\n darkturquoise: '#00ced1',\n darkviolet: '#9400d3',\n deeppink: '#ff1493',\n deepskyblue: '#00bfff',\n dimgray: '#696969',\n dimgrey: '#696969',\n dodgerblue: '#1e90ff',\n firebrick: '#b22222',\n floralwhite: '#fffaf0',\n forestgreen: '#228b22',\n fuchsia: '#ff00ff',\n gainsboro: '#dcdcdc',\n ghostwhite: '#f8f8ff',\n gold: '#ffd700',\n goldenrod: '#daa520',\n gray: '#808080',\n grey: '#808080',\n green: '#008000',\n greenyellow: '#adff2f',\n honeydew: '#f0fff0',\n hotpink: '#ff69b4',\n indianred: '#cd5c5c',\n indigo: '#4b0082',\n ivory: '#fffff0',\n khaki: '#f0e68c',\n lavender: '#e6e6fa',\n lavenderblush: '#fff0f5',\n lawngreen: '#7cfc00',\n lemonchiffon: '#fffacd',\n lightblue: '#add8e6',\n lightcoral: '#f08080',\n lightcyan: '#e0ffff',\n lightgoldenrodyellow: '#fafad2',\n lightgray: '#d3d3d3',\n lightgreen: '#90ee90',\n lightgrey: '#d3d3d3',\n lightpink: '#ffb6c1',\n lightsalmon: '#ffa07a',\n lightseagreen: '#20b2aa',\n lightskyblue: '#87cefa',\n lightslategray: '#778899',\n lightslategrey: '#778899',\n lightsteelblue: '#b0c4de',\n lightyellow: '#ffffe0',\n lime: '#00ff00',\n limegreen: '#32cd32',\n linen: '#faf0e6',\n magenta: '#ff00ff',\n maroon: '#800000',\n mediumaquamarine: '#66cdaa',\n mediumblue: '#0000cd',\n mediumorchid: '#ba55d3',\n mediumpurple: '#9370db',\n mediumseagreen: '#3cb371',\n mediumslateblue: '#7b68ee',\n mediumspringgreen: '#00fa9a',\n mediumturquoise: '#48d1cc',\n mediumvioletred: '#c71585',\n midnightblue: '#191970',\n mintcream: '#f5fffa',\n mistyrose: '#ffe4e1',\n moccasin: '#ffe4b5',\n navajowhite: '#ffdead',\n navy: '#000080',\n oldlace: '#fdf5e6',\n olive: '#808000',\n olivedrab: '#6b8e23',\n orange: '#ffa500',\n orangered: '#ff4500',\n orchid: '#da70d6',\n palegoldenrod: '#eee8aa',\n palegreen: '#98fb98',\n paleturquoise: '#afeeee',\n palevioletred: '#db7093',\n papayawhip: '#ffefd5',\n peachpuff: '#ffdab9',\n peru: '#cd853f',\n pink: '#ffc0cb',\n plum: '#dda0dd',\n powderblue: '#b0e0e6',\n purple: '#800080',\n rebeccapurple: '#663399',\n red: '#ff0000',\n rosybrown: '#bc8f8f',\n royalblue: '#4169e1',\n saddlebrown: '#8b4513',\n salmon: '#fa8072',\n sandybrown: '#f4a460',\n seagreen: '#2e8b57',\n seashell: '#fff5ee',\n sienna: '#a0522d',\n silver: '#c0c0c0',\n skyblue: '#87ceeb',\n slateblue: '#6a5acd',\n slategray: '#708090',\n slategrey: '#708090',\n snow: '#fffafa',\n springgreen: '#00ff7f',\n steelblue: '#4682b4',\n tan: '#d2b48c',\n teal: '#008080',\n thistle: '#d8bfd8',\n tomato: '#ff6347',\n turquoise: '#40e0d0',\n violet: '#ee82ee',\n wheat: '#f5deb3',\n white: '#ffffff',\n whitesmoke: '#f5f5f5',\n yellow: '#ffff00',\n yellowgreen: '#9acd32',\n};\n","import { COLOR_KEYS } from '~/modules/constants';\nimport { CSSColor, cssColors } from '~/modules/css-colors';\n\nimport { ColorModel, HEX, HSL, LAB, LCH, PlainObject, RGB } from '~/types';\n\nexport function hasValidMatches(input: unknown): input is string[] {\n return Array.isArray(input) && input.length === 6;\n}\n\n/**\n * Check if the input is a CSS named color\n */\nexport function isNamedColor(input: unknown): input is CSSColor {\n return isString(input) && Object.keys(cssColors).includes(input.toLowerCase());\n}\n\n/**\n * Check if the input is a number and not NaN\n */\nexport function isNumber(input: unknown): input is number {\n return typeof input === 'number' && !Number.isNaN(input);\n}\n\n/**\n * Check if the input is an object\n */\nexport function isPlainObject(input: unknown): input is PlainObject {\n if (!input) {\n return false;\n }\n\n const { toString } = Object.prototype;\n const prototype = Object.getPrototypeOf(input);\n\n return (\n toString.call(input) === '[object Object]' &&\n (prototype === null || prototype === Object.getPrototypeOf({}))\n );\n}\n\n/**\n * Check if the input is a string\n */\nexport function isString(input: unknown, validate = true): input is string {\n const isValid = typeof input === 'string';\n\n if (validate) {\n return isValid && !!input.trim().length;\n }\n\n return isValid;\n}\n\nexport function isValidColorModel<T extends ColorModel>(input: T): input is T {\n return isHSL(input) || isRGB(input) || isLAB(input) || isLCH(input);\n}\n\nexport function isHex(input: any): input is HEX {\n if (!isString(input)) {\n return false;\n }\n\n return /^#([\\da-f]{3,4}|[\\da-f]{6,8})$/i.test(input);\n}\n\n/**\n * Check if an object contains HSL values\n * The input must be an object with keys 'h', 's', and 'l'\n * with values between 0 and 360 for hue or 0 and 100 for the others.\n */\nexport function isHSL(input: unknown): input is HSL {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'h') {\n return value >= 0 && value <= 360;\n }\n\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.hsl.includes(key) && value >= 0 && value <= 100;\n })\n );\n}\n\n/**\n * Check if an object contains LAB values\n * The input must be an object with keys 'l', 'a', and 'b' with values between -1 and 1.\n */\nexport function isLAB(input: unknown): input is LAB {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'l') {\n return value >= 0 && value <= 100;\n }\n\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.oklab.includes(key) && value >= -1 && value <= 1;\n })\n );\n}\n\n/**\n * Check if an object contains LAB values\n * The input must be an object with keys 'l', 'c', and 'h' with values between 0 and 360.\n */\nexport function isLCH(input: unknown): input is LCH {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'l') {\n return value >= 0 && value <= 100;\n }\n\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.oklch.includes(key) && value >= 0 && value <= (key === 'h' ? 360 : 1);\n })\n );\n}\n\n/**\n * Check if an object contains RGB values.\n * The input must be an object with keys 'r', 'g', and 'b' with values between 0 and 255.\n */\nexport function isRGB(input: unknown): input is RGB {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.rgb.includes(key) && value >= 0 && value <= 255;\n })\n );\n}\n","import { COLOR_KEYS, COLOR_MODELS, MESSAGES, PRECISION } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport {\n isHSL,\n isLAB,\n isLCH,\n isNumber,\n isPlainObject,\n isRGB,\n isValidColorModel,\n} from '~/modules/validators';\n\nimport {\n Alpha,\n ColorModel,\n ColorModelKey,\n ColorModelKeys,\n ConverterParameters,\n LAB,\n LCH,\n PlainObject,\n} from '~/types';\n\nexport function addAlpha<T extends ColorModel>(input: any, alpha?: Alpha): T {\n invariant(isValidColorModel(input), MESSAGES.invalid);\n\n let value = alpha;\n\n if (!value) {\n return input;\n }\n\n /* c8 ignore next 3 */\n if (value > 1) {\n value /= 100;\n }\n\n if (value === 1) {\n return input;\n }\n\n return { ...input, alpha: value };\n}\n\n/**\n * Clamp a value between a min and max\n * @param value\n * @param [min=0] - The minimum value\n * @param [max=100] - The maximum value\n */\nexport function clamp(value: number, min = 0, max = 100) {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Constrain the degrees between 0 and 360\n */\nexport function constrainDegrees(input: number, amount: number): number {\n invariant(isNumber(input), MESSAGES.inputNumber);\n\n let value = input + amount;\n\n if (value > 360) {\n value %= 360;\n }\n\n if (value < 0) {\n value += 360;\n }\n\n return Math.abs(value);\n}\n\n/**\n * Limit values per type.\n */\nexport function limit<TModel extends Extract<ColorModelKey, 'hsl' | 'rgb'>>(\n input: number,\n model: TModel,\n key: ColorModelKeys<TModel>,\n): number {\n invariant(isNumber(input), 'Input is not a number');\n invariant(COLOR_MODELS.includes(model), `Invalid model${model ? `: ${model}` : ''}`);\n invariant(COLOR_KEYS[model].includes(key), `Invalid key${key ? `: ${key}` : ''}`);\n\n switch (model) {\n case 'hsl': {\n invariant(COLOR_KEYS.hsl.includes(key), 'Invalid key');\n\n if (['s', 'l'].includes(key)) {\n return clamp(input);\n }\n\n return clamp(input, 0, 360);\n }\n case 'rgb': {\n invariant(COLOR_KEYS.rgb.includes(key), 'Invalid key');\n\n return clamp(input, 0, 255);\n }\n /* c8 ignore next 3 */\n default: {\n throw new Error('Invalid inputs');\n }\n }\n}\n\n/**\n * Parse the input parameters\n */\nexport function parseInput<T extends ColorModel>(\n input: ConverterParameters<T>,\n model: ColorModelKey,\n): T {\n const keys = COLOR_KEYS[model];\n const validator = {\n hsl: isHSL,\n oklab: isLAB,\n oklch: isLCH,\n rgb: isRGB,\n };\n\n invariant(isPlainObject(input) || Array.isArray(input), MESSAGES.invalid);\n\n const value = Array.isArray(input)\n ? ({ [keys[0]]: input[0], [keys[1]]: input[1], [keys[2]]: input[2] } as unknown as T)\n : input;\n\n invariant(validator[model](value), `invalid ${model} color`);\n\n return value;\n}\n\n/**\n * Creates an object composed of the picked source properties.\n */\nexport function pick(input: PlainObject, options: string[]): PlainObject {\n if (!Array.isArray(options)) {\n throw new TypeError('options must be an array');\n }\n\n return options\n .filter(d => typeof input[d] !== 'undefined')\n .reduce((acc: PlainObject, d) => {\n acc[d] = input[d];\n\n return acc;\n }, {});\n}\n\n/**\n * Restrict the values to a certain number of digits.\n */\nexport function restrictValues<T extends LAB | LCH>(\n input: T,\n precision: number = PRECISION,\n forcePrecision = true,\n): T {\n const output = new Map(Object.entries(input));\n\n for (const [key, value] of output.entries()) {\n output.set(key, round(value, precision, forcePrecision));\n }\n\n return Object.fromEntries(output) as T;\n}\n\n/**\n * Round decimal numbers.\n */\nexport function round(input: number, precision = 2, forcePrecision = true): number {\n if (!isNumber(input) || input === 0) {\n return 0;\n }\n\n if (forcePrecision) {\n const factor = 10 ** precision;\n\n return Math.round(input * factor) / factor;\n }\n\n const absInput = Math.abs(input);\n\n let digits = Math.abs(Math.ceil(Math.log(absInput) / Math.LN10));\n\n if (digits === 0) {\n digits = 2;\n } else if (digits > precision) {\n digits = precision;\n }\n\n let exponent = precision - (digits < 0 ? 0 : digits);\n\n if (exponent <= 1 && precision > 1) {\n exponent = 2;\n } else if (exponent > precision || exponent === 0) {\n exponent = precision;\n }\n\n const factor = 10 ** exponent;\n\n return Math.round(input * factor) / factor;\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { round } from '~/modules/utils';\nimport { isHex, isNumber } from '~/modules/validators';\n\nimport { Alpha } from '~/types';\n\n/**\n * Add an alpha value to a hex string\n */\nexport function addAlphaToHex(input: string, alpha: Alpha): string {\n invariant(isHex(input), MESSAGES.inputHex);\n invariant(isNumber(alpha), MESSAGES.inputNumber);\n\n if (alpha >= 1) {\n return removeAlphaFromHex(input);\n }\n\n return `${removeAlphaFromHex(input)}${convertAlphaToHex(alpha)}`;\n}\n\n/**\n * Convert an alpha value to a hex value.\n */\nexport function convertAlphaToHex(input: Alpha): string {\n invariant(isNumber(input), MESSAGES.inputNumber);\n\n let alpha = input;\n\n if (input > 1) {\n alpha /= 100;\n }\n\n return Math.round(alpha * 255)\n .toString(16)\n .padStart(2, '0');\n}\n\n/**\n * Extract the alpha value from a hex string\n */\nexport function extractAlphaFromHex(input: string): number {\n invariant(isHex(input), MESSAGES.inputString);\n\n const alpha = input.substring(7, 9);\n\n if (!alpha) {\n return 1;\n }\n\n return round(parseInt(alpha, 16) / 255);\n}\n\nexport function hexadecimalToNumber(input: string) {\n return round(parseInt(input, 16));\n}\n\n/**\n * Remove the alpha value from a hex string\n */\nexport function removeAlphaFromHex(input: string) {\n invariant(isHex(input), MESSAGES.inputHex);\n\n if (input.length === 5) {\n return input.substring(0, 4);\n }\n\n return input.substring(0, 7);\n}\n","export { default as hex2hsl } from '~/converters/hex2hsl';\nexport { default as hex2oklab } from '~/converters/hex2oklab';\nexport { default as hex2oklch } from '~/converters/hex2oklch';\nexport { default as hex2rgb } from '~/converters/hex2rgb';\n\nexport { default as hsl2hex } from '~/converters/hsl2hex';\nexport { default as hsl2oklab } from '~/converters/hsl2oklab';\nexport { default as hsl2oklch } from '~/converters/hsl2oklch';\nexport { default as hsl2rgb } from '~/converters/hsl2rgb';\n\nexport { default as oklab2hex } from '~/converters/oklab2hex';\nexport { default as oklab2hsl } from '~/converters/oklab2hsl';\nexport { default as oklab2oklch } from '~/converters/oklab2oklch';\nexport { default as oklab2rgb } from '~/converters/oklab2rgb';\n\nexport { default as oklch2hex } from '~/converters/oklch2hex';\nexport { default as oklch2hsl } from '~/converters/oklch2hsl';\nexport { default as oklch2oklab } from '~/converters/oklch2oklab';\nexport { default as oklch2rgb } from '~/converters/oklch2rgb';\n\nexport { default as rgb2hex } from '~/converters/rgb2hex';\nexport { default as rgb2hsl } from '~/converters/rgb2hsl';\nexport { default as rgb2oklab } from '~/converters/rgb2oklab';\nexport { default as rgb2oklch } from '~/converters/rgb2oklch';\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport { HEX } from '~/types';\n\nexport default function formatHex(input: string): HEX {\n invariant(isHex(input), MESSAGES.inputHex);\n\n let color = input.replace('#', '');\n\n if (color.length === 3 || color.length === 4) {\n const values = [...color];\n\n color = '';\n\n values.forEach(d => {\n color += `${d}${d}`;\n });\n }\n\n const hex = `#${color}`;\n\n invariant(isHex(hex), 'invalid hex');\n\n return hex;\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport formatHex from '~/format-hex';\nimport { RGB } from '~/types';\n\n/** Convert HEX to RGB */\nexport default function hex2rgb(input: string): RGB {\n invariant(isHex(input), MESSAGES.inputHex);\n\n const hex = formatHex(input).slice(1);\n\n return {\n r: parseInt(hex.charAt(0) + hex.charAt(1), 16),\n g: parseInt(hex.charAt(2) + hex.charAt(3), 16),\n b: parseInt(hex.charAt(4) + hex.charAt(5), 16),\n };\n}\n","import { limit, parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, RGB } from '~/types';\n\n/** Convert RGB to HSL */\nexport default function rgb2hsl(input: ConverterParameters<RGB>): HSL {\n const value = parseInput(input, 'rgb');\n\n const rLimit = limit(value.r, 'rgb', 'r') / 255;\n const gLimit = limit(value.g, 'rgb', 'g') / 255;\n const bLimit = limit(value.b, 'rgb', 'b') / 255;\n\n const min = Math.min(rLimit, gLimit, bLimit);\n const max = Math.max(rLimit, gLimit, bLimit);\n const delta = max - min;\n\n let h = 0;\n let s;\n const l = (max + min) / 2;\n let rate;\n\n switch (max) {\n case rLimit:\n rate = !delta ? 0 : (gLimit - bLimit) / delta;\n h = 60 * rate;\n break;\n case gLimit:\n rate = (bLimit - rLimit) / delta;\n h = 60 * rate + 120;\n break;\n case bLimit:\n rate = (rLimit - gLimit) / delta;\n h = 60 * rate + 240;\n break;\n /* c8 ignore next 2 */\n default:\n break;\n }\n\n if (h < 0) {\n h = 360 + h;\n }\n\n if (min === max) {\n s = 0;\n } else {\n s = l < 0.5 ? delta / (2 * l) : delta / (2 - 2 * l);\n }\n\n return {\n h: Math.abs(+(h % 360).toFixed(2)),\n s: +(s * 100).toFixed(2),\n l: +(l * 100).toFixed(2),\n };\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport hex2rgb from '~/converters/hex2rgb';\nimport rgb2hsl from '~/converters/rgb2hsl';\nimport { HSL } from '~/types';\n\n/** Convert HEX to HSL */\nexport default function hex2hsl(input: string): HSL {\n invariant(isHex(input), MESSAGES.inputHex);\n\n return rgb2hsl(hex2rgb(input));\n}\n","import { LRGB_TO_LMS, LSM_TO_LAB, PRECISION } from '~/modules/constants';\nimport { parseInput, restrictValues } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, RGB } from '~/types';\n\nconst { cbrt, sign } = Math;\n\nfunction rgb2lrgb(input: number) {\n const abs = Math.abs(input);\n\n if (abs < 0.04045) {\n return input / 12.92;\n }\n\n return (sign(input) || 1) * ((abs + 0.055) / 1.055) ** 2.4;\n}\n\n/** Convert RGB to oklab */\nexport default function rgb2oklab(input: ConverterParameters<RGB>, precision = PRECISION): LAB {\n const value = parseInput(input, 'rgb');\n\n const [lr, lg, lb] = [rgb2lrgb(value.r / 255), rgb2lrgb(value.g / 255), rgb2lrgb(value.b / 255)];\n const l = cbrt(LRGB_TO_LMS.l[0] * lr + LRGB_TO_LMS.l[1] * lg + LRGB_TO_LMS.l[2] * lb);\n const m = cbrt(LRGB_TO_LMS.m[0] * lr + LRGB_TO_LMS.m[1] * lg + LRGB_TO_LMS.m[2] * lb);\n const s = cbrt(LRGB_TO_LMS.s[0] * lr + LRGB_TO_LMS.s[1] * lg + LRGB_TO_LMS.s[2] * lb);\n\n const lab = {\n l: LSM_TO_LAB.l[0] * l + LSM_TO_LAB.l[1] * m - LSM_TO_LAB.l[2] * s,\n a: LSM_TO_LAB.a[0] * l - LSM_TO_LAB.a[1] * m + LSM_TO_LAB.a[2] * s,\n b: LSM_TO_LAB.b[0] * l + LSM_TO_LAB.b[1] * m - LSM_TO_LAB.b[2] * s,\n };\n\n return restrictValues(lab, precision);\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport hex2rgb from '~/converters/hex2rgb';\nimport rgb2oklab from '~/converters/rgb2oklab';\nimport { LAB } from '~/types';\n\n/** Convert HEX to oklab */\nexport default function hex2oklab(input: string, precision?: number): LAB {\n invariant(isHex(input), MESSAGES.inputHex);\n\n return rgb2oklab(hex2rgb(input), precision);\n}\n","import { RAD2DEG } from '~/modules/constants';\nimport { parseInput, restrictValues, round } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, LCH } from '~/types';\n\nconst { atan2, sqrt } = Math;\n\n/** Convert oklab to oklch */\nexport default function oklab2oklch(input: ConverterParameters<LAB>, precision?: number): LCH {\n const { l, a, b } = restrictValues(parseInput(input, 'oklab'));\n\n const c = sqrt(a ** 2 + b ** 2);\n let h = (atan2(b, a) * RAD2DEG + 360) % 360;\n\n if (round(c * 10000) === 0) {\n h = 0;\n }\n\n return restrictValues({ l, c, h }, precision);\n}\n","import { parseInput } from '~/modules/utils';\n\nimport oklab2oklch from '~/converters/oklab2oklch';\nimport rgb2oklab from '~/converters/rgb2oklab';\nimport { ConverterParameters, LCH, RGB } from '~/types';\n\n/** Convert RGB to oklch */\nexport default function rgb2oklch(input: ConverterParameters<RGB>, precision?: number): LCH {\n const value = parseInput(input, 'rgb');\n\n return oklab2oklch(rgb2oklab(value, precision), precision);\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport hex2rgb from '~/converters/hex2rgb';\nimport rgb2oklch from '~/converters/rgb2oklch';\nimport { LCH } from '~/types';\n\n/** Convert HEX to oklch */\nexport default function hex2oklch(input: string, precision?: number): LCH {\n invariant(isHex(input), MESSAGES.inputHex);\n\n return rgb2oklch(hex2rgb(input), precision);\n}\n","import { invariant } from '~/modules/invariant';\nimport { round } from '~/modules/utils';\nimport { isNumber } from '~/modules/validators';\n\n/**\n * Convert hue to RGB using chroma and median point\n */\nexport default function hue2rgb(point: number, chroma: number, h: number): number {\n invariant(isNumber(point) && isNumber(chroma) && isNumber(h), 'point, chroma and h are required');\n let hue = h;\n\n if (hue < 0) {\n hue += 1;\n }\n\n if (hue > 1) {\n hue -= 1;\n }\n\n if (hue < 1 / 6) {\n return round(point + (chroma - point) * 6 * hue, 4);\n }\n\n if (hue < 1 / 2) {\n return round(chroma, 4);\n }\n\n if (hue < 2 / 3) {\n return round(point + (chroma - point) * (2 / 3 - hue) * 6, 4);\n }\n\n return round(point, 4);\n}\n","import hue2rgb from '~/modules/hue2rgb';\nimport { parseInput, round } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, RGB } from '~/types';\n\n/** Convert HSL to RGB */\nexport default function hsl2rgb(input: ConverterParameters<HSL>): RGB {\n const value = parseInput(input, 'hsl');\n\n const h = round(value.h) / 360;\n const s = round(value.s) / 100;\n const l = round(value.l) / 100;\n\n let r;\n let g;\n let b;\n\n let point;\n let chroma;\n\n if (s === 0) {\n r = l;\n g = l;\n b = l;\n } else {\n chroma = l < 0.5 ? l * (1 + s) : l + s - l * s;\n point = 2 * l - chroma;\n\n r = hue2rgb(point, chroma, h + 1 / 3);\n g = hue2rgb(point, chroma, h);\n b = hue2rgb(point, chroma, h - 1 / 3);\n }\n\n return {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(b * 255),\n };\n}\n","import { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HEX, RGB } from '~/types';\n\n/** Convert RGB to HEX */\nexport default function rgb2hex(input: ConverterParameters<RGB>): HEX {\n const rgb = parseInput(input, 'rgb');\n\n return `#${Object.values(rgb)\n .map(d => `0${Math.floor(d).toString(16)}`.slice(-2))\n .join('')}`;\n}\n","import { parseInput } from '~/modules/utils';\n\nimport hsl2rgb from '~/converters/hsl2rgb';\nimport rgb2hex from '~/converters/rgb2hex';\nimport { ConverterParameters, HEX, HSL } from '~/types';\n\n/** Convert HSL to HEX */\nexport default function hsl2hex(input: ConverterParameters<HSL>): HEX {\n const value = parseInput(input, 'hsl');\n\n return rgb2hex(hsl2rgb(value));\n}\n","import { parseInput } from '~/modules/utils';\n\nimport hsl2rgb from '~/converters/hsl2rgb';\nimport rgb2oklab from '~/converters/rgb2oklab';\nimport { ConverterParameters, HSL, LAB } from '~/types';\n\n/** Convert HSL to oklab */\nexport default function hsl2oklab(input: ConverterParameters<HSL>, precision?: number): LAB {\n const value = parseInput(input, 'hsl');\n\n return rgb2oklab(hsl2rgb(value), precision);\n}\n","import { parseInput } from '~/modules/utils';\n\nimport hsl2rgb from '~/converters/hsl2rgb';\nimport rgb2oklch from '~/converters/rgb2oklch';\nimport { ConverterParameters, HSL, LCH } from '~/types';\n\n/** Convert HSL to oklch */\nexport default function hsl2oklch(input: ConverterParameters<HSL>, precision?: number): LCH {\n const value = parseInput(input, 'hsl');\n\n return rgb2oklch(hsl2rgb(value), precision);\n}\n","import { LAB_TO_LMS, LSM_TO_RGB } from '~/modules/constants';\nimport { clamp, parseInput, round } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, RGB } from '~/types';\n\nconst { abs } = Math;\n\nfunction lrgb2rgb(input: number) {\n const absoluteNumber = abs(input);\n const sign = input < 0 ? -1 : 1;\n\n if (absoluteNumber > 0.0031308) {\n return sign * (absoluteNumber ** (1 / 2.4) * 1.055 - 0.055);\n }\n\n return input * 12.92;\n}\n\n/** Convert oklab to RGB */\nexport default function oklab2rgb(input: ConverterParameters<LAB>, precision = 0): RGB {\n const { l: L, a: A, b: B } = parseInput(input, 'oklab');\n\n const l = (L + LAB_TO_LMS.l[0] * A + LAB_TO_LMS.l[1] * B) ** 3;\n const m = (L + LAB_TO_LMS.m[0] * A + LAB_TO_LMS.m[1] * B) ** 3;\n const s = (L + LAB_TO_LMS.s[0] * A + LAB_TO_LMS.s[1] * B) ** 3;\n\n const r = 255 * lrgb2rgb(LSM_TO_RGB.r[0] * l + LSM_TO_RGB.r[1] * m + LSM_TO_RGB.r[2] * s);\n const g = 255 * lrgb2rgb(LSM_TO_RGB.g[0] * l + LSM_TO_RGB.g[1] * m + LSM_TO_RGB.g[2] * s);\n const b = 255 * lrgb2rgb(LSM_TO_RGB.b[0] * l + LSM_TO_RGB.b[1] * m + LSM_TO_RGB.b[2] * s);\n\n return {\n r: clamp(round(r, precision), 0, 255),\n g: clamp(round(g, precision), 0, 255),\n b: clamp(round(b, precision), 0, 255),\n };\n}\n","import { parseInput } from '~/modules/utils';\n\nimport oklab2rgb from '~/converters/oklab2rgb';\nimport rgb2hex from '~/converters/rgb2hex';\nimport { ConverterParameters, HEX, LAB } from '~/types';\n\n/** Convert oklab to HEX */\nexport default function oklab2hex(input: ConverterParameters<LAB>): HEX {\n const value = parseInput(input, 'oklab');\n\n return rgb2hex(oklab2rgb(value));\n}\n","import { parseInput } from '~/modules/utils';\n\nimport oklab2rgb from '~/converters/oklab2rgb';\nimport rgb2hsl from '~/converters/rgb2hsl';\nimport { ConverterParameters, HSL, LAB } from '~/types';\n\n/** Convert oklab to HSL */\nexport default function oklab2hsl(input: ConverterParameters<LAB>): HSL {\n const value = parseInput(input, 'oklab');\n\n return rgb2hsl(oklab2rgb(value));\n}\n","/* eslint-disable prefer-const */\nimport { DEG2RAD } from '~/modules/constants';\nimport { parseInput, restrictValues } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, LCH } from '~/types';\n\nconst { sin, cos } = Math;\n\n/** Convert oklch to oklab */\nexport default function oklch2oklab(input: ConverterParameters<LCH>, precision?: number): LAB {\n /*\n Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.\n These formulas were invented by David Dalrymple to obtain maximum contrast without going\n out of gamut if the parameters are in the range 0-1.\n\n A saturation multiplier was added by Gregor Aisch\n */\n let { l, c, h } = parseInput(input, 'oklch');\n\n /* c8 ignore next 3 */\n if (Number.isNaN(h) || h < 0) {\n h = 0;\n }\n\n return restrictValues({ l, a: c * cos(h * DEG2RAD), b: c * sin(h * DEG2RAD) }, precision);\n}\n","import { parseInput } from '~/modules/utils';\n\nimport oklab2rgb from '~/converters/oklab2rgb';\nimport oklch2oklab from '~/converters/oklch2oklab';\nimport { ConverterParameters, LCH, RGB } from '~/types';\n\n/** Convert oklch to RGB */\nexport default function oklch2rgb(input: ConverterParameters<LCH>, precision = 0): RGB {\n const value = parseInput(input, 'oklch');\n\n return oklab2rgb(oklch2oklab(value), precision);\n}\n","import { parseInput } from '~/modules/utils';\n\nimport oklch2rgb from '~/converters/oklch2rgb';\nimport rgb2hex from '~/converters/rgb2hex';\nimport { ConverterParameters, HEX, LCH } from '~/types';\n\n/** Convert oklch to HEX */\nexport default function oklch2hex(input: ConverterParameters<LCH>): HEX {\n const value = parseInput(input, 'oklch');\n\n return rgb2hex(oklch2rgb(value));\n}\n","import { parseInput } from '~/modules/utils';\n\nimport oklch2rgb from '~/converters/oklch2rgb';\nimport rgb2hsl from '~/converters/rgb2hsl';\nimport { ConverterParameters, HSL, LCH } from '~/types';\n\n/** Convert oklch to HSL */\nexport default function oklch2hsl(input: ConverterParameters<LCH>): HSL {\n const value = parseInput(input, 'oklch');\n\n return rgb2hsl(oklch2rgb(value));\n}\n","import { COLOR_KEYS, MESSAGES } from '~/modules/constants';\nimport { extractAlphaFromHex } from '~/modules/hex-utils';\nimport { invariant } from '~/modules/invariant';\nimport { hasValidMatches, isHex, isString } from '~/modules/validators';\n\nimport hex2rgb from '~/converters/hex2rgb';\nimport { ColorModelKey, PlainObject } from '~/types';\n\nexport type ExtractColorPartsReturn = {\n alpha?: number;\n model: ColorModelKey;\n} & PlainObject<number>;\n\n/**\n * Extract the color parts from a CSS color string.\n * Hex colors are not supported.\n */\nexport default function extractColorParts(input: string) {\n invariant(isString(input), MESSAGES.inputString);\n\n if (isHex(input)) {\n const keys = COLOR_KEYS.rgb;\n const { r, g, b } = hex2rgb(input);\n const alpha = extractAlphaFromHex(input);\n\n return {\n model: 'rgb' as ColorModelKey,\n [keys[0]]: r,\n [keys[1]]: g,\n [keys[2]]: b,\n alpha: alpha < 1 ? alpha : undefined,\n } as ExtractColorPartsReturn;\n }\n\n const colorRegex =\n /(?:(rgb|hsl|oklab|oklch)a?\\s*\\(\\s*([\\d%.-]+)\\s*[ ,/]\\s*([\\d%.-]+)\\s*[ ,/]\\s*([\\d%.-]+)(?:\\s*[ ,/]\\s*([\\d%.-]+))?\\s*\\))/i;\n\n const matches = colorRegex.exec(input);\n\n invariant(hasValidMatches(matches), MESSAGES.invalidCSS);\n\n const model = matches[1] as ColorModelKey;\n const keys = COLOR_KEYS[model];\n let alpha = matches[5] ? parseFloat(matches[5]) : 1;\n\n if (alpha > 1) {\n alpha /= 100;\n }\n\n return {\n model,\n [keys[0]]: parseFloat(matches[2]),\n [keys[1]]: parseFloat(matches[3]),\n [keys[2]]: parseFloat(matches[4]),\n alpha: alpha < 1 ? alpha : undefined,\n } as ExtractColorPartsReturn;\n}\n","import { MESSAGES, PRECISION } from '~/modules/constants';\nimport { CSSColor, cssColors } from '~/modules/css-colors';\nimport { convertAlphaToHex, extractAlphaFromHex, removeAlphaFromHex } from '~/modules/hex-utils';\nimport { invariant } from '~/modules/invariant';\nimport { addAlpha, round } from '~/modules/utils';\nimport { isHex, isNamedColor, isString } from '~/modules/validators';\n\nimport * as converters from '~/converters';\nimport extractColorParts from '~/extract-color-parts';\nimport { ColorReturn, ColorTuple, ColorType } from '~/types';\n\n/**\n * Parse CSS color\n */\nexport default function parseCSS<T extends ColorType>(input: string, format?: T): ColorReturn<T> {\n invariant(isString(input), MESSAGES.inputString);\n\n let result: any;\n\n const value = isNamedColor(input) ? cssColors[input.toLowerCase() as CSSColor] : input;\n\n const output = format ?? (isHex(value) ? 'hex' : extractColorParts(value).model);\n\n const colorParams = (params: Record<string, number>) => Object.values(params) as ColorTuple;\n\n if (isHex(value)) {\n const alpha = extractAlphaFromHex(value);\n\n switch (output) {\n case 'hsl': {\n result = addAlpha(converters.hex2hsl(value), alpha);\n break;\n }\n case 'oklab': {\n result = addAlpha(converters.hex2oklab(value), alpha);\n break;\n }\n case 'oklch': {\n result = addAlpha(converters.hex2oklch(value), alpha);\n break;\n }\n case 'rgb': {\n result = addAlpha(converters.hex2rgb(value), alpha);\n break;\n }\n default: {\n result = `${removeAlphaFromHex(value)}${alpha !== 1 ? convertAlphaToHex(alpha) : ''}`;\n break;\n }\n }\n\n return result as ColorReturn<T>;\n }\n\n switch (output) {\n case 'hsl': {\n const { alpha, model, ...color } = extractColorParts(value);\n\n if (['oklab', 'oklch'].includes(model) && color.l > 1) {\n color.l = round(color.l / 100, PRECISION);\n }\n\n result = addAlpha(\n model === 'hsl' ? color : converters[`${model}2hsl`](colorParams(color)),\n alpha,\n );\n\n break;\n }\n case 'oklab': {\n const { alpha, model, ...color } = extractColorParts(value);\n\n if (['oklab', 'oklch'].includes(model) && color.l > 1) {\n color.l = round(color.l / 100, PRECISION);\n }\n\n result = addAlpha(\n model === 'oklab' ? color : converters[`${model}2oklab`](colorParams(color)),\n alpha,\n );\n\n break;\n }\n case 'oklch': {\n const { alpha, model, ...color } = extractColorParts(value);\n\n if (['oklab', 'oklch'].includes(model) && color.l > 1) {\n color.l = round(color.l / 100, PRECISION);\n }\n\n result = addAlpha(\n model === 'oklch' ? color : converters[`${model}2oklch`](colorParams(color)),\n alpha,\n );\n break;\n }\n case 'rgb': {\n const { alpha, model, ...color } = extractColorParts(value);\n\n if (['oklab', 'oklch'].includes(model) && color.l > 1) {\n color.l /= 100;\n }\n\n result = addAlpha(\n model === 'rgb' ? color : converters[`${model}2rgb`](colorParams(color)),\n alpha,\n );\n break;\n }\n\n case 'hex':\n default: {\n const { alpha, model, ...color } = extractColorParts(value);\n let alphaPrefix = '';\n\n if (['oklab', 'oklch'].includes(model) && color.l > 1) {\n color.l = round(color.l / 100, PRECISION);\n }\n\n if (alpha) {\n alphaPrefix = convertAlphaToHex(alpha);\n }\n\n result = `${converters[`${model}2hex`](colorParams(color))}${alphaPrefix}`;\n\n break;\n }\n }\n\n return result as ColorReturn<T>;\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { addAlphaToHex } from '~/modules/hex-utils';\nimport { invariant } from '~/modules/invariant';\nimport { addAlpha, limit } from '~/modules/utils';\nimport { isHex, isHSL, isLAB, isLCH, isPlainObject, isRGB, isString } from '~/modules/validators';\n\nimport * as converters from '~/converters';\nimport extractColorParts from '~/extract-color-parts';\nimport parseCSS from '~/parse-css';\nimport { Colors, HSL, LAB, LCH, PlainObject, RGB } from '~/types';\n\nexport default function parseColor(color: string | HSL | LAB | LCH | RGB): Colors {\n invariant(!!color, MESSAGES.input);\n\n const output: PlainObject = {};\n\n if (isString(color)) {\n const { alpha = 1 } = extractColorParts(color);\n const type = isHex(color) ? 'hex' : extractColorParts(color).model;\n\n output.hex = addAlphaToHex(parseCSS(color, 'hex'), alpha);\n output.hsl = addAlpha(parseCSS(color, 'hsl'), alpha);\n output.oklab = addAlpha(parseCSS(color, 'oklab'), alpha);\n output.oklch = addAlpha(parseCSS(color, 'oklch'), alpha);\n output.rgb = addAlpha(parseCSS(color, 'rgb'), alpha);\n\n output.alpha = alpha;\n output.type = type;\n } else if (isPlainObject(color)) {\n const { alpha = 1 } = color;\n\n if (isHSL(color)) {\n output.hsl = {\n h: limit(color.h, 'hsl', 'h'),\n s: limit(color.s, 'hsl', 's'),\n l: limit(color.l, 'hsl', 'l'),\n };\n output.rgb = converters.hsl2rgb(output.hsl);\n output.oklab = converters.hsl2oklab(output.hsl);\n output.oklch = converters.hsl2oklch(output.hsl);\n output.type = 'hsl';\n } else if (isLAB(color)) {\n output.hsl = converters.oklab2hsl(color);\n output.oklab = color;\n output.oklch = converters.oklab2oklch(color);\n output.rgb = converters.oklab2rgb(color);\n output.type = 'oklab';\n } else if (isLCH(color)) {\n output.hsl = converters.oklch2hsl(color);\n output.oklab = converters.oklch2oklab(color);\n output.oklch = color;\n output.rgb = converters.oklch2rgb(color);\n output.type = 'oklch';\n } else if (isRGB(color)) {\n output.rgb = {\n r: limit(color.r, 'rgb', 'r'),\n g: limit(color.g, 'rgb', 'g'),\n b: limit(color.b, 'rgb', 'b'),\n };\n output.hsl = converters.rgb2hsl(output.rgb);\n output.oklab = converters.rgb2oklab(output.rgb);\n output.oklch = converters.rgb2oklch(output.rgb);\n output.type = 'rgb';\n } else {\n throw new Error('invalid color');\n }\n\n output.hex = addAlphaToHex(converters.hsl2hex(output.hsl), alpha);\n output.hsl = addAlpha(output.hsl, alpha);\n output.oklab = addAlpha(output.oklab, alpha);\n output.oklch = addAlpha(output.oklch, alpha);\n output.rgb = addAlpha(output.rgb, alpha);\n\n output.alpha = alpha;\n } else {\n throw new Error(MESSAGES.input);\n }\n\n return output as Colors;\n}\n","import { MESSAGES, PRECISION } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { round } from '~/modules/utils';\nimport { isString } from '~/modules/validators';\n\nimport parseCSS from '~/parse-css';\n\n/**\n * Get the brightness difference between 2 colors.\n */\nexport default function brightnessDifference(\n left: string,\n right: string,\n precision = PRECISION,\n): number {\n invariant(isString(left), MESSAGES.left);\n invariant(isString(right), MESSAGES.right);\n\n const RGBLeft = parseCSS(left, 'rgb');\n const RGBRight = parseCSS(right, 'rgb');\n\n const brightnessLeft = (RGBLeft.r * 299 + RGBLeft.g * 587 + RGBLeft.b * 114) / 1000;\n const brightnessRight = (RGBRight.r * 299 + RGBRight.g * 587 + RGBRight.b * 114) / 1000;\n\n return round(Math.abs(brightnessRight - brightnessLeft), precision);\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { round } from '~/modules/utils';\nimport { isString } from '~/modules/validators';\n\nimport parseCSS from '~/parse-css';\n\n/**\n * Get the chroma of a color.\n */\nexport default function chroma(input: string): number {\n invariant(isString(input), MESSAGES.inputString);\n\n const { r, g, b } = parseCSS(input, 'rgb');\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n\n return round((max - min) / 255, 4);\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isString } from '~/modules/validators';\n\nimport parseCSS from '~/parse-css';\n\n/**\n * Get the difference between 2 colors.\n */\nexport default function colorDifference(left: string, right: string): number {\n invariant(isString(left), MESSAGES.left);\n invariant(isString(right), MESSAGES.right);\n\n const RGBLeft = parseCSS(left, 'rgb');\n const RGBRight = parseCSS(right, 'rgb');\n\n return (\n Math.max(RGBLeft.r, RGBRight.r) -\n Math.min(RGBLeft.r, RGBRight.r) +\n (Math.max(RGBLeft.g, RGBRight.g) - Math.min(RGBLeft.g, RGBRight.g)) +\n (Math.max(RGBLeft.b, RGBRight.b) - Math.min(RGBLeft.b, RGBRight.b))\n );\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { round } from '~/modules/utils';\nimport { isString } from '~/modules/validators';\n\nimport parseCSS from '~/parse-css';\n\n/**\n * Get the luminance of a color.\n */\nexport default function luminance(input: string): number {\n invariant(isString(input), MESSAGES.inputString);\n\n const { r, g, b } = parseCSS(input, 'rgb');\n\n const rgb = [r / 255, g / 255, b / 255];\n\n for (let index = 0; index < rgb.length; index++) {\n if (rgb[index] <= 0.03928) {\n rgb[index] /= 12.92;\n } else {\n rgb[index] = ((rgb[index] + 0.055) / 1.055) ** 2.4;\n }\n }\n\n return round(0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2], 4);\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { round } from '~/modules/utils';\nimport { isString } from '~/modules/validators';\n\nimport getLuminance from '~/luminance';\n\n/**\n * Get the color contrast between 2 colors.\n */\nexport default function contrast(left: string, right: string): number {\n invariant(isString(left), MESSAGES.left);\n invariant(isString(right), MESSAGES.right);\n\n const LuminanceLeft = getLuminance(left);\n const LuminanceRight = getLuminance(right);\n\n return round(\n LuminanceLeft >= LuminanceRight\n ? (LuminanceLeft + 0.05) / (LuminanceRight + 0.05)\n : (LuminanceRight + 0.05) / (LuminanceLeft + 0.05),\n );\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isString } from '~/modules/validators';\n\nimport getBrightnessDifference from '~/brightness-difference';\nimport getColorDifference from '~/color-difference';\nimport getContrast from '~/contrast';\nimport { Analysis } from '~/types';\n\n/**\n * Check 2 colors for WCAG compliance.\n */\nexport default function compare(left: string, right: string): Analysis {\n invariant(isString(left), MESSAGES.left);\n invariant(isString(right), MESSAGES.right);\n\n const colorThreshold = 500;\n const brightnessThreshold = 125;\n\n const colorDifference = getColorDifference(left, right);\n const contrast = getContrast(left, right);\n const brightnessDifference = getBrightnessDifference(left, right);\n\n const isBright = brightnessDifference >= brightnessThreshold;\n const hasEnoughDifference = colorDifference >= colorThreshold;\n\n let compliant = 0;\n\n if (isBright && hasEnoughDifference) {\n compliant = 2;\n } else if (isBright || hasEnoughDifference) {\n compliant = 1;\n }\n\n return {\n brightnessDifference,\n colorDifference,\n compliant,\n contrast,\n largeAA: contrast >= 3,\n largeAAA: contrast >= 4.5,\n normalAA: contrast >= 4.5,\n normalAAA: contrast >= 7,\n };\n}\n","import { MESSAGES, PRECISION } from '~/modules/constants';\nimport { CSSColor, cssColors } from '~/modules/css-colors';\nimport { convertAlphaToHex, removeAlphaFromHex } from '~/modules/hex-utils';\nimport { invariant } from '~/modules/invariant';\nimport { restrictValues, round } from '~/modules/utils';\nimport {\n isHex,\n isHSL,\n isLAB,\n isLCH,\n isNamedColor,\n isRGB,\n isString,\n isValidColorModel,\n} from '~/modules/validators';\n\nimport * as converters from '~/converters';\nimport extractColorParts from '~/extract-color-parts';\nimport { Alpha, ColorModel, ColorReturn, ColorType, HEX } from '~/types';\n\nexport interface FormatCSSOptions {\n /**\n * The alpha value of the color.\n */\n alpha?: Alpha;\n /**\n * Output color format.\n * @default 'hex'\n */\n format?: ColorType;\n /**\n * The number of digits of the output.\n * @default 5\n */\n precision?: number;\n /**\n * The separator between the values.\n *\n * oklab and oklch always use space as a separator.\n * @default ' '\n */\n separator?: string;\n}\n\nfunction getColorModel<T extends ColorModel | string>(input: T): ColorType {\n if (isHex(input) || isNamedColor(input)) {\n return 'hex';\n }\n\n if (isString(input)) {\n return extractColorParts(input).model;\n } else if (isHSL(input)) {\n return 'hsl';\n } else if (isLAB(input)) {\n return 'oklab';\n } else if (isLCH(input)) {\n return 'oklch';\n } else if (isRGB(input)) {\n return 'rgb';\n }\n\n throw new Error(MESSAGES.invalid);\n}\n\nfunction getColorValue<TInput extends ColorModel | string, TOutput extends ColorType>(\n input: TInput,\n output: TOutput,\n): ColorReturn<TOutput> {\n const value = isNamedColor(input) ? cssColors[input.toLowerCase() as CSSColor] : input;\n\n const from = getColorModel(value);\n\n if (from === output) {\n return value as ColorReturn<TOutput>;\n }\n\n const converterKey = `${from}2${output}` as keyof typeof converters; // Retrieve the converter function dynamically\n const converter = (converters as Record<string, (x: any) => any>)[converterKey];\n\n if (!converter) {\n throw new Error(`Converter not found for ${from} to ${output}`);\n }\n\n switch (from) {\n case 'hex': {\n if (output === 'hex') {\n return value as ColorReturn<TOutput>;\n }\n\n return converter(value);\n }\n case 'hsl': {\n if (output === 'hsl') {\n return value as ColorReturn<TOutput>;\n }\n\n return converter(value);\n }\n case 'oklab': {\n if (output === 'oklab') {\n return value as ColorReturn<TOutput>;\n }\n\n return converter(value);\n }\n case 'oklch': {\n if (output === 'oklch') {\n return value as ColorReturn<TOutput>;\n }\n\n return converter(value);\n }\n default: {\n if (output === 'rgb') {\n return value as ColorReturn<TOutput>;\n }\n\n return converter(value);\n }\n }\n}\n\nexport default function formatCSS<T extends ColorModel | HEX>(\n input: T,\n options: FormatCSSOptions = {},\n): string {\n invariant(isHex(input) || isValidColorModel(input), MESSAGES.invalid);\n\n const { alpha, format = 'hex', precision = PRECISION, separator: base