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