@sardine/colour 2.3.0 → 3.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 +0 -3
- package/dist/assertions.d.ts +4 -1
- package/dist/convertCSSRGBtoHex.d.ts +3 -0
- package/dist/convertCSSRGBtoRGB.d.ts +12 -0
- package/dist/convertRGBtoHSV.d.ts +16 -0
- package/dist/getContrastRatio.d.ts +3 -2
- package/dist/index.cjs +359 -259
- package/dist/index.d.ts +5 -4
- package/dist/index.min.js +1 -7
- package/dist/index.mjs +360 -260
- package/dist/isDarkColour.d.ts +2 -2
- package/dist/sortHexColours.d.ts +26 -0
- package/dist/types.d.ts +12 -1
- package/dist/util/index.d.ts +2 -12
- package/dist/util/regexers.d.ts +6 -9
- package/package.json +73 -57
package/dist/index.mjs
CHANGED
|
@@ -1,177 +1,3 @@
|
|
|
1
|
-
function hue_d(x, y) {
|
|
2
|
-
if (x === 0 && y === 0) {
|
|
3
|
-
return 0;
|
|
4
|
-
}
|
|
5
|
-
const angle = Math.atan2(x, y) * (180 / Math.PI);
|
|
6
|
-
if (angle >= 0) {
|
|
7
|
-
return angle;
|
|
8
|
-
}
|
|
9
|
-
return angle + 360;
|
|
10
|
-
}
|
|
11
|
-
function deltaHue_d({ C1, C2, h1_d, h2_d }) {
|
|
12
|
-
if (C1 * C2 === 0) {
|
|
13
|
-
return 0;
|
|
14
|
-
}
|
|
15
|
-
const delta = h2_d - h1_d;
|
|
16
|
-
if (Math.abs(delta) <= 180) {
|
|
17
|
-
return delta;
|
|
18
|
-
}
|
|
19
|
-
if (delta > 180) {
|
|
20
|
-
return delta - 360;
|
|
21
|
-
}
|
|
22
|
-
return delta + 360;
|
|
23
|
-
}
|
|
24
|
-
function meanHue_d({ C1, C2, h1_d, h2_d }) {
|
|
25
|
-
if (C1 * C2 === 0) {
|
|
26
|
-
return h2_d + h1_d;
|
|
27
|
-
}
|
|
28
|
-
const delta = Math.abs(h1_d - h2_d);
|
|
29
|
-
if (delta <= 180) {
|
|
30
|
-
return (h2_d + h1_d) / 2;
|
|
31
|
-
}
|
|
32
|
-
if (delta > 180 && h1_d + h2_d < 360) {
|
|
33
|
-
return (h2_d + h1_d + 360) / 2;
|
|
34
|
-
}
|
|
35
|
-
return (h2_d + h1_d - 360) / 2;
|
|
36
|
-
}
|
|
37
|
-
const toRadians = (n) => n * (Math.PI / 180);
|
|
38
|
-
const bigSquare = (n) => Math.sqrt(n ** 7 / (n ** 7 + 25 ** 7));
|
|
39
|
-
function linearRGB(rgbValue, WCAG21) {
|
|
40
|
-
const rgbRatio = rgbValue / 255;
|
|
41
|
-
const threshold = WCAG21 ? 0.03928 : 0.04045;
|
|
42
|
-
let linearValue;
|
|
43
|
-
if (rgbRatio > threshold) {
|
|
44
|
-
linearValue = ((rgbRatio + 0.055) / 1.055) ** 2.4;
|
|
45
|
-
} else {
|
|
46
|
-
linearValue = rgbRatio / 12.92;
|
|
47
|
-
}
|
|
48
|
-
return linearValue;
|
|
49
|
-
}
|
|
50
|
-
function constrainLab(n) {
|
|
51
|
-
const delta = 6 / 29;
|
|
52
|
-
const deltaCube = delta ** 3;
|
|
53
|
-
let t;
|
|
54
|
-
if (n > deltaCube) {
|
|
55
|
-
t = Math.cbrt(n);
|
|
56
|
-
} else {
|
|
57
|
-
t = n / (3 * delta ** 2) + 4 / 29;
|
|
58
|
-
}
|
|
59
|
-
return t;
|
|
60
|
-
}
|
|
61
|
-
function clamp(value, min, max) {
|
|
62
|
-
return Math.min(Math.max(value, min), max);
|
|
63
|
-
}
|
|
64
|
-
function ciede2000(colour1, colour2) {
|
|
65
|
-
const lightness1 = colour1.L;
|
|
66
|
-
const greenRed1 = colour1.a;
|
|
67
|
-
const blueYellow1 = colour1.b;
|
|
68
|
-
const lightness2 = colour2.L;
|
|
69
|
-
const greenRed2 = colour2.a;
|
|
70
|
-
const blueYellow2 = colour2.b;
|
|
71
|
-
const luminanceWeight = 1;
|
|
72
|
-
const chromaWeight = 1;
|
|
73
|
-
const hueWeight = 1;
|
|
74
|
-
const chroma1 = Math.sqrt(greenRed1 ** 2 + blueYellow1 ** 2);
|
|
75
|
-
const chroma2 = Math.sqrt(greenRed2 ** 2 + blueYellow2 ** 2);
|
|
76
|
-
const deltaLightness = lightness2 - lightness1;
|
|
77
|
-
const meanChroma = (chroma1 + chroma2) / 2;
|
|
78
|
-
const G = 0.5 * (1 - bigSquare(meanChroma));
|
|
79
|
-
const greenRed1Prime = greenRed1 * (1 + G);
|
|
80
|
-
const greenRed2Prime = greenRed2 * (1 + G);
|
|
81
|
-
const chroma1Prime = Math.sqrt(greenRed1Prime ** 2 + blueYellow1 ** 2);
|
|
82
|
-
const chroma2Prime = Math.sqrt(greenRed2Prime ** 2 + blueYellow2 ** 2);
|
|
83
|
-
const meanChromaPrime = (chroma1Prime + chroma2Prime) / 2;
|
|
84
|
-
const deltaChromaPrime = chroma2Prime - chroma1Prime;
|
|
85
|
-
const hue1Prime = hue_d(blueYellow1, greenRed1Prime);
|
|
86
|
-
const hue2Prime = hue_d(blueYellow2, greenRed2Prime);
|
|
87
|
-
const deltaHuePrime = deltaHue_d({
|
|
88
|
-
C1: chroma1,
|
|
89
|
-
C2: chroma2,
|
|
90
|
-
h1_d: hue1Prime,
|
|
91
|
-
h2_d: hue2Prime
|
|
92
|
-
});
|
|
93
|
-
const deltaHue = 2 * Math.sqrt(chroma1Prime * chroma2Prime) * Math.sin(toRadians(deltaHuePrime) / 2);
|
|
94
|
-
const meanHuePrime = meanHue_d({
|
|
95
|
-
C1: chroma1,
|
|
96
|
-
C2: chroma2,
|
|
97
|
-
h1_d: hue1Prime,
|
|
98
|
-
h2_d: hue2Prime
|
|
99
|
-
});
|
|
100
|
-
const meanLightness = (lightness1 + lightness2) / 2;
|
|
101
|
-
const T = 1 - 0.17 * Math.cos(toRadians(meanHuePrime - 30)) + 0.24 * Math.cos(toRadians(2 * meanHuePrime)) + 0.32 * Math.cos(toRadians(3 * meanHuePrime + 6)) - 0.2 * Math.cos(toRadians(4 * meanHuePrime - 63));
|
|
102
|
-
const SL = 1 + 0.015 * (meanLightness - 50) ** 2 / Math.sqrt(20 + (meanLightness - 50) ** 2);
|
|
103
|
-
const SC = 0.045 * meanChromaPrime + 1;
|
|
104
|
-
const SH = 1 + 0.015 * meanChromaPrime * T;
|
|
105
|
-
const rotation = 30 * Math.exp(-(((meanHuePrime - 275) / 25) ** 2));
|
|
106
|
-
const RT = -2 * bigSquare(meanChromaPrime) * Math.sin(toRadians(rotation * 2));
|
|
107
|
-
const deltaE = Math.sqrt(
|
|
108
|
-
(deltaLightness / (luminanceWeight * SL)) ** 2 + (deltaChromaPrime / (chromaWeight * SC)) ** 2 + (deltaHue / (hueWeight * SH)) ** 2 + RT * (deltaChromaPrime / (chromaWeight * SC)) * (deltaHue / (hueWeight * SH))
|
|
109
|
-
);
|
|
110
|
-
return deltaE;
|
|
111
|
-
}
|
|
112
|
-
function convertRGBtoXYZ(colour) {
|
|
113
|
-
const { R, G, B } = colour;
|
|
114
|
-
const _R = linearRGB(R) * 100;
|
|
115
|
-
const _G = linearRGB(G) * 100;
|
|
116
|
-
const _B = linearRGB(B) * 100;
|
|
117
|
-
const X = _R * 0.4124 + _G * 0.3576 + _B * 0.1805;
|
|
118
|
-
const Y = _R * 0.2126 + _G * 0.7152 + _B * 0.0722;
|
|
119
|
-
const Z = _R * 0.0193 + _G * 0.1192 + _B * 0.9505;
|
|
120
|
-
return { X, Y, Z };
|
|
121
|
-
}
|
|
122
|
-
function convertXYZtoLab(colour) {
|
|
123
|
-
const { X, Y, Z } = colour;
|
|
124
|
-
const _X = X / 95.047;
|
|
125
|
-
const _Y = Y / 100;
|
|
126
|
-
const _Z = Z / 108.883;
|
|
127
|
-
const fX = constrainLab(_X);
|
|
128
|
-
const fY = constrainLab(_Y);
|
|
129
|
-
const fZ = constrainLab(_Z);
|
|
130
|
-
const L = 116 * fY - 16;
|
|
131
|
-
const a = 500 * (fX - fY);
|
|
132
|
-
const b = 200 * (fY - fZ);
|
|
133
|
-
return { L, a, b };
|
|
134
|
-
}
|
|
135
|
-
function convertRGBtoLab(colour) {
|
|
136
|
-
const XYZColour = convertRGBtoXYZ(colour);
|
|
137
|
-
return convertXYZtoLab(XYZColour);
|
|
138
|
-
}
|
|
139
|
-
const RGBdistance = (colour1, colour2) => {
|
|
140
|
-
const c1 = convertRGBtoLab(colour1);
|
|
141
|
-
const c2 = convertRGBtoLab(colour2);
|
|
142
|
-
return ciede2000(c1, c2);
|
|
143
|
-
};
|
|
144
|
-
const hexRegex = /^#[a-fA-F0-9]{6}$/;
|
|
145
|
-
const hexAlphaRegex = /^#[a-fA-F0-9]{8}$/;
|
|
146
|
-
const shortHexRegex = /^#[a-fA-F0-9]{3}$/;
|
|
147
|
-
const shortAlphaHexRegex = /^#[a-fA-F0-9]{4}$/;
|
|
148
|
-
const cssRGBARegex = /^rgba*\(\s*([-+]?\d+)\s*(?:,)?\s*([-+]?\d+)\s*(?:,)?\s*([-+]?\d+)\s*(?:,*|\/*)\s*([-+]?\d*[.]?\d+[%]?)*\)$/i;
|
|
149
|
-
function convertCSSRGBtoRGB(colour) {
|
|
150
|
-
const match = colour.match(cssRGBARegex);
|
|
151
|
-
if (!match) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`convertCSSRGBtoHex expects a valid CSS RGB string but got ${colour}`
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
const rgbNumber = (n) => n ? Number.parseFloat(n) : void 0;
|
|
157
|
-
return {
|
|
158
|
-
R: rgbNumber(match[1]),
|
|
159
|
-
G: rgbNumber(match[2]),
|
|
160
|
-
B: rgbNumber(match[3]),
|
|
161
|
-
A: rgbNumber(match[4])
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
function convertRGBtoHex({ R, G, B, A }) {
|
|
165
|
-
const hex = (n) => {
|
|
166
|
-
const value = clamp(n, 0, 255);
|
|
167
|
-
return value.toString(16).padStart(2, "0");
|
|
168
|
-
};
|
|
169
|
-
return `#${hex(R)}${hex(G)}${hex(B)}${A ? hex(Math.round(A * 255)) : ""}`;
|
|
170
|
-
}
|
|
171
|
-
function convertCSSRGBtoHex(colour) {
|
|
172
|
-
const rgb = convertCSSRGBtoRGB(colour);
|
|
173
|
-
return convertRGBtoHex(rgb);
|
|
174
|
-
}
|
|
175
1
|
const namedCSSColours = /* @__PURE__ */ new Map([
|
|
176
2
|
["aliceblue", "#f0f8ff"],
|
|
177
3
|
["antiquewhite", "#faebd7"],
|
|
@@ -278,6 +104,7 @@ const namedCSSColours = /* @__PURE__ */ new Map([
|
|
|
278
104
|
["oldlace", "#fdf5e6"],
|
|
279
105
|
["olive", "#808000"],
|
|
280
106
|
["olivedrab", "#6b8e23"],
|
|
107
|
+
["orange", "#ffa500"],
|
|
281
108
|
["orangered", "#ff4500"],
|
|
282
109
|
["orchid", "#da70d6"],
|
|
283
110
|
["palegoldenrod", "#eee8aa"],
|
|
@@ -291,6 +118,7 @@ const namedCSSColours = /* @__PURE__ */ new Map([
|
|
|
291
118
|
["plum", "#dda0dd"],
|
|
292
119
|
["powderblue", "#b0e0e6"],
|
|
293
120
|
["purple", "#800080"],
|
|
121
|
+
["rebeccapurple", "#663399"],
|
|
294
122
|
["red", "#ff0000"],
|
|
295
123
|
["rosybrown", "#bc8f8f"],
|
|
296
124
|
["royalblue", "#4169e1"],
|
|
@@ -321,51 +149,209 @@ const namedCSSColours = /* @__PURE__ */ new Map([
|
|
|
321
149
|
["yellow", "#ffff00"],
|
|
322
150
|
["yellowgreen", "#9acd32"]
|
|
323
151
|
]);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
152
|
+
const hexAnyRegex = /^#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})$/;
|
|
153
|
+
const cssRGBARegex = /^rgba?\(\s*([-+]?\d*\.?\d+%?)\s*[,\s]\s*([-+]?\d*\.?\d+%?)\s*[,\s]\s*([-+]?\d*\.?\d+%?)\s*(?:[,/]\s*([-+]?\d*\.?\d+%?))?\s*\)$/i;
|
|
154
|
+
function isCSSRGBColour(colour) {
|
|
155
|
+
return cssRGBARegex.test(colour);
|
|
156
|
+
}
|
|
157
|
+
function isHexColour(colour) {
|
|
158
|
+
return hexAnyRegex.test(colour);
|
|
159
|
+
}
|
|
160
|
+
function isNamedCSSColour(colour) {
|
|
161
|
+
return namedCSSColours.has(colour);
|
|
162
|
+
}
|
|
163
|
+
function hue_d(x, y) {
|
|
164
|
+
if (x === 0 && y === 0) {
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
const angle = Math.atan2(x, y) * (180 / Math.PI);
|
|
168
|
+
if (angle >= 0) {
|
|
169
|
+
return angle;
|
|
170
|
+
}
|
|
171
|
+
return angle + 360;
|
|
172
|
+
}
|
|
173
|
+
function deltaHue_d({ C1, C2, h1_d, h2_d }) {
|
|
174
|
+
if (C1 * C2 === 0) {
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
const delta = h2_d - h1_d;
|
|
178
|
+
if (Math.abs(delta) <= 180) {
|
|
179
|
+
return delta;
|
|
180
|
+
}
|
|
181
|
+
if (delta > 180) {
|
|
182
|
+
return delta - 360;
|
|
183
|
+
}
|
|
184
|
+
return delta + 360;
|
|
185
|
+
}
|
|
186
|
+
function meanHue_d({ C1, C2, h1_d, h2_d }) {
|
|
187
|
+
if (C1 * C2 === 0) {
|
|
188
|
+
return h2_d + h1_d;
|
|
189
|
+
}
|
|
190
|
+
const delta = Math.abs(h1_d - h2_d);
|
|
191
|
+
if (delta <= 180) {
|
|
192
|
+
return (h2_d + h1_d) / 2;
|
|
193
|
+
}
|
|
194
|
+
if (delta > 180 && h1_d + h2_d < 360) {
|
|
195
|
+
return (h2_d + h1_d + 360) / 2;
|
|
196
|
+
}
|
|
197
|
+
return (h2_d + h1_d - 360) / 2;
|
|
198
|
+
}
|
|
199
|
+
const DEG_TO_RAD = Math.PI / 180;
|
|
200
|
+
const toRadians = (n) => n * DEG_TO_RAD;
|
|
201
|
+
const POW_25_7 = 25 ** 7;
|
|
202
|
+
const bigSquare = (n) => Math.sqrt(n ** 7 / (n ** 7 + POW_25_7));
|
|
203
|
+
function linearRGB(rgbValue, WCAG21) {
|
|
204
|
+
const rgbRatio = rgbValue / 255;
|
|
205
|
+
const threshold = WCAG21 ? 0.03928 : 0.04045;
|
|
206
|
+
let linearValue;
|
|
207
|
+
if (rgbRatio > threshold) {
|
|
208
|
+
linearValue = ((rgbRatio + 0.055) / 1.055) ** 2.4;
|
|
209
|
+
} else {
|
|
210
|
+
linearValue = rgbRatio / 12.92;
|
|
211
|
+
}
|
|
212
|
+
return linearValue;
|
|
213
|
+
}
|
|
214
|
+
const LAB_DELTA = 6 / 29;
|
|
215
|
+
const LAB_DELTA_CUBE = LAB_DELTA ** 3;
|
|
216
|
+
const LAB_DELTA_SQ_3 = 3 * LAB_DELTA ** 2;
|
|
217
|
+
function constrainLab(n) {
|
|
218
|
+
if (n > LAB_DELTA_CUBE) {
|
|
219
|
+
return Math.cbrt(n);
|
|
220
|
+
}
|
|
221
|
+
return n / LAB_DELTA_SQ_3 + 4 / 29;
|
|
222
|
+
}
|
|
223
|
+
function clamp(value, min, max) {
|
|
224
|
+
return Math.min(Math.max(value, min), max);
|
|
225
|
+
}
|
|
226
|
+
const NAMED_CSS_COLOUR_URL = "https://developer.mozilla.org/en-US/docs/Web/CSS/named-color";
|
|
227
|
+
function ciede2000(colour1, colour2) {
|
|
228
|
+
const lightness1 = colour1.L;
|
|
229
|
+
const greenRed1 = colour1.a;
|
|
230
|
+
const blueYellow1 = colour1.b;
|
|
231
|
+
const lightness2 = colour2.L;
|
|
232
|
+
const greenRed2 = colour2.a;
|
|
233
|
+
const blueYellow2 = colour2.b;
|
|
234
|
+
const luminanceWeight = 1;
|
|
235
|
+
const chromaWeight = 1;
|
|
236
|
+
const hueWeight = 1;
|
|
237
|
+
const chroma1 = Math.sqrt(greenRed1 ** 2 + blueYellow1 ** 2);
|
|
238
|
+
const chroma2 = Math.sqrt(greenRed2 ** 2 + blueYellow2 ** 2);
|
|
239
|
+
const deltaLightness = lightness2 - lightness1;
|
|
240
|
+
const meanChroma = (chroma1 + chroma2) / 2;
|
|
241
|
+
const G = 0.5 * (1 - bigSquare(meanChroma));
|
|
242
|
+
const greenRed1Prime = greenRed1 * (1 + G);
|
|
243
|
+
const greenRed2Prime = greenRed2 * (1 + G);
|
|
244
|
+
const chroma1Prime = Math.sqrt(greenRed1Prime ** 2 + blueYellow1 ** 2);
|
|
245
|
+
const chroma2Prime = Math.sqrt(greenRed2Prime ** 2 + blueYellow2 ** 2);
|
|
246
|
+
const meanChromaPrime = (chroma1Prime + chroma2Prime) / 2;
|
|
247
|
+
const deltaChromaPrime = chroma2Prime - chroma1Prime;
|
|
248
|
+
const hue1Prime = hue_d(blueYellow1, greenRed1Prime);
|
|
249
|
+
const hue2Prime = hue_d(blueYellow2, greenRed2Prime);
|
|
250
|
+
const deltaHuePrime = deltaHue_d({
|
|
251
|
+
C1: chroma1,
|
|
252
|
+
C2: chroma2,
|
|
253
|
+
h1_d: hue1Prime,
|
|
254
|
+
h2_d: hue2Prime
|
|
255
|
+
});
|
|
256
|
+
const deltaHue = 2 * Math.sqrt(chroma1Prime * chroma2Prime) * Math.sin(toRadians(deltaHuePrime) / 2);
|
|
257
|
+
const meanHuePrime = meanHue_d({
|
|
258
|
+
C1: chroma1,
|
|
259
|
+
C2: chroma2,
|
|
260
|
+
h1_d: hue1Prime,
|
|
261
|
+
h2_d: hue2Prime
|
|
262
|
+
});
|
|
263
|
+
const meanLightness = (lightness1 + lightness2) / 2;
|
|
264
|
+
const T = 1 - 0.17 * Math.cos(toRadians(meanHuePrime - 30)) + 0.24 * Math.cos(toRadians(2 * meanHuePrime)) + 0.32 * Math.cos(toRadians(3 * meanHuePrime + 6)) - 0.2 * Math.cos(toRadians(4 * meanHuePrime - 63));
|
|
265
|
+
const SL = 1 + 0.015 * (meanLightness - 50) ** 2 / Math.sqrt(20 + (meanLightness - 50) ** 2);
|
|
266
|
+
const SC = 0.045 * meanChromaPrime + 1;
|
|
267
|
+
const SH = 1 + 0.015 * meanChromaPrime * T;
|
|
268
|
+
const rotation = 30 * Math.exp(-(((meanHuePrime - 275) / 25) ** 2));
|
|
269
|
+
const RT = -2 * bigSquare(meanChromaPrime) * Math.sin(toRadians(rotation * 2));
|
|
270
|
+
const deltaE = Math.sqrt(
|
|
271
|
+
(deltaLightness / (luminanceWeight * SL)) ** 2 + (deltaChromaPrime / (chromaWeight * SC)) ** 2 + (deltaHue / (hueWeight * SH)) ** 2 + RT * (deltaChromaPrime / (chromaWeight * SC)) * (deltaHue / (hueWeight * SH))
|
|
272
|
+
);
|
|
273
|
+
return deltaE;
|
|
274
|
+
}
|
|
275
|
+
function convertCssValueToNumber(value, isAlpha = false) {
|
|
276
|
+
if (value.endsWith("%")) {
|
|
277
|
+
const percentage = Number.parseFloat(value.slice(0, -1));
|
|
278
|
+
if (isAlpha) {
|
|
279
|
+
return percentage / 100;
|
|
328
280
|
}
|
|
281
|
+
return Math.round(percentage / 100 * 255);
|
|
329
282
|
}
|
|
330
|
-
return
|
|
283
|
+
return Number.parseFloat(value);
|
|
284
|
+
}
|
|
285
|
+
function convertCssRgbChannelValue(value) {
|
|
286
|
+
if (!value) return void 0;
|
|
287
|
+
return convertCssValueToNumber(value);
|
|
288
|
+
}
|
|
289
|
+
function convertCssAlphaChannelValue(value) {
|
|
290
|
+
if (!value) return void 0;
|
|
291
|
+
return convertCssValueToNumber(value, true);
|
|
292
|
+
}
|
|
293
|
+
function convertCSSRGBtoRGB(colour) {
|
|
294
|
+
const match = colour.match(cssRGBARegex);
|
|
295
|
+
if (!match) {
|
|
296
|
+
throw new Error(
|
|
297
|
+
`convertCSSRGBtoRGB expects a valid CSS RGB string but got ${colour}`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
R: convertCssRgbChannelValue(match[1]),
|
|
302
|
+
G: convertCssRgbChannelValue(match[2]),
|
|
303
|
+
B: convertCssRgbChannelValue(match[3]),
|
|
304
|
+
A: convertCssAlphaChannelValue(match[4])
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function convertRGBtoHex({ R, G, B, A }) {
|
|
308
|
+
const hex = (n) => {
|
|
309
|
+
const value = clamp(n, 0, 255);
|
|
310
|
+
return value.toString(16).padStart(2, "0");
|
|
311
|
+
};
|
|
312
|
+
return `#${hex(R)}${hex(G)}${hex(B)}${A ? hex(Math.round(A * 255)) : ""}`;
|
|
313
|
+
}
|
|
314
|
+
function convertCSSRGBtoHex(colour) {
|
|
315
|
+
const rgb = convertCSSRGBtoRGB(colour);
|
|
316
|
+
return convertRGBtoHex(rgb);
|
|
331
317
|
}
|
|
332
318
|
function convertHextoRGB(hex) {
|
|
333
319
|
if (typeof hex !== "string") {
|
|
334
320
|
throw new Error(`convertHextoRGB expects a string but got a ${typeof hex}`);
|
|
335
321
|
}
|
|
336
|
-
if (
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
322
|
+
if (!hexAnyRegex.test(hex)) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`convertHextoRGB expects a valid hexadecimal colour value but got ${hex}`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
switch (hex.length) {
|
|
328
|
+
case 7:
|
|
329
|
+
return {
|
|
330
|
+
R: parseInt(hex.slice(1, 3), 16),
|
|
331
|
+
G: parseInt(hex.slice(3, 5), 16),
|
|
332
|
+
B: parseInt(hex.slice(5, 7), 16)
|
|
333
|
+
};
|
|
334
|
+
case 4:
|
|
335
|
+
return {
|
|
336
|
+
R: parseInt(hex.slice(1, 2).repeat(2), 16),
|
|
337
|
+
G: parseInt(hex.slice(2, 3).repeat(2), 16),
|
|
338
|
+
B: parseInt(hex.slice(3, 4).repeat(2), 16)
|
|
339
|
+
};
|
|
340
|
+
case 9:
|
|
341
|
+
return {
|
|
342
|
+
R: parseInt(hex.slice(1, 3), 16),
|
|
343
|
+
G: parseInt(hex.slice(3, 5), 16),
|
|
344
|
+
B: parseInt(hex.slice(5, 7), 16),
|
|
345
|
+
A: Math.round(parseInt(hex.slice(7, 9), 16) / 255 * 100) / 100
|
|
346
|
+
};
|
|
347
|
+
default:
|
|
348
|
+
return {
|
|
349
|
+
R: parseInt(hex.slice(1, 2).repeat(2), 16),
|
|
350
|
+
G: parseInt(hex.slice(2, 3).repeat(2), 16),
|
|
351
|
+
B: parseInt(hex.slice(3, 4).repeat(2), 16),
|
|
352
|
+
A: Math.round(parseInt(hex.slice(4, 5).repeat(2), 16) / 255 * 100) / 100
|
|
353
|
+
};
|
|
365
354
|
}
|
|
366
|
-
throw new Error(
|
|
367
|
-
`convertHextoRGB expects an valid hexadecimal colour value but got ${hex}`
|
|
368
|
-
);
|
|
369
355
|
}
|
|
370
356
|
function convertHextoCSSRGB(hex) {
|
|
371
357
|
const rgb = convertHextoRGB(hex);
|
|
@@ -374,6 +360,14 @@ function convertHextoCSSRGB(hex) {
|
|
|
374
360
|
}
|
|
375
361
|
return `rgb(${rgb.R},${rgb.G},${rgb.B})`;
|
|
376
362
|
}
|
|
363
|
+
function convertHextoNamedCSSColour(colour) {
|
|
364
|
+
for (const [name, hex] of namedCSSColours.entries()) {
|
|
365
|
+
if (hex === colour) {
|
|
366
|
+
return name;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return void 0;
|
|
370
|
+
}
|
|
377
371
|
function convertNamedCSSColourtoHex(name) {
|
|
378
372
|
return namedCSSColours.get(name);
|
|
379
373
|
}
|
|
@@ -387,44 +381,56 @@ function convertNamedCSSColourtoRGB(colour) {
|
|
|
387
381
|
function convertRGBtoCSSRGB({ R, G, B, A }) {
|
|
388
382
|
return `rgb(${R} ${G} ${B}${A ? ` / ${A}` : ""})`;
|
|
389
383
|
}
|
|
384
|
+
function convertRGBtoXYZ(colour) {
|
|
385
|
+
const { R, G, B } = colour;
|
|
386
|
+
const _R = linearRGB(R) * 100;
|
|
387
|
+
const _G = linearRGB(G) * 100;
|
|
388
|
+
const _B = linearRGB(B) * 100;
|
|
389
|
+
const X = _R * 0.4124 + _G * 0.3576 + _B * 0.1805;
|
|
390
|
+
const Y = _R * 0.2126 + _G * 0.7152 + _B * 0.0722;
|
|
391
|
+
const Z = _R * 0.0193 + _G * 0.1192 + _B * 0.9505;
|
|
392
|
+
return { X, Y, Z };
|
|
393
|
+
}
|
|
394
|
+
function convertXYZtoLab(colour) {
|
|
395
|
+
const { X, Y, Z } = colour;
|
|
396
|
+
const _X = X / 95.047;
|
|
397
|
+
const _Y = Y / 100;
|
|
398
|
+
const _Z = Z / 108.883;
|
|
399
|
+
const fX = constrainLab(_X);
|
|
400
|
+
const fY = constrainLab(_Y);
|
|
401
|
+
const fZ = constrainLab(_Z);
|
|
402
|
+
const L = 116 * fY - 16;
|
|
403
|
+
const a = 500 * (fX - fY);
|
|
404
|
+
const b = 200 * (fY - fZ);
|
|
405
|
+
return { L, a, b };
|
|
406
|
+
}
|
|
407
|
+
function convertRGBtoLab(colour) {
|
|
408
|
+
const XYZColour = convertRGBtoXYZ(colour);
|
|
409
|
+
return convertXYZtoLab(XYZColour);
|
|
410
|
+
}
|
|
390
411
|
function convertRGBtoNamedCSSColour(colour) {
|
|
391
412
|
const hex = convertRGBtoHex(colour);
|
|
392
413
|
return convertHextoNamedCSSColour(hex);
|
|
393
414
|
}
|
|
415
|
+
const RGBdistance = (colour1, colour2) => {
|
|
416
|
+
const c1 = convertRGBtoLab(colour1);
|
|
417
|
+
const c2 = convertRGBtoLab(colour2);
|
|
418
|
+
return ciede2000(c1, c2);
|
|
419
|
+
};
|
|
394
420
|
function findNearestRGBColour(colour, palette) {
|
|
395
421
|
if (!palette || palette.length < 2) {
|
|
396
422
|
return colour;
|
|
397
423
|
}
|
|
398
|
-
|
|
424
|
+
let nearest = colour;
|
|
425
|
+
let minDistance = Number.POSITIVE_INFINITY;
|
|
399
426
|
for (const paletteColour of palette) {
|
|
400
427
|
const distance = RGBdistance(colour, paletteColour);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
function findNearestCSSRGBColour(colour, palette) {
|
|
407
|
-
if (!palette || palette.length < 2) {
|
|
408
|
-
return colour;
|
|
428
|
+
if (distance < minDistance) {
|
|
429
|
+
minDistance = distance;
|
|
430
|
+
nearest = paletteColour;
|
|
431
|
+
}
|
|
409
432
|
}
|
|
410
|
-
|
|
411
|
-
const paletteRGBColours = palette.map(
|
|
412
|
-
(hexColour) => convertCSSRGBtoRGB(hexColour)
|
|
413
|
-
);
|
|
414
|
-
const nearestRGBColour = findNearestRGBColour(
|
|
415
|
-
baseRGBColour,
|
|
416
|
-
paletteRGBColours
|
|
417
|
-
);
|
|
418
|
-
return convertRGBtoCSSRGB(nearestRGBColour);
|
|
419
|
-
}
|
|
420
|
-
function isCSSRGBColour(colour) {
|
|
421
|
-
return !!colour.match(cssRGBARegex);
|
|
422
|
-
}
|
|
423
|
-
function isHexColour(colour) {
|
|
424
|
-
return !!colour.match(hexRegex) || !!colour.match(hexAlphaRegex) || !!colour.match(shortAlphaHexRegex) || !!colour.match(shortHexRegex);
|
|
425
|
-
}
|
|
426
|
-
function isNamedCSSColour(colour) {
|
|
427
|
-
return namedCSSColours.has(colour);
|
|
433
|
+
return nearest;
|
|
428
434
|
}
|
|
429
435
|
function findNearestColour(colour, palette) {
|
|
430
436
|
if (!palette || palette.length < 2) {
|
|
@@ -436,12 +442,10 @@ function findNearestColour(colour, palette) {
|
|
|
436
442
|
if (isHexColour(colour)) {
|
|
437
443
|
baseColour = convertHextoRGB(colour);
|
|
438
444
|
colourType = "hex";
|
|
439
|
-
}
|
|
440
|
-
if (isCSSRGBColour(colour)) {
|
|
445
|
+
} else if (isCSSRGBColour(colour)) {
|
|
441
446
|
baseColour = convertCSSRGBtoRGB(colour);
|
|
442
447
|
colourType = "cssRGB";
|
|
443
|
-
}
|
|
444
|
-
if (isNamedCSSColour(colour)) {
|
|
448
|
+
} else if (isNamedCSSColour(colour)) {
|
|
445
449
|
baseColour = convertNamedCSSColourtoRGB(colour);
|
|
446
450
|
colourType = "namedCSS";
|
|
447
451
|
}
|
|
@@ -451,12 +455,11 @@ function findNearestColour(colour, palette) {
|
|
|
451
455
|
for (const paletteColour of palette) {
|
|
452
456
|
if (isHexColour(paletteColour)) {
|
|
453
457
|
paletteRGB.push(convertHextoRGB(paletteColour));
|
|
454
|
-
}
|
|
455
|
-
if (isCSSRGBColour(paletteColour)) {
|
|
458
|
+
} else if (isCSSRGBColour(paletteColour)) {
|
|
456
459
|
paletteRGB.push(convertCSSRGBtoRGB(paletteColour));
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
paletteRGB.push(
|
|
460
|
+
} else if (isNamedCSSColour(paletteColour)) {
|
|
461
|
+
const rgb = convertNamedCSSColourtoRGB(paletteColour);
|
|
462
|
+
if (rgb) paletteRGB.push(rgb);
|
|
460
463
|
}
|
|
461
464
|
}
|
|
462
465
|
if (paletteRGB.length < 2) {
|
|
@@ -471,6 +474,20 @@ function findNearestColour(colour, palette) {
|
|
|
471
474
|
}
|
|
472
475
|
return convertRGBtoNamedCSSColour(nearest);
|
|
473
476
|
}
|
|
477
|
+
function findNearestCSSRGBColour(colour, palette) {
|
|
478
|
+
if (!palette || palette.length < 2) {
|
|
479
|
+
return colour;
|
|
480
|
+
}
|
|
481
|
+
const baseRGBColour = convertCSSRGBtoRGB(colour);
|
|
482
|
+
const paletteRGBColours = palette.map(
|
|
483
|
+
(hexColour) => convertCSSRGBtoRGB(hexColour)
|
|
484
|
+
);
|
|
485
|
+
const nearestRGBColour = findNearestRGBColour(
|
|
486
|
+
baseRGBColour,
|
|
487
|
+
paletteRGBColours
|
|
488
|
+
);
|
|
489
|
+
return convertRGBtoCSSRGB(nearestRGBColour);
|
|
490
|
+
}
|
|
474
491
|
function findNearestHexColour(colour, palette) {
|
|
475
492
|
if (!palette || palette.length < 2) {
|
|
476
493
|
return colour;
|
|
@@ -538,9 +555,7 @@ function getContrastRatio(colour1, colour2, standard) {
|
|
|
538
555
|
const _hexColour1 = convertNamedCSSColourtoHex(colour1);
|
|
539
556
|
if (_hexColour1 === void 0) {
|
|
540
557
|
throw new Error(
|
|
541
|
-
`getContrastRatio expects valid CSS named colours.
|
|
542
|
-
${colour1} is not a valid CSS named colour.
|
|
543
|
-
See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`
|
|
558
|
+
`getContrastRatio expects valid CSS named colours. ${colour1} is not a valid CSS named colour. See ${NAMED_CSS_COLOUR_URL}`
|
|
544
559
|
);
|
|
545
560
|
}
|
|
546
561
|
hexColour1 = _hexColour1;
|
|
@@ -552,9 +567,9 @@ function getContrastRatio(colour1, colour2, standard) {
|
|
|
552
567
|
} else {
|
|
553
568
|
const _hexColour2 = convertNamedCSSColourtoHex(colour2);
|
|
554
569
|
if (_hexColour2 === void 0) {
|
|
555
|
-
throw new Error(
|
|
556
|
-
|
|
557
|
-
|
|
570
|
+
throw new Error(
|
|
571
|
+
`getContrastRatio expects valid CSS named colours. ${colour2} is not a valid CSS named colour. See ${NAMED_CSS_COLOUR_URL}`
|
|
572
|
+
);
|
|
558
573
|
}
|
|
559
574
|
hexColour2 = _hexColour2;
|
|
560
575
|
}
|
|
@@ -572,9 +587,7 @@ function getContrastRatioFromNamedCSSColour(colour1, colour2, standard) {
|
|
|
572
587
|
const hexColour2 = convertNamedCSSColourtoHex(colour2);
|
|
573
588
|
if (hexColour1 === void 0 || hexColour2 === void 0) {
|
|
574
589
|
throw new Error(
|
|
575
|
-
`getContrastRatioFromNamedCSSColour expects valid CSS named colours.
|
|
576
|
-
${colour1} or ${colour2} are not valid CSS named colours.
|
|
577
|
-
See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`
|
|
590
|
+
`getContrastRatioFromNamedCSSColour expects valid CSS named colours. ${colour1} or ${colour2} are not valid CSS named colours. See ${NAMED_CSS_COLOUR_URL}`
|
|
578
591
|
);
|
|
579
592
|
}
|
|
580
593
|
return getContrastRatioFromHex(hexColour1, hexColour2, standard);
|
|
@@ -591,7 +604,7 @@ function isCSSNamedDarkColour(name, standard) {
|
|
|
591
604
|
return isHexDarkColour(hex, standard);
|
|
592
605
|
}
|
|
593
606
|
throw new Error(
|
|
594
|
-
`${name} is not a valid colour format. isCSSNamedDarkColour only accepts CSS named colours.
|
|
607
|
+
`${name} is not a valid colour format. isCSSNamedDarkColour only accepts CSS named colours. See ${NAMED_CSS_COLOUR_URL}`
|
|
595
608
|
);
|
|
596
609
|
}
|
|
597
610
|
function isCSSRGBDarkColour(colour, standard) {
|
|
@@ -610,9 +623,9 @@ function isDarkColour(colour, standard) {
|
|
|
610
623
|
return isCSSRGBDarkColour(colour, standard);
|
|
611
624
|
}
|
|
612
625
|
return isCSSNamedDarkColour(colour, standard);
|
|
613
|
-
} catch (
|
|
626
|
+
} catch (_error) {
|
|
614
627
|
throw new Error(
|
|
615
|
-
`${colour} is not a valid colour format. isDarkColour accepts CSS RGB formats, ie rgb(0,0,0)
|
|
628
|
+
`${colour} is not a valid colour format. isDarkColour accepts CSS RGB formats, ie rgb(0,0,0), rgba(255, 255, 255, 0.4), rgb(50%, 25%, 100%), and rgba(50%, 25%, 100%, 80%), hexadecimal and CSS named colours.`
|
|
616
629
|
);
|
|
617
630
|
}
|
|
618
631
|
}
|
|
@@ -639,6 +652,90 @@ const pickHexColourContrast = ({ backgroundColour, optionOneColour, optionTwoCol
|
|
|
639
652
|
}
|
|
640
653
|
return optionTwoColour;
|
|
641
654
|
};
|
|
655
|
+
function convertRGBtoHSV(r, g, b) {
|
|
656
|
+
const red = r / 255;
|
|
657
|
+
const green = g / 255;
|
|
658
|
+
const blue = b / 255;
|
|
659
|
+
const max = Math.max(red, green, blue);
|
|
660
|
+
const min = Math.min(red, green, blue);
|
|
661
|
+
let hue = 0;
|
|
662
|
+
let saturation = 0;
|
|
663
|
+
const value = max;
|
|
664
|
+
const delta = max - min;
|
|
665
|
+
if (max === 0) {
|
|
666
|
+
saturation = 0;
|
|
667
|
+
} else {
|
|
668
|
+
saturation = delta / max;
|
|
669
|
+
}
|
|
670
|
+
if (delta !== 0) {
|
|
671
|
+
if (max === red) {
|
|
672
|
+
hue = (green - blue) / delta % 6;
|
|
673
|
+
} else if (max === green) {
|
|
674
|
+
hue = (blue - red) / delta + 2;
|
|
675
|
+
} else if (max === blue) {
|
|
676
|
+
hue = (red - green) / delta + 4;
|
|
677
|
+
}
|
|
678
|
+
hue = hue * 60;
|
|
679
|
+
if (hue < 0) {
|
|
680
|
+
hue = hue + 360;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return { h: hue, s: saturation, v: value };
|
|
684
|
+
}
|
|
685
|
+
function getColorInfo(hex) {
|
|
686
|
+
const { R, G, B, A } = convertHextoRGB(hex);
|
|
687
|
+
const a = typeof A === "number" ? A : 1;
|
|
688
|
+
const { h, s, v } = convertRGBtoHSV(R, G, B);
|
|
689
|
+
return { h, s, v, a };
|
|
690
|
+
}
|
|
691
|
+
function getGreyscaleOrder(v) {
|
|
692
|
+
if (v === 0) return 1;
|
|
693
|
+
if (v === 1) return 2;
|
|
694
|
+
return 0;
|
|
695
|
+
}
|
|
696
|
+
function sortHexColours(hexColours) {
|
|
697
|
+
const cache = /* @__PURE__ */ new Map();
|
|
698
|
+
for (const hex of hexColours) {
|
|
699
|
+
if (!cache.has(hex)) {
|
|
700
|
+
cache.set(hex, getColorInfo(hex));
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const normal = [];
|
|
704
|
+
const greyscale = [];
|
|
705
|
+
const transparent = [];
|
|
706
|
+
for (const hex of hexColours) {
|
|
707
|
+
const { s, a } = cache.get(hex);
|
|
708
|
+
if (a === 0) {
|
|
709
|
+
transparent.push(hex);
|
|
710
|
+
} else if (s === 0) {
|
|
711
|
+
greyscale.push(hex);
|
|
712
|
+
} else {
|
|
713
|
+
normal.push(hex);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
normal.sort((a, b) => {
|
|
717
|
+
const ca = cache.get(a);
|
|
718
|
+
const cb = cache.get(b);
|
|
719
|
+
if (ca.h !== cb.h) return ca.h - cb.h;
|
|
720
|
+
return cb.s - ca.s;
|
|
721
|
+
});
|
|
722
|
+
greyscale.sort((a, b) => {
|
|
723
|
+
const ca = cache.get(a);
|
|
724
|
+
const cb = cache.get(b);
|
|
725
|
+
const orderA = getGreyscaleOrder(ca.v);
|
|
726
|
+
const orderB = getGreyscaleOrder(cb.v);
|
|
727
|
+
if (orderA !== orderB) {
|
|
728
|
+
return orderA - orderB;
|
|
729
|
+
}
|
|
730
|
+
return ca.v - cb.v;
|
|
731
|
+
});
|
|
732
|
+
transparent.sort((a, b) => {
|
|
733
|
+
const ca = cache.get(a);
|
|
734
|
+
const cb = cache.get(b);
|
|
735
|
+
return ca.v - cb.v;
|
|
736
|
+
});
|
|
737
|
+
return [...normal, ...greyscale, ...transparent];
|
|
738
|
+
}
|
|
642
739
|
export {
|
|
643
740
|
RGBdistance,
|
|
644
741
|
ciede2000,
|
|
@@ -670,6 +767,9 @@ export {
|
|
|
670
767
|
isCSSRGBColour,
|
|
671
768
|
isCSSRGBDarkColour,
|
|
672
769
|
isDarkColour,
|
|
770
|
+
isHexColour,
|
|
673
771
|
isHexDarkColour,
|
|
674
|
-
|
|
772
|
+
isNamedCSSColour,
|
|
773
|
+
pickHexColourContrast,
|
|
774
|
+
sortHexColours
|
|
675
775
|
};
|