@sardine/colour 2.4.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/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 +365 -267
- package/dist/index.d.ts +4 -5
- package/dist/index.min.js +1 -7
- package/dist/index.mjs +366 -268
- 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,224 +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
|
-
function convertHextoRGB(hex) {
|
|
178
|
-
if (typeof hex !== "string") {
|
|
179
|
-
throw new Error(`convertHextoRGB expects a string but got a ${typeof hex}`);
|
|
180
|
-
}
|
|
181
|
-
if (hex.match(hexRegex)) {
|
|
182
|
-
return {
|
|
183
|
-
R: Number.parseInt(`${hex[1]}${hex[2]}`, 16),
|
|
184
|
-
G: Number.parseInt(`${hex[3]}${hex[4]}`, 16),
|
|
185
|
-
B: Number.parseInt(`${hex[5]}${hex[6]}`, 16)
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
if (hex.match(shortHexRegex)) {
|
|
189
|
-
return {
|
|
190
|
-
R: Number.parseInt(`${hex[1]}${hex[1]}`, 16),
|
|
191
|
-
G: Number.parseInt(`${hex[2]}${hex[2]}`, 16),
|
|
192
|
-
B: Number.parseInt(`${hex[3]}${hex[3]}`, 16)
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
if (hex.match(hexAlphaRegex)) {
|
|
196
|
-
return {
|
|
197
|
-
R: Number.parseInt(`${hex[1]}${hex[2]}`, 16),
|
|
198
|
-
G: Number.parseInt(`${hex[3]}${hex[4]}`, 16),
|
|
199
|
-
B: Number.parseInt(`${hex[5]}${hex[6]}`, 16),
|
|
200
|
-
A: Math.round(Number.parseInt(`${hex[7]}${hex[8]}`, 16) / 255 * 100) / 100
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
if (hex.match(shortAlphaHexRegex)) {
|
|
204
|
-
return {
|
|
205
|
-
R: Number.parseInt(`${hex[1]}${hex[1]}`, 16),
|
|
206
|
-
G: Number.parseInt(`${hex[2]}${hex[2]}`, 16),
|
|
207
|
-
B: Number.parseInt(`${hex[3]}${hex[3]}`, 16),
|
|
208
|
-
A: Math.round(Number.parseInt(`${hex[4]}${hex[4]}`, 16) / 255 * 100) / 100
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
throw new Error(
|
|
212
|
-
`convertHextoRGB expects an valid hexadecimal colour value but got ${hex}`
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
function convertHextoCSSRGB(hex) {
|
|
216
|
-
const rgb = convertHextoRGB(hex);
|
|
217
|
-
if (rgb.A) {
|
|
218
|
-
return `rgba(${rgb.R},${rgb.G},${rgb.B},${rgb.A})`;
|
|
219
|
-
}
|
|
220
|
-
return `rgb(${rgb.R},${rgb.G},${rgb.B})`;
|
|
221
|
-
}
|
|
222
3
|
const namedCSSColours = /* @__PURE__ */ new Map([
|
|
223
4
|
["aliceblue", "#f0f8ff"],
|
|
224
5
|
["antiquewhite", "#faebd7"],
|
|
@@ -325,6 +106,7 @@ const namedCSSColours = /* @__PURE__ */ new Map([
|
|
|
325
106
|
["oldlace", "#fdf5e6"],
|
|
326
107
|
["olive", "#808000"],
|
|
327
108
|
["olivedrab", "#6b8e23"],
|
|
109
|
+
["orange", "#ffa500"],
|
|
328
110
|
["orangered", "#ff4500"],
|
|
329
111
|
["orchid", "#da70d6"],
|
|
330
112
|
["palegoldenrod", "#eee8aa"],
|
|
@@ -338,6 +120,7 @@ const namedCSSColours = /* @__PURE__ */ new Map([
|
|
|
338
120
|
["plum", "#dda0dd"],
|
|
339
121
|
["powderblue", "#b0e0e6"],
|
|
340
122
|
["purple", "#800080"],
|
|
123
|
+
["rebeccapurple", "#663399"],
|
|
341
124
|
["red", "#ff0000"],
|
|
342
125
|
["rosybrown", "#bc8f8f"],
|
|
343
126
|
["royalblue", "#4169e1"],
|
|
@@ -368,6 +151,217 @@ const namedCSSColours = /* @__PURE__ */ new Map([
|
|
|
368
151
|
["yellow", "#ffff00"],
|
|
369
152
|
["yellowgreen", "#9acd32"]
|
|
370
153
|
]);
|
|
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;
|
|
282
|
+
}
|
|
283
|
+
return Math.round(percentage / 100 * 255);
|
|
284
|
+
}
|
|
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);
|
|
319
|
+
}
|
|
320
|
+
function convertHextoRGB(hex) {
|
|
321
|
+
if (typeof hex !== "string") {
|
|
322
|
+
throw new Error(`convertHextoRGB expects a string but got a ${typeof hex}`);
|
|
323
|
+
}
|
|
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
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function convertHextoCSSRGB(hex) {
|
|
359
|
+
const rgb = convertHextoRGB(hex);
|
|
360
|
+
if (rgb.A) {
|
|
361
|
+
return `rgba(${rgb.R},${rgb.G},${rgb.B},${rgb.A})`;
|
|
362
|
+
}
|
|
363
|
+
return `rgb(${rgb.R},${rgb.G},${rgb.B})`;
|
|
364
|
+
}
|
|
371
365
|
function convertHextoNamedCSSColour(colour) {
|
|
372
366
|
for (const [name, hex] of namedCSSColours.entries()) {
|
|
373
367
|
if (hex === colour) {
|
|
@@ -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;
|
|
@@ -675,3 +772,4 @@ exports.isHexColour = isHexColour;
|
|
|
675
772
|
exports.isHexDarkColour = isHexDarkColour;
|
|
676
773
|
exports.isNamedCSSColour = isNamedCSSColour;
|
|
677
774
|
exports.pickHexColourContrast = pickHexColourContrast;
|
|
775
|
+
exports.sortHexColours = sortHexColours;
|