Skip to content

Commit 3f6ef56

Browse files
authored
chore: add hslToHex and hexToHsl color helpers (#6629)
* chore: add more color helpers * chore: added error handling
1 parent cba27c3 commit 3f6ef56

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

packages/utils/src/color.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
*/
88
export type RGB = { r: number; g: number; b: number };
99

10+
export type HSL = { h: number; s: number; l: number };
11+
1012
/**
1113
* @description Validates and clamps color values to RGB range (0-255)
1214
* @param {number} value - The color value to validate
@@ -62,3 +64,81 @@ export const hexToRgb = (hex: string): RGB => {
6264
* rgbToHex({ r: 0, g: 0, b: 255 }) // returns "#0000ff"
6365
*/
6466
export const rgbToHex = ({ r, g, b }: RGB): string => `#${toHex(r)}${toHex(g)}${toHex(b)}`;
67+
68+
/**
69+
* Converts Hex values to HSL values
70+
* @param {string} hex - The hexadecimal color code (e.g., "#ff0000" for red)
71+
* @returns {HSL} An object containing the HSL values
72+
* @example
73+
* hexToHsl("#ff0000") // returns { h: 0, s: 100, l: 50 }
74+
* hexToHsl("#00ff00") // returns { h: 120, s: 100, l: 50 }
75+
* hexToHsl("#0000ff") // returns { h: 240, s: 100, l: 50 }
76+
*/
77+
export const hexToHsl = (hex: string): HSL => {
78+
// return default value for invalid hex
79+
if (!/^#[0-9A-Fa-f]{6}$/.test(hex)) return { h: 0, s: 0, l: 0 };
80+
81+
const r = parseInt(hex.slice(1, 3), 16) / 255;
82+
const g = parseInt(hex.slice(3, 5), 16) / 255;
83+
const b = parseInt(hex.slice(5, 7), 16) / 255;
84+
85+
const max = Math.max(r, g, b);
86+
const min = Math.min(r, g, b);
87+
let h = 0;
88+
let s = 0;
89+
const l = (max + min) / 2;
90+
91+
if (max !== min) {
92+
const d = max - min;
93+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
94+
switch (max) {
95+
case r:
96+
h = (g - b) / d + (g < b ? 6 : 0);
97+
break;
98+
case g:
99+
h = (b - r) / d + 2;
100+
break;
101+
case b:
102+
h = (r - g) / d + 4;
103+
break;
104+
}
105+
h /= 6;
106+
}
107+
108+
return {
109+
h: h * 360,
110+
s: s * 100,
111+
l: l * 100,
112+
};
113+
};
114+
115+
/**
116+
* Converts HSL values to a hexadecimal color code
117+
* @param {HSL} hsl - An object containing HSL values
118+
* @param {number} hsl.h - Hue component (0-360)
119+
* @param {number} hsl.s - Saturation component (0-100)
120+
* @param {number} hsl.l - Lightness component (0-100)
121+
* @returns {string} The hexadecimal color code (e.g., "#ff0000" for red)
122+
* @example
123+
* hslToHex({ h: 0, s: 100, l: 50 }) // returns "#ff0000"
124+
* hslToHex({ h: 120, s: 100, l: 50 }) // returns "#00ff00"
125+
* hslToHex({ h: 240, s: 100, l: 50 }) // returns "#0000ff"
126+
*/
127+
export const hslToHex = ({ h, s, l }: HSL): string => {
128+
if (h < 0 || h > 360) return "#000000";
129+
if (s < 0 || s > 100) return "#000000";
130+
if (l < 0 || l > 100) return "#000000";
131+
132+
l /= 100;
133+
const a = (s * Math.min(l, 1 - l)) / 100;
134+
135+
const f = (n: number) => {
136+
const k = (n + h / 30) % 12;
137+
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
138+
return Math.round(255 * color)
139+
.toString(16)
140+
.padStart(2, "0");
141+
};
142+
143+
return `#${f(0)}${f(8)}${f(4)}`;
144+
};

0 commit comments

Comments
 (0)