accessible-colors
Version:
Utility functions for working with colors in adherence to WCAG 2.1 guidelines.
1 lines • 21.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/helpers.ts","../src/index.ts"],"sourcesContent":["import { HSL, RGB } from './types';\n\n/**\n * hslToHex will return the hex representation of an hsl color.\n * @param hsl - hue, saturation, and lightness values of a color represented as numbers between 0 and 1 {h, s, l}\n * @returns the hex representation of a color (e.g. #000000)\n */\nexport const hslToHex = (hsl: HSL): string => {\n const rgb = hslToRgb(hsl);\n\n return rgbToHex(rgb);\n};\n\n/**\n * hexToHsl will return the HSL representation of a hex color.\n * @param str - hex representation of a color (e.g. #000000)\n * @returns the HSL representation of a color {h, s, l}\n */\nexport const hexToHsl = (str: string): HSL => {\n const rgb = hexToRgb(str);\n\n return rgbToHsl(rgb);\n};\n\n/**\n * rgbToHex will return the hex representation of an rgb color.\n * @param rgb - red, green, and blue values of a color represented as numbers between 0 and 255 {r, g, b}\n * @returns the hex representation of a color (e.g. #000000)\n */\nexport const rgbToHex = (rgb: RGB): string => {\n const { r, g, b } = rgb;\n const hex = ((r << 16) | (g << 8) | b).toString(16);\n return `#${hex.padStart(6, '0')}`;\n};\n\n/**\n * hexToRgb will return the red, green, and blue values of a color represented as numbers between 0 and 255.\n * @param hex - hex representation of a color (e.g. #000000)\n * @returns red, green, and blue values of a color represented as numbers between 0 and 255 {r, g, b}\n */\nexport const hexToRgb = (hex: string): RGB => {\n const bigint = parseInt(hex.replace(/^#/, ''), 16);\n const r = (bigint >> 16) & 255;\n const g = (bigint >> 8) & 255;\n const b = bigint & 255;\n\n return { r, g, b };\n};\n\n/**\n * Converts an HSL color value to RGB. Conversion formula\n * adapted from http://en.wikipedia.org/wiki/HSL_color_space.\n * Assumes h, s, and l are contained in the set [0, 1] and\n * returns r, g, and b in the set [0, 255].\n *\n * Shamelessly copied from https://stackoverflow.com/a/9493060/\n * with attributions to the original author.\n *\n * @param h - the hue\n * @param s - the saturation\n * @param l - the lightness\n * @returns the RGB representation\n */\nexport const hslToRgb = ({ h, s, l }: HSL): RGB => {\n let r, g, b;\n\n if (s == 0) {\n r = g = b = l; // achromatic\n } else {\n const hue2rgb = (p: number, q: number, t: number) => {\n if (t < 0) t += 1;\n if (t > 1) t -= 1;\n if (t < 1 / 6) return p + (q - p) * 6 * t;\n if (t < 1 / 2) return q;\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n return p;\n };\n\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n const p = 2 * l - q;\n r = hue2rgb(p, q, h + 1 / 3);\n g = hue2rgb(p, q, h);\n b = hue2rgb(p, q, 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\n/**\n *\n * Converts an RGB color value to HSL. Conversion formula\n * adapted from http://en.wikipedia.org/wiki/HSL_color_space.\n * Assumes r, g, and b are contained in the set [0, 255] and\n * returns h, s, and l in the set [0, 1].\n *\n * Shamelessly copied from https://stackoverflow.com/a/9493060/\n * with attributions to the original author.\n *\n * @param r - the red color value\n * @param g - the green color value\n * @param b - the blue color value\n * @returns the HSL representation\n */\nexport const rgbToHsl = ({ r, g, b }: RGB): HSL => {\n r /= 255;\n g /= 255;\n b /= 255;\n const max = Math.max(r, g, b),\n min = Math.min(r, g, b);\n let h, s;\n const l = (max + min) / 2;\n\n if (max == min) {\n h = s = 0; // achromatic\n } else {\n const d = max - min;\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n switch (max) {\n case r:\n h = (g - b) / d + (g < b ? 6 : 0);\n break;\n case g:\n h = (b - r) / d + 2;\n break;\n case b:\n h = (r - g) / d + 4;\n break;\n }\n // @ts-ignore\n h /= 6;\n }\n\n // @ts-ignore\n return { h, s, l };\n};\n\n/**\n * binarySearchContrast will run a binary search to find the closest accessible color provided a fixed color,\n * a starting color, and a direction to search in.\n * @param change - the color to change, with the lightness value set to the starting point.\n * @param fixed - the fixed color to use for the contrast ratio calculation.\n * @param direction - the direction to search in, either 'lighten' or 'darken'.\n * @param contrastFn - the contrast function to use to determine if a color is accessible.\n * @param large - whether the text should be considered large, adjusting the contrast ratio requirement to 3:1.\n * @returns the closest accessible color to the starting point.\n */\nexport const binarySearchContrast = (\n change: HSL,\n fixed: HSL,\n direction: 'lighten' | 'darken',\n contrastFn: (c: string, f: string, l?: boolean) => boolean | null,\n large?: boolean\n) => {\n const { l, ...hs } = change;\n\n let max = direction === 'lighten' ? 1 : l;\n let min = direction === 'lighten' ? l : 0;\n\n let minColor: string = hslToHex({ ...hs, l: min });\n let maxColor: string = hslToHex({ ...hs, l: max });\n const fixedHex = hslToHex(fixed);\n\n // If the contrast at the minimum or maximum is unacceptable, then it's not worth\n // the time to check.\n if (\n !contrastFn(direction === 'lighten' ? maxColor : minColor, fixedHex, large)\n ) {\n return null;\n }\n\n let prevMin: string | null = null;\n let prevMax: string | null = null;\n\n while (minColor !== prevMin || maxColor !== prevMax) {\n prevMin = minColor;\n prevMax = maxColor;\n\n const adjusted = (min + max) / 2;\n\n const stringified = hslToHex({ ...hs, l: adjusted });\n if (direction === 'lighten') {\n if (!contrastFn(stringified, fixedHex, large)) {\n min = adjusted;\n minColor = hslToHex({ ...hs, l: adjusted });\n } else {\n max = adjusted;\n maxColor = hslToHex({ ...hs, l: adjusted });\n }\n }\n if (direction === 'darken') {\n if (!contrastFn(stringified, fixedHex, large)) {\n max = adjusted;\n maxColor = hslToHex({ ...hs, l: adjusted });\n } else {\n min = adjusted;\n minColor = hslToHex({ ...hs, l: adjusted });\n }\n }\n }\n\n return hexToHsl(direction === 'lighten' ? maxColor : minColor);\n};\n\n/**\n * suggestColorVariant will suggest a color variant that is accessible against a fixed color.\n * @param colorToChange - the color to change.\n * @param colorToKeep - the color to keep.\n * @param compareFn - the contrast function to use to determine if a color is accessible.\n * @param large - whether the text should be considered large, adjusting the contrast ratio requirements.\n * @returns the suggested color variant.\n */\nexport const suggestColorVariant = (\n colorToChange: string,\n colorToKeep: string,\n compareFn: (\n color1: string,\n color2: string,\n large?: boolean\n ) => boolean | null,\n large?: boolean\n) => {\n const hslChange = hexToHsl(colorToChange);\n const hslKeep = hexToHsl(colorToKeep);\n if (!hslKeep || !hslChange) {\n return null;\n }\n if (compareFn(colorToChange, colorToKeep, large)) {\n return colorToChange;\n }\n const darker = binarySearchContrast(\n hslChange,\n hslKeep,\n 'darken',\n compareFn,\n large\n );\n const lighter = binarySearchContrast(\n hslChange,\n hslKeep,\n 'lighten',\n compareFn,\n large\n );\n if (darker !== null && lighter !== null) {\n const darkerDiff = Math.abs(hslChange.l - darker.l);\n const lighterDiff = Math.abs(hslChange.l - lighter.l);\n return hslToHex(darkerDiff < lighterDiff ? darker : lighter);\n }\n if (darker === null && lighter !== null) {\n return hslToHex(lighter);\n }\n if (lighter === null && darker !== null) {\n return hslToHex(darker);\n }\n return null;\n};\n","import { hexToRgb, suggestColorVariant } from './helpers';\n\n/**\n * Original luminance function (used here, WCAG2.0 standard):\n * @link https://www.w3.org/TR/WCAG20/#relativeluminancedef\n * L = 0.2126 * R + 0.7152 * G + 0.0722 * B\n * @param color (r, g, b) color\n * @returns a number between 0 and 1 representing the linear luminance of the color\n */\nexport const getLuminance = (color: string) => {\n if (!color) {\n return null;\n }\n const rgb = hexToRgb(color);\n if (!rgb) {\n return null;\n }\n const [r, g, b] = Object.values(rgb).map((v) => {\n const value = v / 255;\n return value <= 0.03928\n ? value / 12.92\n : Math.pow((value + 0.055) / 1.055, 2.4);\n });\n\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n};\n\n/**\n * @link https://www.w3.org/TR/WCAG20/#contrast-ratiodef\n * Produces a contrast ratio between two colors between 1 and 21. This\n * is expressed as 1:1 - 21:1, where contrast of 4.5:1 is considered\n * to be the minimum for normal text and 3:1 for large text.\n * @param color1 - first color to compare in hex format (e.g. #000000)\n * @param color2 - second color to compare in hex format (e.g. #ffffff)\n * @param precision - number of decimal places to round to\n * @returns\n */\nexport const getContrast = (\n color1: string | null,\n color2: string | null,\n precision = 3\n) => {\n if (color1 === null || color2 === null) {\n return null;\n }\n const luminance1 = getLuminance(color1);\n const luminance2 = getLuminance(color2);\n if (luminance1 === null || luminance2 === null) {\n return null;\n }\n const [light, dark] = [luminance1, luminance2].sort((a, b) => b - a);\n return (\n Math.round(((light + 0.05) / (dark + 0.05)) * 10 ** precision) /\n 10 ** precision\n );\n};\n\n/**\n * isContrasting returns true if the constrast ratio between two specified colors is at least the specified ratio.\n * @param color1 - first color to compare in hex format (e.g. #000000)\n * @param color2 - second color to compare in hex format (e.g. #ffffff)\n * @param ratio - the contrast ratio to compare against. Should be between 1 and 21\n * @returns - true if the contrast ratio is at least the specified ratio\n */\nexport const isContrasting = (\n color1: string,\n color2: string,\n ratio: number\n) => {\n const contrast = getContrast(color1, color2);\n if (!contrast) {\n return null;\n }\n return contrast >= ratio;\n};\n\n/**\n * isAAContrast returns true if the constrast ratio between two specified colors satisfies the WCAG 2.0 AA standard\n * @link https://www.w3.org/WAI/GL/UNDERSTANDING-WCAG20/visual-audio-contrast7.html\n * @param color1 - first color to compare in hex format (e.g. #000000)\n * @param color2 - second color to compare in hex format (e.g. #ffffff)\n * @param large Large text is defined as at least 14 point (18.66px) + bold, or 18 point (24px) without bold. @link https://www.w3.org/WAI/GL/UNDERSTANDING-WCAG20/visual-audio-contrast7.html#larger-scaledef\n * @returns - true if the contrast ratio is at least 4.5:1 (normal text) or 3:1 (large text)\n */\nexport const isAAContrast = (color1: string, color2: string, large = false) => {\n return isContrasting(color1, color2, large ? 3 : 4.5);\n};\n\n/**\n * isAAAContrast returns true if the constrast ratio between two specified colors satisfies the WCAG 2.0 AAA standard\n * @link https://www.w3.org/WAI/GL/UNDERSTANDING-WCAG20/visual-audio-contrast7.html\n * @param color1 - first color to compare in hex format (e.g. #000000)\n * @param color2 - second color to compare in hex format (e.g. #ffffff)\n * @param large Large text is defined as at least 14 point (18.66px) + bold, or 18 point (24px) without bold. @link https://www.w3.org/WAI/GL/UNDERSTANDING-WCAG20/visual-audio-contrast7.html#larger-scaledef\n * @returns - true if the contrast ratio is at least 7:1 (normal text) or 4.5:1 (large text)\n */\nexport const isAAAContrast = (\n color1: string,\n color2: string,\n large = false\n) => {\n return isContrasting(color1, color2, large ? 4.5 : 7);\n};\n\n/**\n * randomColor will return a random color in hex format (e.g. `'#000000'`)\n * @returns a random color in hex format (e.g. `'#000000'`)\n */\nexport const randomColor = () => {\n const hex = Math.floor(Math.random() * 16777215).toString(16);\n return `#${hex.padStart(6, '0')}`;\n};\n\n/**\n * getRandomAAColor will return a random color that is accessible based on the\n * WCAG 2.0 AA standard, which requires a contrast ratio of at least 4.5:1.\n * @param background - the background color to use for the contrast ratio calculation.\n * @param large - whether the text should be considered large, adjusting the contrast ratio requirement to 3:1.\n * @returns a random color that is accessible based on the WCAG 2.0 AA standard.\n */\nexport const getRandomAAColor = (\n background: string,\n large = false\n): string | null => {\n let color = randomColor();\n let attempts = 0;\n while (!isAAContrast(background, color, large)) {\n if (attempts++ > 1000) {\n return null; // could not find a color that meets the contrast ratio within a reasonable number of tries\n }\n color = randomColor();\n }\n return color;\n};\n\n/**\n * getRandomAAAColor will return a random color that is accessible based on the\n * WCAG 2.0 AAA standard, which requires a contrast ratio of at least 7:1. It will\n * take into account the luminance of the background color (hash).\n * @param background - the background color to use for the contrast ratio calculation.\n * @param large - whether the text should be considered large, adjusting the contrast ratio requirement to 4.5:1.\n * @returns a random color that is accessible based on the WCAG 2.0 AAA standard.\n */\nexport const getRandomAAAColor = (\n background: string,\n large = false\n): string | null => {\n let color = randomColor();\n let attempts = 0;\n while (!isAAAContrast(background, color, large)) {\n if (attempts++ > 1000) {\n return null; // could not find a color that meets the contrast ratio within a reasonable number of tries\n }\n color = randomColor();\n }\n\n return color;\n};\n\n/**\n * suggestAAColor will return a close accessible color to the specified color with WCAG AA compatibility.\n * @param colorToChange - the color we want to find a close accessible color for.\n * @param colorToKeep - the color we want to keep the contrast ratio with.\n * @param large - whether the text should be considered large, adjusting the contrast ratio requirement to 3:1.\n * @returns a close accessible color to the specified `colorToChange` relative to the `colorToKeep`, or `null` if no accessible color can be found.\n */\nexport const suggestAAColorVariant = (\n colorToChange: string,\n colorToKeep: string,\n large?: boolean\n): string | null => {\n return suggestColorVariant(colorToChange, colorToKeep, isAAContrast, large);\n};\n\n/**\n * suggestAAAColor will return a close accessible color to the specified color with WCAG AAA compatibility.\n * @param colorToChange - the color we want to find a close accessible color for.\n * @param colorToKeep - the color we want to keep the contrast ratio with.\n * @param large - whether the text should be considered large, adjusting the contrast ratio requirement to 4.5:1.\n * @returns a close accessible color to the specified `colorToChange` relative to the `colorToKeep`, or `null` if no accessible color can be found.\n */\nexport const suggestAAAColorVariant = (\n colorToChange: string,\n colorToKeep: string,\n large?: boolean\n): string | null => {\n return suggestColorVariant(colorToChange, colorToKeep, isAAAContrast, large);\n};\n"],"mappings":";;;;;;AAOO,IAAMA,EAAYC,GAAqB,CAC5C,IAAMC,EAAMC,EAASF,CAAG,EAExB,OAAOG,EAASF,CAAG,CACrB,EAOaG,EAAYC,GAAqB,CAC5C,IAAMJ,EAAMK,EAASD,CAAG,EAExB,OAAOE,EAASN,CAAG,CACrB,EAOaE,EAAYF,GAAqB,CAC5C,GAAM,CAAE,EAAAO,EAAG,EAAAC,EAAG,EAAAC,CAAE,EAAIT,EAEpB,MAAO,KADOO,GAAK,GAAOC,GAAK,EAAKC,GAAG,SAAS,EAAE,EACnC,SAAS,EAAG,GAAG,CAAC,EACjC,EAOaJ,EAAYK,GAAqB,CAC5C,IAAMC,EAAS,SAASD,EAAI,QAAQ,KAAM,EAAE,EAAG,EAAE,EAC3CH,EAAKI,GAAU,GAAM,IACrBH,EAAKG,GAAU,EAAK,IACpBF,EAAIE,EAAS,IAEnB,MAAO,CAAE,EAAAJ,EAAG,EAAAC,EAAG,EAAAC,CAAE,CACnB,EAgBaR,EAAW,CAAC,CAAE,EAAAW,EAAG,EAAAC,EAAG,EAAAC,CAAE,IAAgB,CACjD,IAAIP,EAAGC,EAAGC,EAEV,GAAII,GAAK,EACPN,EAAIC,EAAIC,EAAIK,MACP,CACL,IAAMC,EAAU,CAACC,EAAWC,EAAWC,KACjCA,EAAI,IAAGA,GAAK,GACZA,EAAI,IAAGA,GAAK,GACZA,EAAI,mBAAcF,GAAKC,EAAID,GAAK,EAAIE,EACpCA,EAAI,GAAcD,EAClBC,EAAI,kBAAcF,GAAKC,EAAID,IAAM,kBAAQE,GAAK,EAC3CF,GAGHC,EAAIH,EAAI,GAAMA,GAAK,EAAID,GAAKC,EAAID,EAAIC,EAAID,EACxCG,EAAI,EAAIF,EAAIG,EAClBV,EAAIQ,EAAQC,EAAGC,EAAGL,EAAI,EAAI,CAAC,EAC3BJ,EAAIO,EAAQC,EAAGC,EAAGL,CAAC,EACnBH,EAAIM,EAAQC,EAAGC,EAAGL,EAAI,EAAI,CAAC,CAC7B,CAEA,MAAO,CACL,EAAG,KAAK,MAAML,EAAI,GAAG,EACrB,EAAG,KAAK,MAAMC,EAAI,GAAG,EACrB,EAAG,KAAK,MAAMC,EAAI,GAAG,CACvB,CACF,EAiBaH,EAAW,CAAC,CAAE,EAAG,EAAAE,EAAG,EAAAC,CAAE,IAAgB,CACjD,GAAK,IACLD,GAAK,IACLC,GAAK,IACL,IAAMU,EAAM,KAAK,IAAI,EAAGX,EAAGC,CAAC,EAC1BW,EAAM,KAAK,IAAI,EAAGZ,EAAGC,CAAC,EACpBG,EAAGC,EACDC,GAAKK,EAAMC,GAAO,EAExB,GAAID,GAAOC,EACTR,EAAIC,EAAI,MACH,CACL,IAAMQ,EAAIF,EAAMC,EAEhB,OADAP,EAAIC,EAAI,GAAMO,GAAK,EAAIF,EAAMC,GAAOC,GAAKF,EAAMC,GACvCD,EAAK,CACX,KAAK,EACHP,GAAKJ,EAAIC,GAAKY,GAAKb,EAAIC,EAAI,EAAI,GAC/B,MACF,KAAKD,EACHI,GAAKH,EAAI,GAAKY,EAAI,EAClB,MACF,KAAKZ,EACHG,GAAK,EAAIJ,GAAKa,EAAI,EAClB,KACJ,CAEAT,GAAK,CACP,CAGA,MAAO,CAAE,EAAAA,EAAG,EAAAC,EAAG,EAAAC,CAAE,CACnB,EAYaQ,EAAuB,CAClCC,EACAC,EACAC,EACAC,EACAC,IACG,CACH,GAAM,CAAE,EAAAb,EAAG,GAAGc,CAAG,EAAIL,EAEjBJ,EAAMM,IAAc,UAAY,EAAIX,EACpCM,EAAMK,IAAc,UAAYX,EAAI,EAEpCe,EAAmB/B,EAAS,CAAE,GAAG8B,EAAI,EAAGR,CAAI,CAAC,EAC7CU,EAAmBhC,EAAS,CAAE,GAAG8B,EAAI,EAAGT,CAAI,CAAC,EAC3CY,EAAWjC,EAAS0B,CAAK,EAI/B,GACE,CAACE,EAAWD,IAAc,UAAYK,EAAWD,EAAUE,EAAUJ,CAAK,EAE1E,OAAO,KAGT,IAAIK,EAAyB,KACzBC,EAAyB,KAE7B,KAAOJ,IAAaG,GAAWF,IAAaG,GAAS,CACnDD,EAAUH,EACVI,EAAUH,EAEV,IAAMI,GAAYd,EAAMD,GAAO,EAEzBgB,EAAcrC,EAAS,CAAE,GAAG8B,EAAI,EAAGM,CAAS,CAAC,EAC/CT,IAAc,YACXC,EAAWS,EAAaJ,EAAUJ,CAAK,GAI1CR,EAAMe,EACNJ,EAAWhC,EAAS,CAAE,GAAG8B,EAAI,EAAGM,CAAS,CAAC,IAJ1Cd,EAAMc,EACNL,EAAW/B,EAAS,CAAE,GAAG8B,EAAI,EAAGM,CAAS,CAAC,IAM1CT,IAAc,WACXC,EAAWS,EAAaJ,EAAUJ,CAAK,GAI1CP,EAAMc,EACNL,EAAW/B,EAAS,CAAE,GAAG8B,EAAI,EAAGM,CAAS,CAAC,IAJ1Cf,EAAMe,EACNJ,EAAWhC,EAAS,CAAE,GAAG8B,EAAI,EAAGM,CAAS,CAAC,GAMhD,CAEA,OAAO/B,EAASsB,IAAc,UAAYK,EAAWD,CAAQ,CAC/D,EAUaO,EAAsB,CACjCC,EACAC,EACAC,EAKAZ,IACG,CACH,IAAMa,EAAYrC,EAASkC,CAAa,EAClCI,EAAUtC,EAASmC,CAAW,EACpC,GAAI,CAACG,GAAW,CAACD,EACf,OAAO,KAET,GAAID,EAAUF,EAAeC,EAAaX,CAAK,EAC7C,OAAOU,EAET,IAAMK,EAASpB,EACbkB,EACAC,EACA,SACAF,EACAZ,CACF,EACMgB,EAAUrB,EACdkB,EACAC,EACA,UACAF,EACAZ,CACF,EACA,GAAIe,IAAW,MAAQC,IAAY,KAAM,CACvC,IAAMC,EAAa,KAAK,IAAIJ,EAAU,EAAIE,EAAO,CAAC,EAC5CG,EAAc,KAAK,IAAIL,EAAU,EAAIG,EAAQ,CAAC,EACpD,OAAO7C,EAAS8C,EAAaC,EAAcH,EAASC,CAAO,CAC7D,CACA,OAAID,IAAW,MAAQC,IAAY,KAC1B7C,EAAS6C,CAAO,EAErBA,IAAY,MAAQD,IAAW,KAC1B5C,EAAS4C,CAAM,EAEjB,IACT,EC1PO,IAAMI,EAAgBC,GAAkB,CAC7C,GAAI,CAACA,EACH,OAAO,KAET,IAAMC,EAAMC,EAASF,CAAK,EAC1B,GAAI,CAACC,EACH,OAAO,KAET,GAAM,CAACE,EAAGC,EAAGC,CAAC,EAAI,OAAO,OAAOJ,CAAG,EAAE,IAAKK,GAAM,CAC9C,IAAMC,EAAQD,EAAI,IAClB,OAAOC,GAAS,OACZA,EAAQ,MACR,KAAK,KAAKA,EAAQ,MAAS,MAAO,GAAG,CAC3C,CAAC,EAED,MAAO,OAASJ,EAAI,MAASC,EAAI,MAASC,CAC5C,EAYaG,EAAc,CACzBC,EACAC,EACAC,EAAY,IACT,CACH,GAAIF,IAAW,MAAQC,IAAW,KAChC,OAAO,KAET,IAAME,EAAab,EAAaU,CAAM,EAChCI,EAAad,EAAaW,CAAM,EACtC,GAAIE,IAAe,MAAQC,IAAe,KACxC,OAAO,KAET,GAAM,CAACC,EAAOC,CAAI,EAAI,CAACH,EAAYC,CAAU,EAAE,KAAK,CAACG,EAAGX,IAAMA,EAAIW,CAAC,EACnE,OACE,KAAK,OAAQF,EAAQ,MAASC,EAAO,KAAS,IAAMJ,CAAS,EAC7D,IAAMA,CAEV,EASaM,EAAgB,CAC3BR,EACAC,EACAQ,IACG,CACH,IAAMC,EAAWX,EAAYC,EAAQC,CAAM,EAC3C,OAAKS,EAGEA,GAAYD,EAFV,IAGX,EAUaE,EAAe,CAACX,EAAgBC,EAAgBW,EAAQ,KAC5DJ,EAAcR,EAAQC,EAAQW,EAAQ,EAAI,GAAG,EAWzCC,EAAgB,CAC3Bb,EACAC,EACAW,EAAQ,KAEDJ,EAAcR,EAAQC,EAAQW,EAAQ,IAAM,CAAC,EAOzCE,EAAc,IAElB,IADK,KAAK,MAAM,KAAK,OAAO,EAAI,QAAQ,EAAE,SAAS,EAAE,EAC7C,SAAS,EAAG,GAAG,CAAC,GAUpBC,EAAmB,CAC9BC,EACAJ,EAAQ,KACU,CAClB,IAAIrB,EAAQuB,EAAY,EACpBG,EAAW,EACf,KAAO,CAACN,EAAaK,EAAYzB,EAAOqB,CAAK,GAAG,CAC9C,GAAIK,IAAa,IACf,OAAO,KAET1B,EAAQuB,EAAY,CACtB,CACA,OAAOvB,CACT,EAUa2B,EAAoB,CAC/BF,EACAJ,EAAQ,KACU,CAClB,IAAIrB,EAAQuB,EAAY,EACpBG,EAAW,EACf,KAAO,CAACJ,EAAcG,EAAYzB,EAAOqB,CAAK,GAAG,CAC/C,GAAIK,IAAa,IACf,OAAO,KAET1B,EAAQuB,EAAY,CACtB,CAEA,OAAOvB,CACT,EASa4B,EAAwB,CACnCC,EACAC,EACAT,IAEOU,EAAoBF,EAAeC,EAAaV,EAAcC,CAAK,EAU/DW,EAAyB,CACpCH,EACAC,EACAT,IAEOU,EAAoBF,EAAeC,EAAaR,EAAeD,CAAK","names":["hslToHex","hsl","rgb","hslToRgb","rgbToHex","hexToHsl","str","hexToRgb","rgbToHsl","r","g","b","hex","bigint","h","s","l","hue2rgb","p","q","t","max","min","d","binarySearchContrast","change","fixed","direction","contrastFn","large","hs","minColor","maxColor","fixedHex","prevMin","prevMax","adjusted","stringified","suggestColorVariant","colorToChange","colorToKeep","compareFn","hslChange","hslKeep","darker","lighter","darkerDiff","lighterDiff","getLuminance","color","rgb","hexToRgb","r","g","b","v","value","getContrast","color1","color2","precision","luminance1","luminance2","light","dark","a","isContrasting","ratio","contrast","isAAContrast","large","isAAAContrast","randomColor","getRandomAAColor","background","attempts","getRandomAAAColor","suggestAAColorVariant","colorToChange","colorToKeep","suggestColorVariant","suggestAAAColorVariant"]}