color-value-tools 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/index.d.ts +203 -0
- package/dist/index.js +590 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# color-value-tools
|
|
2
|
+
|
|
3
|
+
A tiny utility library for parsing, converting, and manipulating color values across common formats (hex, RGB, HSL, HSV, Lab, CMYK) and CSS variables.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install color-value-tools
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Example (ESM / TypeScript):
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { normalizeColor, mixColors } from 'color-value-tools';
|
|
17
|
+
|
|
18
|
+
console.log(normalizeColor('#3498db'));
|
|
19
|
+
// { type: 'hex', hex: '#3498db', r: 52, g: 152, b: 219, ... }
|
|
20
|
+
|
|
21
|
+
console.log(mixColors('#ff0000', '#0000ff', 0.5));
|
|
22
|
+
// '#800080'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Example (CommonJS / Node):
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const { normalizeColor, mixColors } = require('color-value-tools');
|
|
29
|
+
console.log(normalizeColor('rgba(255,0,0,0.5)'));
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## API & Options
|
|
33
|
+
|
|
34
|
+
- **`isCssVariable`**: Check if a string is a CSS variable (e.g. `var(--main)`).
|
|
35
|
+
- **`isHexColor`**: Detect hex color strings (3- or 6-digit, with or without `#`).
|
|
36
|
+
- **`isRgbColor`**: Detect `rgb()` / `rgba()` color strings.
|
|
37
|
+
- **`isHslColor`**: Detect `hsl()` / `hsla()` color strings.
|
|
38
|
+
- **`getColorType`**: Returns the color type: `hex`, `css-var`, `rgb`, `hsl`, `named`, or `unknown`.
|
|
39
|
+
- **`extractCssVariableName`**: Extracts the CSS variable name from `var(--name)`.
|
|
40
|
+
- **`normalizeHex`**: Normalizes and validates hex strings, returns a 6-digit lowercase hex (fallback `#f5e477`).
|
|
41
|
+
- **`hexToRgb`**: Convert a hex color to an `[r, g, b]` tuple.
|
|
42
|
+
- **`hexToRgba`**: Convert a hex color to an `rgba(...)` string with opacity.
|
|
43
|
+
- **`hexToHsl`**: Convert a hex color to an `[h, s, l]` tuple.
|
|
44
|
+
- **`hslToHex`**: Convert HSL values to a hex color string.
|
|
45
|
+
- **`adjustHexBrightness`**: Lighten or darken a hex color by a percentage offset.
|
|
46
|
+
- **`rotateHue`**: Rotate the hue of a hex color by degrees.
|
|
47
|
+
- **`rgbToHex` / `rgbaToHex`**: Convert RGB(A) channels to `#rrggbb` / `#rrggbbaa`.
|
|
48
|
+
- **`rgbToRgbaString` / `rgbaStringToRgba`**: Build and parse `rgba(...)` / `rgb(...)` strings.
|
|
49
|
+
- **`rgbToHsl` / `hslToRgb`**: RGB ↔ HSL conversions.
|
|
50
|
+
- **`rgbToHsv` / `hsvToRgb`**: RGB ↔ HSV conversions.
|
|
51
|
+
- **`hexToHsv` / `hsvToHex`**: Hex ↔ HSV helpers.
|
|
52
|
+
- **`hex8ToRgba` / `rgbaToHex8`**: Parse and build 8-digit hex with alpha.
|
|
53
|
+
- **`normalizeColor`**: Universal parser/normalizer returning `{ type, hex?, r?, g?, b?, a?, h?, s?, l?, v? }`.
|
|
54
|
+
- **`mixColors`**: Linear interpolation between two colors (supports `rgb` or `hsl` mixing, returns hex/rgb/rgba/hsl).
|
|
55
|
+
- **`relativeLuminance` / `contrastRatio`**: WCAG relative luminance and contrast ratio.
|
|
56
|
+
- **`isDark` / `isLight`**: Quick luminance-based checks.
|
|
57
|
+
- **`rgbToCmyk` / `cmykToRgb`**: CMYK conversions for print scenarios.
|
|
58
|
+
- **`rgbToLab` / `labToRgb` / `rgbToLch` / `lchToRgb`**: Perceptual color space conversions (Lab / LCH) for advanced operations.
|
|
59
|
+
|
|
60
|
+
## Author
|
|
61
|
+
|
|
62
|
+
Danil Lisin Vladimirovich aka Macrulez
|
|
63
|
+
|
|
64
|
+
GitHub: [macrulezru](https://github.com/macrulezru)
|
|
65
|
+
|
|
66
|
+
Website: [macrulez.ru](https://macrulez.ru/)
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
export type ColorType = 'hex' | 'css-var' | 'rgb' | 'hsl' | 'named' | 'unknown';
|
|
2
|
+
export declare function isCssVariable(value: string): boolean;
|
|
3
|
+
export declare function isHexColor(value: string): boolean;
|
|
4
|
+
export declare function isRgbColor(value: string): boolean;
|
|
5
|
+
export declare function isHslColor(value: string): boolean;
|
|
6
|
+
export declare function getColorType(value: string): ColorType;
|
|
7
|
+
export declare function extractCssVariableName(value: string): string;
|
|
8
|
+
export declare function normalizeHex(hex: string): string;
|
|
9
|
+
export declare function hexToRgb(hex: string): [number, number, number];
|
|
10
|
+
export declare function hexToRgba(hex: string, opacity?: number): string;
|
|
11
|
+
export declare function hexToHsl(hex: string): [number, number, number];
|
|
12
|
+
export declare function hslToHex(h: number, s: number, l: number): string;
|
|
13
|
+
export declare function adjustHexBrightness(hex: string, offsetPercent: number): string;
|
|
14
|
+
export declare function rotateHue(hex: string, degrees: number): string;
|
|
15
|
+
export declare function rgbToHex({ r, g, b }: {
|
|
16
|
+
r: number;
|
|
17
|
+
g: number;
|
|
18
|
+
b: number;
|
|
19
|
+
}): string;
|
|
20
|
+
export declare function rgbaToHex({ r, g, b, a }: {
|
|
21
|
+
r: number;
|
|
22
|
+
g: number;
|
|
23
|
+
b: number;
|
|
24
|
+
a: number;
|
|
25
|
+
}): string;
|
|
26
|
+
export declare function rgbToRgbaString({ r, g, b }: {
|
|
27
|
+
r: number;
|
|
28
|
+
g: number;
|
|
29
|
+
b: number;
|
|
30
|
+
}, a: number): string;
|
|
31
|
+
export declare function rgbaStringToRgba(str: string): {
|
|
32
|
+
r: number;
|
|
33
|
+
g: number;
|
|
34
|
+
b: number;
|
|
35
|
+
a: number;
|
|
36
|
+
} | null;
|
|
37
|
+
export declare function rgbToHsl({ r, g, b }: {
|
|
38
|
+
r: number;
|
|
39
|
+
g: number;
|
|
40
|
+
b: number;
|
|
41
|
+
}): [number, number, number];
|
|
42
|
+
export declare function hslToRgb(h: number, s: number, l: number): {
|
|
43
|
+
r: number;
|
|
44
|
+
g: number;
|
|
45
|
+
b: number;
|
|
46
|
+
};
|
|
47
|
+
export declare function rgbToHsv({ r, g, b }: {
|
|
48
|
+
r: number;
|
|
49
|
+
g: number;
|
|
50
|
+
b: number;
|
|
51
|
+
}): [number, number, number];
|
|
52
|
+
export declare function hsvToRgb(h: number, s: number, v: number): {
|
|
53
|
+
r: number;
|
|
54
|
+
g: number;
|
|
55
|
+
b: number;
|
|
56
|
+
};
|
|
57
|
+
export declare function hexToHsv(hex: string): [number, number, number];
|
|
58
|
+
export declare function hsvToHex(h: number, s: number, v: number): string;
|
|
59
|
+
export declare function hex8ToRgba(hex: string): {
|
|
60
|
+
r: number;
|
|
61
|
+
g: number;
|
|
62
|
+
b: number;
|
|
63
|
+
a: number;
|
|
64
|
+
} | null;
|
|
65
|
+
export declare function rgbaToHex8({ r, g, b, a }: {
|
|
66
|
+
r: number;
|
|
67
|
+
g: number;
|
|
68
|
+
b: number;
|
|
69
|
+
a: number;
|
|
70
|
+
}): string;
|
|
71
|
+
export declare function normalizeColor(input: string | {
|
|
72
|
+
r: number;
|
|
73
|
+
g: number;
|
|
74
|
+
b: number;
|
|
75
|
+
} | {
|
|
76
|
+
h: number;
|
|
77
|
+
s: number;
|
|
78
|
+
l: number;
|
|
79
|
+
}): {
|
|
80
|
+
type: string;
|
|
81
|
+
hex: string;
|
|
82
|
+
r: any;
|
|
83
|
+
g: any;
|
|
84
|
+
b: any;
|
|
85
|
+
a: number;
|
|
86
|
+
h: number;
|
|
87
|
+
s: number;
|
|
88
|
+
l: number;
|
|
89
|
+
v: number;
|
|
90
|
+
raw?: undefined;
|
|
91
|
+
} | {
|
|
92
|
+
type: string;
|
|
93
|
+
hex: string;
|
|
94
|
+
r: number;
|
|
95
|
+
g: number;
|
|
96
|
+
b: number;
|
|
97
|
+
a: number;
|
|
98
|
+
h: any;
|
|
99
|
+
s: any;
|
|
100
|
+
l: any;
|
|
101
|
+
v: number;
|
|
102
|
+
raw?: undefined;
|
|
103
|
+
} | {
|
|
104
|
+
type: string;
|
|
105
|
+
hex?: undefined;
|
|
106
|
+
r?: undefined;
|
|
107
|
+
g?: undefined;
|
|
108
|
+
b?: undefined;
|
|
109
|
+
a?: undefined;
|
|
110
|
+
h?: undefined;
|
|
111
|
+
s?: undefined;
|
|
112
|
+
l?: undefined;
|
|
113
|
+
v?: undefined;
|
|
114
|
+
raw?: undefined;
|
|
115
|
+
} | {
|
|
116
|
+
type: string;
|
|
117
|
+
raw: string;
|
|
118
|
+
hex?: undefined;
|
|
119
|
+
r?: undefined;
|
|
120
|
+
g?: undefined;
|
|
121
|
+
b?: undefined;
|
|
122
|
+
a?: undefined;
|
|
123
|
+
h?: undefined;
|
|
124
|
+
s?: undefined;
|
|
125
|
+
l?: undefined;
|
|
126
|
+
v?: undefined;
|
|
127
|
+
} | {
|
|
128
|
+
type: string;
|
|
129
|
+
hex: string;
|
|
130
|
+
r: number;
|
|
131
|
+
g: number;
|
|
132
|
+
b: number;
|
|
133
|
+
a: number;
|
|
134
|
+
h?: undefined;
|
|
135
|
+
s?: undefined;
|
|
136
|
+
l?: undefined;
|
|
137
|
+
v?: undefined;
|
|
138
|
+
raw?: undefined;
|
|
139
|
+
};
|
|
140
|
+
export declare function mixColors(c1: string, c2: string, t: number, opts?: {
|
|
141
|
+
mode?: 'rgb' | 'hsl';
|
|
142
|
+
format?: 'hex' | 'rgb' | 'rgba' | 'hsl';
|
|
143
|
+
}): string;
|
|
144
|
+
export declare function relativeLuminance(color: string): number;
|
|
145
|
+
export declare function contrastRatio(a: string, b: string): number;
|
|
146
|
+
export declare function isDark(color: string, threshold?: number): boolean;
|
|
147
|
+
export declare function isLight(color: string, threshold?: number): boolean;
|
|
148
|
+
export declare function rgbToCmyk({ r, g, b }: {
|
|
149
|
+
r: number;
|
|
150
|
+
g: number;
|
|
151
|
+
b: number;
|
|
152
|
+
}): {
|
|
153
|
+
c: number;
|
|
154
|
+
m: number;
|
|
155
|
+
y: number;
|
|
156
|
+
k: number;
|
|
157
|
+
};
|
|
158
|
+
export declare function cmykToRgb({ c, m, y, k }: {
|
|
159
|
+
c: number;
|
|
160
|
+
m: number;
|
|
161
|
+
y: number;
|
|
162
|
+
k: number;
|
|
163
|
+
}): {
|
|
164
|
+
r: number;
|
|
165
|
+
g: number;
|
|
166
|
+
b: number;
|
|
167
|
+
};
|
|
168
|
+
export declare function rgbToLab({ r, g, b }: {
|
|
169
|
+
r: number;
|
|
170
|
+
g: number;
|
|
171
|
+
b: number;
|
|
172
|
+
}): {
|
|
173
|
+
L: number;
|
|
174
|
+
a: number;
|
|
175
|
+
b: number;
|
|
176
|
+
};
|
|
177
|
+
export declare function labToRgb({ L, a, b }: {
|
|
178
|
+
L: number;
|
|
179
|
+
a: number;
|
|
180
|
+
b: number;
|
|
181
|
+
}): {
|
|
182
|
+
r: number;
|
|
183
|
+
g: number;
|
|
184
|
+
b: number;
|
|
185
|
+
};
|
|
186
|
+
export declare function rgbToLch({ r, g, b }: {
|
|
187
|
+
r: number;
|
|
188
|
+
g: number;
|
|
189
|
+
b: number;
|
|
190
|
+
}): {
|
|
191
|
+
L: number;
|
|
192
|
+
C: number;
|
|
193
|
+
H: number;
|
|
194
|
+
};
|
|
195
|
+
export declare function lchToRgb({ L, C, H }: {
|
|
196
|
+
L: number;
|
|
197
|
+
C: number;
|
|
198
|
+
H: number;
|
|
199
|
+
}): {
|
|
200
|
+
r: number;
|
|
201
|
+
g: number;
|
|
202
|
+
b: number;
|
|
203
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isCssVariable = isCssVariable;
|
|
4
|
+
exports.isHexColor = isHexColor;
|
|
5
|
+
exports.isRgbColor = isRgbColor;
|
|
6
|
+
exports.isHslColor = isHslColor;
|
|
7
|
+
exports.getColorType = getColorType;
|
|
8
|
+
exports.extractCssVariableName = extractCssVariableName;
|
|
9
|
+
exports.normalizeHex = normalizeHex;
|
|
10
|
+
exports.hexToRgb = hexToRgb;
|
|
11
|
+
exports.hexToRgba = hexToRgba;
|
|
12
|
+
exports.hexToHsl = hexToHsl;
|
|
13
|
+
exports.hslToHex = hslToHex;
|
|
14
|
+
exports.adjustHexBrightness = adjustHexBrightness;
|
|
15
|
+
exports.rotateHue = rotateHue;
|
|
16
|
+
exports.rgbToHex = rgbToHex;
|
|
17
|
+
exports.rgbaToHex = rgbaToHex;
|
|
18
|
+
exports.rgbToRgbaString = rgbToRgbaString;
|
|
19
|
+
exports.rgbaStringToRgba = rgbaStringToRgba;
|
|
20
|
+
exports.rgbToHsl = rgbToHsl;
|
|
21
|
+
exports.hslToRgb = hslToRgb;
|
|
22
|
+
exports.rgbToHsv = rgbToHsv;
|
|
23
|
+
exports.hsvToRgb = hsvToRgb;
|
|
24
|
+
exports.hexToHsv = hexToHsv;
|
|
25
|
+
exports.hsvToHex = hsvToHex;
|
|
26
|
+
exports.hex8ToRgba = hex8ToRgba;
|
|
27
|
+
exports.rgbaToHex8 = rgbaToHex8;
|
|
28
|
+
exports.normalizeColor = normalizeColor;
|
|
29
|
+
exports.mixColors = mixColors;
|
|
30
|
+
exports.relativeLuminance = relativeLuminance;
|
|
31
|
+
exports.contrastRatio = contrastRatio;
|
|
32
|
+
exports.isDark = isDark;
|
|
33
|
+
exports.isLight = isLight;
|
|
34
|
+
exports.rgbToCmyk = rgbToCmyk;
|
|
35
|
+
exports.cmykToRgb = cmykToRgb;
|
|
36
|
+
exports.rgbToLab = rgbToLab;
|
|
37
|
+
exports.labToRgb = labToRgb;
|
|
38
|
+
exports.rgbToLch = rgbToLch;
|
|
39
|
+
exports.lchToRgb = lchToRgb;
|
|
40
|
+
function isCssVariable(value) {
|
|
41
|
+
return value.trim().startsWith('var(--');
|
|
42
|
+
}
|
|
43
|
+
function isHexColor(value) {
|
|
44
|
+
const trimmed = value.trim();
|
|
45
|
+
return /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(trimmed);
|
|
46
|
+
}
|
|
47
|
+
function isRgbColor(value) {
|
|
48
|
+
const trimmed = value.trim().toLowerCase();
|
|
49
|
+
return trimmed.startsWith('rgb(') || trimmed.startsWith('rgba(');
|
|
50
|
+
}
|
|
51
|
+
function isHslColor(value) {
|
|
52
|
+
const trimmed = value.trim().toLowerCase();
|
|
53
|
+
return trimmed.startsWith('hsl(') || trimmed.startsWith('hsla(');
|
|
54
|
+
}
|
|
55
|
+
function getColorType(value) {
|
|
56
|
+
const trimmed = value.trim().toLowerCase();
|
|
57
|
+
if (isCssVariable(trimmed))
|
|
58
|
+
return 'css-var';
|
|
59
|
+
if (isHexColor(trimmed))
|
|
60
|
+
return 'hex';
|
|
61
|
+
if (isRgbColor(trimmed))
|
|
62
|
+
return 'rgb';
|
|
63
|
+
if (isHslColor(trimmed))
|
|
64
|
+
return 'hsl';
|
|
65
|
+
const namedColors = [
|
|
66
|
+
'transparent', 'currentcolor', 'inherit', 'initial', 'unset', 'black', 'white', 'red', 'green', 'blue', 'yellow', 'purple', 'orange', 'gray', 'grey', 'pink', 'brown', 'cyan', 'magenta', 'violet', 'aqua', 'beige', 'coral', 'gold', 'indigo', 'ivory', 'khaki', 'lavender', 'lime', 'maroon', 'navy', 'olive', 'orchid', 'plum', 'salmon', 'silver', 'tan', 'teal', 'tomato', 'wheat',
|
|
67
|
+
];
|
|
68
|
+
if (namedColors.includes(trimmed))
|
|
69
|
+
return 'named';
|
|
70
|
+
return 'unknown';
|
|
71
|
+
}
|
|
72
|
+
function extractCssVariableName(value) {
|
|
73
|
+
const match = value.match(/var\((--[^)]+)\)/);
|
|
74
|
+
return (match === null || match === void 0 ? void 0 : match[1]) || value;
|
|
75
|
+
}
|
|
76
|
+
function normalizeHex(hex) {
|
|
77
|
+
let cleanHex = hex.trim();
|
|
78
|
+
if (!cleanHex.startsWith('#'))
|
|
79
|
+
cleanHex = `#${cleanHex}`;
|
|
80
|
+
if (cleanHex.length === 4) {
|
|
81
|
+
cleanHex = `#${cleanHex.slice(1).split('').map(c => c + c).join('')}`;
|
|
82
|
+
}
|
|
83
|
+
if (!/^#[0-9A-Fa-f]{6}$/.test(cleanHex)) {
|
|
84
|
+
return '#f5e477';
|
|
85
|
+
}
|
|
86
|
+
return cleanHex.toLowerCase();
|
|
87
|
+
}
|
|
88
|
+
function hexToRgb(hex) {
|
|
89
|
+
const normalizedHex = normalizeHex(hex);
|
|
90
|
+
const r = parseInt(normalizedHex.slice(1, 3), 16);
|
|
91
|
+
const g = parseInt(normalizedHex.slice(3, 5), 16);
|
|
92
|
+
const b = parseInt(normalizedHex.slice(5, 7), 16);
|
|
93
|
+
return [r, g, b];
|
|
94
|
+
}
|
|
95
|
+
function hexToRgba(hex, opacity = 1) {
|
|
96
|
+
const [r, g, b] = hexToRgb(hex);
|
|
97
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
98
|
+
}
|
|
99
|
+
function hexToHsl(hex) {
|
|
100
|
+
const [r, g, b] = hexToRgb(hex);
|
|
101
|
+
const red = r / 255;
|
|
102
|
+
const green = g / 255;
|
|
103
|
+
const blue = b / 255;
|
|
104
|
+
const max = Math.max(red, green, blue);
|
|
105
|
+
const min = Math.min(red, green, blue);
|
|
106
|
+
let h = 0, s = 0;
|
|
107
|
+
const l = (max + min) / 2;
|
|
108
|
+
if (max !== min) {
|
|
109
|
+
const d = max - min;
|
|
110
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
111
|
+
switch (max) {
|
|
112
|
+
case red:
|
|
113
|
+
h = (green - blue) / d + (green < blue ? 6 : 0);
|
|
114
|
+
break;
|
|
115
|
+
case green:
|
|
116
|
+
h = (blue - red) / d + 2;
|
|
117
|
+
break;
|
|
118
|
+
case blue:
|
|
119
|
+
h = (red - green) / d + 4;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
h /= 6;
|
|
123
|
+
}
|
|
124
|
+
return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
|
|
125
|
+
}
|
|
126
|
+
function hslToHex(h, s, l) {
|
|
127
|
+
h = h % 360;
|
|
128
|
+
s = Math.max(0, Math.min(100, s)) / 100;
|
|
129
|
+
l = Math.max(0, Math.min(100, l)) / 100;
|
|
130
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
131
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
132
|
+
const m = l - c / 2;
|
|
133
|
+
let r = 0, g = 0, b = 0;
|
|
134
|
+
if (h >= 0 && h < 60) {
|
|
135
|
+
r = c;
|
|
136
|
+
g = x;
|
|
137
|
+
b = 0;
|
|
138
|
+
}
|
|
139
|
+
else if (h >= 60 && h < 120) {
|
|
140
|
+
r = x;
|
|
141
|
+
g = c;
|
|
142
|
+
b = 0;
|
|
143
|
+
}
|
|
144
|
+
else if (h >= 120 && h < 180) {
|
|
145
|
+
r = 0;
|
|
146
|
+
g = c;
|
|
147
|
+
b = x;
|
|
148
|
+
}
|
|
149
|
+
else if (h >= 180 && h < 240) {
|
|
150
|
+
r = 0;
|
|
151
|
+
g = x;
|
|
152
|
+
b = c;
|
|
153
|
+
}
|
|
154
|
+
else if (h >= 240 && h < 300) {
|
|
155
|
+
r = x;
|
|
156
|
+
g = 0;
|
|
157
|
+
b = c;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
r = c;
|
|
161
|
+
g = 0;
|
|
162
|
+
b = x;
|
|
163
|
+
}
|
|
164
|
+
r = Math.round((r + m) * 255);
|
|
165
|
+
g = Math.round((g + m) * 255);
|
|
166
|
+
b = Math.round((b + m) * 255);
|
|
167
|
+
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
|
|
168
|
+
}
|
|
169
|
+
function adjustHexBrightness(hex, offsetPercent) {
|
|
170
|
+
const normalizedHex = normalizeHex(hex);
|
|
171
|
+
const p = Math.max(-100, Math.min(100, offsetPercent)) / 100;
|
|
172
|
+
const r = parseInt(normalizedHex.slice(1, 3), 16);
|
|
173
|
+
const g = parseInt(normalizedHex.slice(3, 5), 16);
|
|
174
|
+
const b = parseInt(normalizedHex.slice(5, 7), 16);
|
|
175
|
+
const adjustChannel = (channel) => {
|
|
176
|
+
if (p > 0)
|
|
177
|
+
return Math.min(255, Math.floor(channel + (255 - channel) * p));
|
|
178
|
+
else if (p < 0)
|
|
179
|
+
return Math.max(0, Math.floor(channel * (1 + p)));
|
|
180
|
+
return channel;
|
|
181
|
+
};
|
|
182
|
+
const newR = adjustChannel(r);
|
|
183
|
+
const newG = adjustChannel(g);
|
|
184
|
+
const newB = adjustChannel(b);
|
|
185
|
+
return `#${newR.toString(16).padStart(2, '0')}${newG.toString(16).padStart(2, '0')}${newB.toString(16).padStart(2, '0')}`;
|
|
186
|
+
}
|
|
187
|
+
function rotateHue(hex, degrees) {
|
|
188
|
+
const [h, s, l] = hexToHsl(hex);
|
|
189
|
+
const newH = (h + degrees) % 360;
|
|
190
|
+
return hslToHex(newH, s, l);
|
|
191
|
+
}
|
|
192
|
+
function clamp01(v) { return Math.max(0, Math.min(1, v)); }
|
|
193
|
+
function toHex2(n) { return Math.round(n).toString(16).padStart(2, '0'); }
|
|
194
|
+
function rgbToHex({ r, g, b }) {
|
|
195
|
+
return `#${toHex2(r)}${toHex2(g)}${toHex2(b)}`.toLowerCase();
|
|
196
|
+
}
|
|
197
|
+
function rgbaToHex({ r, g, b, a }) {
|
|
198
|
+
const aa = Math.round(clamp01(a) * 255);
|
|
199
|
+
return `#${toHex2(r)}${toHex2(g)}${toHex2(b)}${toHex2(aa)}`.toLowerCase();
|
|
200
|
+
}
|
|
201
|
+
function rgbToRgbaString({ r, g, b }, a) {
|
|
202
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${+a.toFixed(3)})`;
|
|
203
|
+
}
|
|
204
|
+
function rgbaStringToRgba(str) {
|
|
205
|
+
const s = str.trim().toLowerCase();
|
|
206
|
+
const m = s.match(/rgba?\(([^)]+)\)/);
|
|
207
|
+
if (!m)
|
|
208
|
+
return null;
|
|
209
|
+
const parts = m[1].split(/,\s*/).map(p => p.trim());
|
|
210
|
+
if (parts.length < 3)
|
|
211
|
+
return null;
|
|
212
|
+
const parseChannel = (v) => v.endsWith('%') ? Math.round(parseFloat(v) * 2.55) : Math.round(parseFloat(v));
|
|
213
|
+
const r = parseChannel(parts[0]);
|
|
214
|
+
const g = parseChannel(parts[1]);
|
|
215
|
+
const b = parseChannel(parts[2]);
|
|
216
|
+
const a = parts[3] !== undefined ? parseFloat(parts[3]) : 1;
|
|
217
|
+
return { r, g, b, a };
|
|
218
|
+
}
|
|
219
|
+
function rgbToHsl({ r, g, b }) {
|
|
220
|
+
const rd = r / 255, gd = g / 255, bd = b / 255;
|
|
221
|
+
const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
|
|
222
|
+
let h = 0, s = 0;
|
|
223
|
+
const l = (max + min) / 2;
|
|
224
|
+
if (max !== min) {
|
|
225
|
+
const d = max - min;
|
|
226
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
227
|
+
switch (max) {
|
|
228
|
+
case rd:
|
|
229
|
+
h = (gd - bd) / d + (gd < bd ? 6 : 0);
|
|
230
|
+
break;
|
|
231
|
+
case gd:
|
|
232
|
+
h = (bd - rd) / d + 2;
|
|
233
|
+
break;
|
|
234
|
+
case bd:
|
|
235
|
+
h = (rd - gd) / d + 4;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
h /= 6;
|
|
239
|
+
}
|
|
240
|
+
return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
|
|
241
|
+
}
|
|
242
|
+
function hslToRgb(h, s, l) {
|
|
243
|
+
h = ((h % 360) + 360) % 360;
|
|
244
|
+
s = Math.max(0, Math.min(100, s)) / 100;
|
|
245
|
+
l = Math.max(0, Math.min(100, l)) / 100;
|
|
246
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
247
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
248
|
+
const m = l - c / 2;
|
|
249
|
+
let rd = 0, gd = 0, bd = 0;
|
|
250
|
+
if (h >= 0 && h < 60) {
|
|
251
|
+
rd = c;
|
|
252
|
+
gd = x;
|
|
253
|
+
bd = 0;
|
|
254
|
+
}
|
|
255
|
+
else if (h >= 60 && h < 120) {
|
|
256
|
+
rd = x;
|
|
257
|
+
gd = c;
|
|
258
|
+
bd = 0;
|
|
259
|
+
}
|
|
260
|
+
else if (h >= 120 && h < 180) {
|
|
261
|
+
rd = 0;
|
|
262
|
+
gd = c;
|
|
263
|
+
bd = x;
|
|
264
|
+
}
|
|
265
|
+
else if (h >= 180 && h < 240) {
|
|
266
|
+
rd = 0;
|
|
267
|
+
gd = x;
|
|
268
|
+
bd = c;
|
|
269
|
+
}
|
|
270
|
+
else if (h >= 240 && h < 300) {
|
|
271
|
+
rd = x;
|
|
272
|
+
gd = 0;
|
|
273
|
+
bd = c;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
rd = c;
|
|
277
|
+
gd = 0;
|
|
278
|
+
bd = x;
|
|
279
|
+
}
|
|
280
|
+
return { r: Math.round((rd + m) * 255), g: Math.round((gd + m) * 255), b: Math.round((bd + m) * 255) };
|
|
281
|
+
}
|
|
282
|
+
function rgbToHsv({ r, g, b }) {
|
|
283
|
+
const rd = r / 255, gd = g / 255, bd = b / 255;
|
|
284
|
+
const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
|
|
285
|
+
const v = max;
|
|
286
|
+
const d = max - min;
|
|
287
|
+
const s = max === 0 ? 0 : d / max;
|
|
288
|
+
let h = 0;
|
|
289
|
+
if (d !== 0) {
|
|
290
|
+
switch (max) {
|
|
291
|
+
case rd:
|
|
292
|
+
h = (gd - bd) / d + (gd < bd ? 6 : 0);
|
|
293
|
+
break;
|
|
294
|
+
case gd:
|
|
295
|
+
h = (bd - rd) / d + 2;
|
|
296
|
+
break;
|
|
297
|
+
case bd:
|
|
298
|
+
h = (rd - gd) / d + 4;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
h /= 6;
|
|
302
|
+
}
|
|
303
|
+
return [Math.round(h * 360), Math.round(s * 100), Math.round(v * 100)];
|
|
304
|
+
}
|
|
305
|
+
function hsvToRgb(h, s, v) {
|
|
306
|
+
h = ((h % 360) + 360) % 360;
|
|
307
|
+
s = Math.max(0, Math.min(100, s)) / 100;
|
|
308
|
+
v = Math.max(0, Math.min(100, v)) / 100;
|
|
309
|
+
const i = Math.floor(h / 60);
|
|
310
|
+
const f = h / 60 - i;
|
|
311
|
+
const p = v * (1 - s);
|
|
312
|
+
const q = v * (1 - f * s);
|
|
313
|
+
const t = v * (1 - (1 - f) * s);
|
|
314
|
+
let rd = 0, gd = 0, bd = 0;
|
|
315
|
+
switch (i) {
|
|
316
|
+
case 0:
|
|
317
|
+
rd = v;
|
|
318
|
+
gd = t;
|
|
319
|
+
bd = p;
|
|
320
|
+
break;
|
|
321
|
+
case 1:
|
|
322
|
+
rd = q;
|
|
323
|
+
gd = v;
|
|
324
|
+
bd = p;
|
|
325
|
+
break;
|
|
326
|
+
case 2:
|
|
327
|
+
rd = p;
|
|
328
|
+
gd = v;
|
|
329
|
+
bd = t;
|
|
330
|
+
break;
|
|
331
|
+
case 3:
|
|
332
|
+
rd = p;
|
|
333
|
+
gd = q;
|
|
334
|
+
bd = v;
|
|
335
|
+
break;
|
|
336
|
+
case 4:
|
|
337
|
+
rd = t;
|
|
338
|
+
gd = p;
|
|
339
|
+
bd = v;
|
|
340
|
+
break;
|
|
341
|
+
default:
|
|
342
|
+
rd = v;
|
|
343
|
+
gd = p;
|
|
344
|
+
bd = q;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
return { r: Math.round(rd * 255), g: Math.round(gd * 255), b: Math.round(bd * 255) };
|
|
348
|
+
}
|
|
349
|
+
function hexToHsv(hex) {
|
|
350
|
+
const [r, g, b] = hexToRgb(hex);
|
|
351
|
+
return rgbToHsv({ r, g, b });
|
|
352
|
+
}
|
|
353
|
+
function hsvToHex(h, s, v) {
|
|
354
|
+
const { r, g, b } = hsvToRgb(h, s, v);
|
|
355
|
+
return rgbToHex({ r, g, b });
|
|
356
|
+
}
|
|
357
|
+
function hex8ToRgba(hex) {
|
|
358
|
+
let h = hex.trim();
|
|
359
|
+
if (!h.startsWith('#'))
|
|
360
|
+
h = `#${h}`;
|
|
361
|
+
if (h.length === 5) { // #rgba shorthand
|
|
362
|
+
h = `#${h[1]}${h[1]}${h[2]}${h[2]}${h[3]}${h[3]}${h[4]}${h[4]}`;
|
|
363
|
+
}
|
|
364
|
+
if (h.length !== 9)
|
|
365
|
+
return null;
|
|
366
|
+
const r = parseInt(h.slice(1, 3), 16);
|
|
367
|
+
const g = parseInt(h.slice(3, 5), 16);
|
|
368
|
+
const b = parseInt(h.slice(5, 7), 16);
|
|
369
|
+
const a = parseInt(h.slice(7, 9), 16) / 255;
|
|
370
|
+
return { r, g, b, a };
|
|
371
|
+
}
|
|
372
|
+
function rgbaToHex8({ r, g, b, a }) {
|
|
373
|
+
return rgbaToHex({ r, g, b, a });
|
|
374
|
+
}
|
|
375
|
+
function normalizeColor(input) {
|
|
376
|
+
if (typeof input !== 'string') {
|
|
377
|
+
if ('r' in input && 'g' in input && 'b' in input) {
|
|
378
|
+
const { r, g, b } = input;
|
|
379
|
+
const hex = rgbToHex({ r, g, b });
|
|
380
|
+
const [h, s, l] = rgbToHsl({ r, g, b });
|
|
381
|
+
const [hh, ss, vv] = rgbToHsv({ r, g, b });
|
|
382
|
+
return { type: 'rgb', hex, r, g, b, a: 1, h, s, l, v: vv };
|
|
383
|
+
}
|
|
384
|
+
if ('h' in input && 's' in input && 'l' in input) {
|
|
385
|
+
const { h, s, l } = input;
|
|
386
|
+
const { r, g, b } = hslToRgb(h, s, l);
|
|
387
|
+
const hex = rgbToHex({ r, g, b });
|
|
388
|
+
const [hh, ss, vv] = rgbToHsv({ r, g, b });
|
|
389
|
+
return { type: 'hsl', hex, r, g, b, a: 1, h, s, l, v: vv };
|
|
390
|
+
}
|
|
391
|
+
return { type: 'unknown' };
|
|
392
|
+
}
|
|
393
|
+
const str = input.trim();
|
|
394
|
+
if (isCssVariable(str))
|
|
395
|
+
return { type: 'css-var', raw: str };
|
|
396
|
+
if (isHexColor(str)) {
|
|
397
|
+
const hex = normalizeHex(str);
|
|
398
|
+
const [r, g, b] = hexToRgb(hex);
|
|
399
|
+
const [h, s, l] = hexToHsl(hex);
|
|
400
|
+
const [hh, ss, vv] = rgbToHsv({ r, g, b });
|
|
401
|
+
return { type: 'hex', hex, r, g, b, a: 1, h, s, l, v: vv };
|
|
402
|
+
}
|
|
403
|
+
const hex8 = str.match(/^#([0-9a-f]{8}|[0-9a-f]{4})$/i);
|
|
404
|
+
if (hex8) {
|
|
405
|
+
const rgba = hex8ToRgba(str);
|
|
406
|
+
if (rgba) {
|
|
407
|
+
const { r, g, b, a } = rgba;
|
|
408
|
+
const hex = rgbToHex({ r, g, b });
|
|
409
|
+
const [h, s, l] = rgbToHsl({ r, g, b });
|
|
410
|
+
const [hh, ss, vv] = rgbToHsv({ r, g, b });
|
|
411
|
+
return { type: 'hex', hex, r, g, b, a, h, s, l, v: vv };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (isRgbColor(str)) {
|
|
415
|
+
const rgba = rgbaStringToRgba(str);
|
|
416
|
+
if (rgba) {
|
|
417
|
+
const { r, g, b, a } = rgba;
|
|
418
|
+
const hex = rgbToHex({ r, g, b });
|
|
419
|
+
const [h, s, l] = rgbToHsl({ r, g, b });
|
|
420
|
+
const [hh, ss, vv] = rgbToHsv({ r, g, b });
|
|
421
|
+
return { type: 'rgb', hex, r, g, b, a, h, s, l, v: vv };
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (isHslColor(str)) {
|
|
425
|
+
const m = str.match(/hsla?\(([^)]+)\)/i);
|
|
426
|
+
if (m) {
|
|
427
|
+
const parts = m[1].split(/,\s*/);
|
|
428
|
+
const h = parseFloat(parts[0]);
|
|
429
|
+
const s = parseFloat(parts[1]);
|
|
430
|
+
const l = parseFloat(parts[2]);
|
|
431
|
+
const a = parts[3] ? parseFloat(parts[3]) : 1;
|
|
432
|
+
const { r, g, b } = hslToRgb(h, s, l);
|
|
433
|
+
const hex = rgbToHex({ r, g, b });
|
|
434
|
+
const [hh, ss, vv] = rgbToHsv({ r, g, b });
|
|
435
|
+
return { type: 'hsl', hex, r, g, b, a, h, s, l, v: vv };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (str === 'transparent')
|
|
439
|
+
return { type: 'named', hex: '#000000', r: 0, g: 0, b: 0, a: 0 };
|
|
440
|
+
return { type: 'unknown' };
|
|
441
|
+
}
|
|
442
|
+
function mixColors(c1, c2, t, opts) {
|
|
443
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
444
|
+
const o1 = normalizeColor(c1);
|
|
445
|
+
const o2 = normalizeColor(c2);
|
|
446
|
+
t = Math.max(0, Math.min(1, t));
|
|
447
|
+
const mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'rgb';
|
|
448
|
+
const out = (opts === null || opts === void 0 ? void 0 : opts.format) || 'hex';
|
|
449
|
+
let r = 0, g = 0, b = 0, a = 1;
|
|
450
|
+
if (mode === 'hsl') {
|
|
451
|
+
const h1 = (_a = o1.h) !== null && _a !== void 0 ? _a : rgbToHsl({ r: (_b = o1.r) !== null && _b !== void 0 ? _b : 0, g: (_c = o1.g) !== null && _c !== void 0 ? _c : 0, b: (_d = o1.b) !== null && _d !== void 0 ? _d : 0 })[0];
|
|
452
|
+
const s1 = (_e = o1.s) !== null && _e !== void 0 ? _e : 0;
|
|
453
|
+
const l1 = (_f = o1.l) !== null && _f !== void 0 ? _f : 0;
|
|
454
|
+
const h2 = (_g = o2.h) !== null && _g !== void 0 ? _g : rgbToHsl({ r: (_h = o2.r) !== null && _h !== void 0 ? _h : 0, g: (_j = o2.g) !== null && _j !== void 0 ? _j : 0, b: (_k = o2.b) !== null && _k !== void 0 ? _k : 0 })[0];
|
|
455
|
+
const s2 = (_l = o2.s) !== null && _l !== void 0 ? _l : 0;
|
|
456
|
+
const l2 = (_m = o2.l) !== null && _m !== void 0 ? _m : 0;
|
|
457
|
+
const ih = h1 + (((h2 - h1 + 540) % 360) - 180) * t;
|
|
458
|
+
const is = s1 + (s2 - s1) * t;
|
|
459
|
+
const il = l1 + (l2 - l1) * t;
|
|
460
|
+
const rgb = hslToRgb(ih, is, il);
|
|
461
|
+
r = rgb.r;
|
|
462
|
+
g = rgb.g;
|
|
463
|
+
b = rgb.b;
|
|
464
|
+
a = ((_o = o1.a) !== null && _o !== void 0 ? _o : 1) + (((_p = o2.a) !== null && _p !== void 0 ? _p : 1) - ((_q = o1.a) !== null && _q !== void 0 ? _q : 1)) * t;
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
r = (((_r = o1.r) !== null && _r !== void 0 ? _r : 0) * (1 - t)) + (((_s = o2.r) !== null && _s !== void 0 ? _s : 0) * t);
|
|
468
|
+
g = (((_t = o1.g) !== null && _t !== void 0 ? _t : 0) * (1 - t)) + (((_u = o2.g) !== null && _u !== void 0 ? _u : 0) * t);
|
|
469
|
+
b = (((_v = o1.b) !== null && _v !== void 0 ? _v : 0) * (1 - t)) + (((_w = o2.b) !== null && _w !== void 0 ? _w : 0) * t);
|
|
470
|
+
a = ((_x = o1.a) !== null && _x !== void 0 ? _x : 1) * (1 - t) + ((_y = o2.a) !== null && _y !== void 0 ? _y : 1) * t;
|
|
471
|
+
}
|
|
472
|
+
if (out === 'hex')
|
|
473
|
+
return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
|
|
474
|
+
if (out === 'rgba')
|
|
475
|
+
return rgbToRgbaString({ r: Math.round(r), g: Math.round(g), b: Math.round(b) }, a);
|
|
476
|
+
if (out === 'rgb')
|
|
477
|
+
return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
|
|
478
|
+
if (out === 'hsl') {
|
|
479
|
+
const [hh, ss, ll] = rgbToHsl({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
|
|
480
|
+
return `hsl(${hh}, ${ss}%, ${ll}%)`;
|
|
481
|
+
}
|
|
482
|
+
return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
|
|
483
|
+
}
|
|
484
|
+
function relativeLuminance(color) {
|
|
485
|
+
var _a, _b, _c;
|
|
486
|
+
const n = normalizeColor(color);
|
|
487
|
+
const r = ((_a = n.r) !== null && _a !== void 0 ? _a : 0) / 255;
|
|
488
|
+
const g = ((_b = n.g) !== null && _b !== void 0 ? _b : 0) / 255;
|
|
489
|
+
const b = ((_c = n.b) !== null && _c !== void 0 ? _c : 0) / 255;
|
|
490
|
+
const srgbToLin = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
491
|
+
const R = srgbToLin(r), G = srgbToLin(g), B = srgbToLin(b);
|
|
492
|
+
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
|
493
|
+
}
|
|
494
|
+
function contrastRatio(a, b) {
|
|
495
|
+
const L1 = relativeLuminance(a);
|
|
496
|
+
const L2 = relativeLuminance(b);
|
|
497
|
+
const light = Math.max(L1, L2);
|
|
498
|
+
const dark = Math.min(L1, L2);
|
|
499
|
+
return +((light + 0.05) / (dark + 0.05)).toFixed(2);
|
|
500
|
+
}
|
|
501
|
+
function isDark(color, threshold = 0.5) {
|
|
502
|
+
return relativeLuminance(color) < threshold;
|
|
503
|
+
}
|
|
504
|
+
function isLight(color, threshold = 0.5) {
|
|
505
|
+
return !isDark(color, threshold);
|
|
506
|
+
}
|
|
507
|
+
function rgbToCmyk({ r, g, b }) {
|
|
508
|
+
const rd = r / 255, gd = g / 255, bd = b / 255;
|
|
509
|
+
const k = 1 - Math.max(rd, gd, bd);
|
|
510
|
+
if (k === 1)
|
|
511
|
+
return { c: 0, m: 0, y: 0, k: 1 };
|
|
512
|
+
const c = (1 - rd - k) / (1 - k);
|
|
513
|
+
const m = (1 - gd - k) / (1 - k);
|
|
514
|
+
const y = (1 - bd - k) / (1 - k);
|
|
515
|
+
return { c, m, y, k };
|
|
516
|
+
}
|
|
517
|
+
function cmykToRgb({ c, m, y, k }) {
|
|
518
|
+
const r = 255 * (1 - c) * (1 - k);
|
|
519
|
+
const g = 255 * (1 - m) * (1 - k);
|
|
520
|
+
const b = 255 * (1 - y) * (1 - k);
|
|
521
|
+
return { r: Math.round(r), g: Math.round(g), b: Math.round(b) };
|
|
522
|
+
}
|
|
523
|
+
function srgbToLinear(v) {
|
|
524
|
+
v = v / 255;
|
|
525
|
+
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
526
|
+
}
|
|
527
|
+
function linearToSrgb(v) {
|
|
528
|
+
const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
|
|
529
|
+
return Math.round(Math.max(0, Math.min(1, t)) * 255);
|
|
530
|
+
}
|
|
531
|
+
function rgbToXyz({ r, g, b }) {
|
|
532
|
+
const R = srgbToLinear(r);
|
|
533
|
+
const G = srgbToLinear(g);
|
|
534
|
+
const B = srgbToLinear(b);
|
|
535
|
+
const X = R * 0.4124564 + G * 0.3575761 + B * 0.1804375;
|
|
536
|
+
const Y = R * 0.2126729 + G * 0.7151522 + B * 0.0721750;
|
|
537
|
+
const Z = R * 0.0193339 + G * 0.1191920 + B * 0.9503041;
|
|
538
|
+
return { X: X * 100, Y: Y * 100, Z: Z * 100 };
|
|
539
|
+
}
|
|
540
|
+
function xyzToRgb({ X, Y, Z }) {
|
|
541
|
+
X = X / 100;
|
|
542
|
+
Y = Y / 100;
|
|
543
|
+
Z = Z / 100;
|
|
544
|
+
let R = X * 3.2404542 + Y * -1.5371385 + Z * -0.4985314;
|
|
545
|
+
let G = X * -0.9692660 + Y * 1.8760108 + Z * 0.0415560;
|
|
546
|
+
let B = X * 0.0556434 + Y * -0.2040259 + Z * 1.0572252;
|
|
547
|
+
R = linearToSrgb(R);
|
|
548
|
+
G = linearToSrgb(G);
|
|
549
|
+
B = linearToSrgb(B);
|
|
550
|
+
return { r: R, g: G, b: B };
|
|
551
|
+
}
|
|
552
|
+
function rgbToLab({ r, g, b }) {
|
|
553
|
+
const { X, Y, Z } = rgbToXyz({ r, g, b });
|
|
554
|
+
const refX = 95.047, refY = 100.0, refZ = 108.883;
|
|
555
|
+
const x = X / refX, y = Y / refY, z = Z / refZ;
|
|
556
|
+
const fx = x > 0.008856 ? Math.cbrt(x) : (7.787 * x) + 16 / 116;
|
|
557
|
+
const fy = y > 0.008856 ? Math.cbrt(y) : (7.787 * y) + 16 / 116;
|
|
558
|
+
const fz = z > 0.008856 ? Math.cbrt(z) : (7.787 * z) + 16 / 116;
|
|
559
|
+
const L = (116 * fy) - 16;
|
|
560
|
+
const a = 500 * (fx - fy);
|
|
561
|
+
const b2 = 200 * (fy - fz);
|
|
562
|
+
return { L, a, b: b2 };
|
|
563
|
+
}
|
|
564
|
+
function labToRgb({ L, a, b }) {
|
|
565
|
+
const refX = 95.047, refY = 100.0, refZ = 108.883;
|
|
566
|
+
let fy = (L + 16) / 116;
|
|
567
|
+
let fx = a / 500 + fy;
|
|
568
|
+
let fz = fy - b / 200;
|
|
569
|
+
const fx3 = Math.pow(fx, 3);
|
|
570
|
+
const fz3 = Math.pow(fz, 3);
|
|
571
|
+
const fy3 = Math.pow(fy, 3);
|
|
572
|
+
const xr = fx3 > 0.008856 ? fx3 : (fx - 16 / 116) / 7.787;
|
|
573
|
+
const yr = L > (903.3 * 0.008856) ? fy3 : L / 903.3;
|
|
574
|
+
const zr = fz3 > 0.008856 ? fz3 : (fz - 16 / 116) / 7.787;
|
|
575
|
+
const X = xr * refX, Y = yr * refY, Z = zr * refZ;
|
|
576
|
+
return xyzToRgb({ X, Y, Z });
|
|
577
|
+
}
|
|
578
|
+
function rgbToLch({ r, g, b }) {
|
|
579
|
+
const { L, a, b: bb } = rgbToLab({ r, g, b });
|
|
580
|
+
const C = Math.sqrt(a * a + bb * bb);
|
|
581
|
+
let H = Math.atan2(bb, a) * (180 / Math.PI);
|
|
582
|
+
if (H < 0)
|
|
583
|
+
H += 360;
|
|
584
|
+
return { L, C, H };
|
|
585
|
+
}
|
|
586
|
+
function lchToRgb({ L, C, H }) {
|
|
587
|
+
const a = Math.cos(H * Math.PI / 180) * C;
|
|
588
|
+
const b = Math.sin(H * Math.PI / 180) * C;
|
|
589
|
+
return labToRgb({ L, a, b });
|
|
590
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "color-value-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A tiny utility library for parsing, converting, and manipulating color values across common formats (hex, RGB, HSL, HSV, Lab, CMYK) and CSS variables.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/macrulezru/color-value-tools.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://macrulez.ru/#/en",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/macrulezru/color-value-tools/issues"
|
|
12
|
+
},
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
|
+
"module": "dist/index.js",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"require": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"css",
|
|
30
|
+
"gradient",
|
|
31
|
+
"linear-gradient",
|
|
32
|
+
"radial-gradient",
|
|
33
|
+
"conic-gradient",
|
|
34
|
+
"typescript"
|
|
35
|
+
],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/vue": "^1.0.31",
|
|
40
|
+
"@vue/reactivity": "^3.5.27",
|
|
41
|
+
"@vue/runtime-core": "^3.5.27",
|
|
42
|
+
"typescript": "^5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"vue": "^3.5.27"
|
|
46
|
+
}
|
|
47
|
+
}
|