type RGB = { r: number; g: number; b: number } type HSL = { h: number; s: number; l: number } function rgbToHsl({ r, g, b }: RGB): HSL { r /= 255 g /= 255 b /= 255 const max = Math.max(r, g, b), min = Math.min(r, g, b) let h: number, s: number const l: number = (max + min) / 2 if (max === min) { h = s = 0 // achromatic } else { const d = max - min s = l > 0.5 ? d / (2 - max - min) : d / (max + min) switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0) break case g: h = (b - r) / d + 2 break case b: h = (r - g) / d + 4 break } h /= 6 } return { h, s, l } } function hslToRgb({ h, s, l }: HSL): RGB { let r: number, g: number, b: number if (s === 0) { r = g = b = l // achromatic } else { const hue2rgb = (p: number, q: number, t: number) => { if (t < 0) t += 1 if (t > 1) t -= 1 if (t < 1 / 6) return p + (q - p) * 6 * t if (t < 1 / 2) return q if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6 return p } const q = l < 0.5 ? l * (1 + s) : l + s - l * s const p = 2 * l - q r = hue2rgb(p, q, h + 1 / 3) g = hue2rgb(p, q, h) b = hue2rgb(p, q, h - 1 / 3) } return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) } } function hexToRgb(hex: string): RGB { let r = 0, g = 0, b = 0 // 3 digits if (hex.length == 4) { r = parseInt(hex[1] + hex[1], 16) g = parseInt(hex[2] + hex[2], 16) b = parseInt(hex[3] + hex[3], 16) } // 6 digits else if (hex.length == 7) { r = parseInt(hex.slice(1, 3), 16) g = parseInt(hex.slice(3, 5), 16) b = parseInt(hex.slice(5, 7), 16) } return { r, g, b } } function rgbToHex({ r, g, b }: RGB): string { return ( '#' + [r, g, b] .map((x) => { const hex = x.toString(16) return hex.length === 1 ? '0' + hex : hex }) .join('') ) } export function lightenColor(hex: string, amount: number): string { let rgb = hexToRgb(hex) const hsl = rgbToHsl(rgb) hsl.l = Math.min(1, hsl.l + amount) rgb = hslToRgb(hsl) return rgbToHex(rgb) }