UNPKG

@patreon/studio

Version:

Patreon Studio Design System

234 lines 7.92 kB
import { Hct, argbFromHex, yFromLstar } from '@material/material-color-utilities'; /** * Converts an ARGB color to a hex color. * * Note: We're defining our own function here because the * material-color-utilities library `hexFromArgb` function * reduces hexes to 3 and 4 character strings when possible, * and we need to ensure we always return a 6 or 8 character * hex string. * * @param argb - The ARGB color to convert. * @returns The hex color. */ function argb2hex(argb) { // eslint-disable-next-line no-bitwise return ('#' + ((argb & 0xffffff) | 0x1000000).toString(16).slice(1)); } /** * Converts a number to a hex string * * @param value - The number to convert. * @returns The hex string. */ function num2hexFragment(value) { return Math.round(value).toString(16).padStart(2, '0'); } /** * Converts a hex fragment to a number * * @param hexFragment - The hex fragment to convert (e.g. "00" or "FF"). * @returns The number (0 - 255). */ function hexFragment2Number(hexFragment) { return parseInt(hexFragment, 16); } /** * Checks if a hex color is a 3 character hex string * * @param colorString - The hex color to check including the # symbol. * @returns True if the hex color is a 3 character hex string, false otherwise. */ export function isValidHex3(colorString) { return colorString.length === 4 && /(^#[\da-f]{3}$)/i.test(colorString); } /** * Checks if a hex color is a 6 character hex string * * @param colorString - The hex color to check including the # symbol. * @returns True if the hex color is a 6 character hex string, false otherwise. */ export function isValidHex6(colorString) { return colorString.length === 7 && /(^#[\da-f]{6}$)/i.test(colorString); } /** * Checks if a hex color is a 8 character hex string * * @param colorString - The hex color to check including the # symbol. * @returns True if the hex color is a 8 character hex string, false otherwise. */ export function isValidHex8(colorString) { return colorString.length === 9 && /(^#[\da-f]{8}$)/i.test(colorString); } /** * Checks if a hex color is a valid hex color * * @param colorString - The hex color to check including the # symbol. * @returns True if the hex color is a valid hex color, false otherwise. */ export function isValidHex(colorString) { return isValidHex3(colorString) || isValidHex6(colorString) || isValidHex8(colorString); } /** * Converts a hex color to an HCT instance * * @internal we don't intend to export this function because * we don't want to fully expose the internals the HCT class. * * @param hex - The hex color to convert (e.g. "#000000") * @returns An HCT color instance. */ export function hex2hctInstance(hex) { if (!isValidHex(hex)) { throw new Error('Invalid hex color'); } return Hct.fromInt(argbFromHex(hex)); } /** * Converts an HCT instance to a hex color * * @internal we don't intend to export this function because * we don't want to fully expose the internals the HCT class. * * @param hct - The HCT color instance to convert. * @returns The hex color. **/ export function hctInstance2hex(hct) { return argb2hex(hct.toInt()); } /** * Converts an HCT instance to a HCTa object * * @param hct - The HCT color instance to convert. * @returns A HCTa object. */ export function hctInstance2hcta(hct) { return { hue: hct.hue, chroma: hct.chroma, tone: hct.tone, alpha: undefined, }; } /** * Converts a hex color to an HCTa color object * * @param hex - The hex color to convert (e.g. "#000000") * @returns An object with an HCT color instance and alpha value (0-1). **/ export function hex2hcta(hex) { const isSixDigit = isValidHex6(hex); const isEightDigit = isValidHex8(hex); if (!isSixDigit && !isEightDigit) { throw new Error('Invalid hex color, expected 6 or 8 character hex string'); } const instance = Hct.fromInt(argbFromHex(hex)); const hue = instance.hue; const chroma = instance.chroma; const tone = instance.tone; const alpha = isEightDigit ? hexFragment2Number(hex.slice(7, 9)) / 255 : undefined; return { hue, chroma, tone, alpha }; } export function hcta2hex({ hue, chroma, tone, alpha }) { const hctInstance = Hct.from(hue, chroma, tone); if (alpha === undefined) { return hctInstance2hex(hctInstance); } return `${hctInstance2hex(hctInstance)}${num2hexFragment(alpha * 255)}`; } /** * hello linear interpolation function, we call you lerp? * * @param x - The start value. * @param y - The end value. * @param a - The amount to interpolate between the start and end values. * @returns The interpolated value. */ export const lerp = (x, y, a) => x * (1 - a) + y * a; /** * Linearly interpolates between two colors in the HCT color space. * * @param startColor - The start color (6 or 8-digit hex string). * @param endColor - The end color (6 or 8-digit hex string). * @param a - Amount 0 - 1 between start and end. * @returns The interpolated color (6 or 8-digit hex string). */ export function lerpColor(startColor, endColor, a) { const startHcta = hex2hcta(startColor); const endHcta = hex2hcta(endColor); if (startHcta.alpha === undefined && endHcta.alpha === undefined) { return hcta2hex({ hue: lerp(startHcta.hue, endHcta.hue, a), chroma: lerp(startHcta.chroma, endHcta.chroma, a), tone: lerp(startHcta.tone, endHcta.tone, a), }); } return hcta2hex({ hue: lerp(startHcta.hue, endHcta.hue, a), chroma: lerp(startHcta.chroma, endHcta.chroma, a), tone: lerp(startHcta.tone, endHcta.tone, a), alpha: lerp(startHcta.alpha ?? 1, endHcta.alpha ?? 1, a), }); } /** * Calculates the distance between two hex colors using the * Hct color space. * * @param color1 - The first hex color. * @param color2 - The second hex color. * @returns The distance between the two colors. */ export function computeColorDistance(color1, color2) { const hct1 = hex2hcta(color1); const hct2 = hex2hcta(color2); // compute the distance between the two colors in the // HCT color space using the Euclidean distance formula return Math.sqrt( // compute the distance between hue Math.pow(hct1.hue - hct2.hue, 2) + // compute the distance between chroma Math.pow(hct1.chroma - hct2.chroma, 2) + // compute the distance between tone Math.pow(hct1.tone - hct2.tone, 2)); } /** * Computes the contrast ratio between two hex colors using the * HCT color space. * * Returns a value between 1 and 21, where: * - 1:1 contrast ratio, no contrast * - 3:1 contrast ratio, AA large text * - 4.5:1 contrast ratio, AA small text / AAA large text * - 7:1 contrast ratio, AAA small text * * @param color1 - The first hex color. * @param color2 - The second hex color. * @returns The contrast ratio between the two colors. */ export function computeContrastRatio(color1, color2) { const { tone: tone1 } = hex2hcta(color1); const { tone: tone2 } = hex2hcta(color2); return computeContrastRatioFromTone(tone1, tone2); } /** * Computes the contrast ratio between two tones using the * Y value from the L*a*b* color space. * * Returns a value between 1 and 21, where: * - 1:1 contrast ratio, no contrast * - 3:1 contrast ratio, AA large text * - 4.5:1 contrast ratio, AA small text / AAA large text * - 7:1 contrast ratio, AAA small text * * @param tone1 - The first tone. * @param tone2 - The second tone. * @returns The contrast ratio between the two tones. */ export function computeContrastRatioFromTone(tone1, tone2) { const y1 = yFromLstar(tone1); const y2 = yFromLstar(tone2); const lighter = y1 > y2 ? y1 : y2; const darker = lighter === y2 ? y1 : y2; return (lighter + 5.0) / (darker + 5.0); } //# sourceMappingURL=index.js.map