color-value-tools 1.1.0 → 1.1.2
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 +182 -18
- package/dist/cjs/index.d.ts +116 -2
- package/dist/cjs/index.js +466 -32
- package/dist/cli/bin/cli.js +179 -0
- package/dist/cli/src/index.js +1410 -0
- package/dist/esm/index.js +435 -32
- package/package.json +6 -2
package/dist/cjs/index.js
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isCssVariable = isCssVariable;
|
|
4
4
|
exports.isHexColor = isHexColor;
|
|
5
|
+
exports.isOklchColor = isOklchColor;
|
|
6
|
+
exports.isColorFunction = isColorFunction;
|
|
5
7
|
exports.isRgbColor = isRgbColor;
|
|
6
8
|
exports.isHslColor = isHslColor;
|
|
7
9
|
exports.getColorType = getColorType;
|
|
8
10
|
exports.extractCssVariableName = extractCssVariableName;
|
|
11
|
+
exports.parseCssVar = parseCssVar;
|
|
12
|
+
exports.parseOklchString = parseOklchString;
|
|
13
|
+
exports.parseColorFn = parseColorFn;
|
|
14
|
+
exports.rgbToDisplayP3 = rgbToDisplayP3;
|
|
15
|
+
exports.displayP3ToRgb = displayP3ToRgb;
|
|
16
|
+
exports.toDisplayP3Hex = toDisplayP3Hex;
|
|
17
|
+
exports.shortHexToRgba = shortHexToRgba;
|
|
9
18
|
exports.normalizeHex = normalizeHex;
|
|
10
19
|
exports.hexToRgb = hexToRgb;
|
|
11
20
|
exports.hexToRgba = hexToRgba;
|
|
@@ -67,6 +76,28 @@ exports.toHslString = toHslString;
|
|
|
67
76
|
exports.colorDeltaE = colorDeltaE;
|
|
68
77
|
exports.randomColor = randomColor;
|
|
69
78
|
exports.toNearestNamedColor = toNearestNamedColor;
|
|
79
|
+
exports.toOklchString = toOklchString;
|
|
80
|
+
exports.toColorP3String = toColorP3String;
|
|
81
|
+
exports.interpolateColors = interpolateColors;
|
|
82
|
+
exports.createColorScale = createColorScale;
|
|
83
|
+
exports.midpointColor = midpointColor;
|
|
84
|
+
exports.tints = tints;
|
|
85
|
+
exports.shades = shades;
|
|
86
|
+
exports.tones = tones;
|
|
87
|
+
exports.simulateProtanopia = simulateProtanopia;
|
|
88
|
+
exports.simulateDeuteranopia = simulateDeuteranopia;
|
|
89
|
+
exports.simulateTritanopia = simulateTritanopia;
|
|
90
|
+
exports.simulateColorBlindness = simulateColorBlindness;
|
|
91
|
+
exports.isReadableOnBackground = isReadableOnBackground;
|
|
92
|
+
exports.bestContrastPalette = bestContrastPalette;
|
|
93
|
+
exports.clearColorCache = clearColorCache;
|
|
94
|
+
exports.getCacheStats = getCacheStats;
|
|
95
|
+
exports.enableCache = enableCache;
|
|
96
|
+
exports.disableCache = disableCache;
|
|
97
|
+
exports.normalizeColorCached = normalizeColorCached;
|
|
98
|
+
exports.generateGradientColors = generateGradientColors;
|
|
99
|
+
exports.generateTints = generateTints;
|
|
100
|
+
exports.generateShades = generateShades;
|
|
70
101
|
const NAMED_COLORS = {
|
|
71
102
|
aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff',
|
|
72
103
|
beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff',
|
|
@@ -107,7 +138,13 @@ function isCssVariable(value) {
|
|
|
107
138
|
}
|
|
108
139
|
function isHexColor(value) {
|
|
109
140
|
const trimmed = value.trim();
|
|
110
|
-
return /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(trimmed);
|
|
141
|
+
return /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6})$/.test(trimmed);
|
|
142
|
+
}
|
|
143
|
+
function isOklchColor(value) {
|
|
144
|
+
return /^\s*oklcha?\s*\(/i.test(value.trim());
|
|
145
|
+
}
|
|
146
|
+
function isColorFunction(value) {
|
|
147
|
+
return /^\s*color\s*\(\s*(display-p3|srgb|srgb-linear)\s/i.test(value.trim());
|
|
111
148
|
}
|
|
112
149
|
function isRgbColor(value) {
|
|
113
150
|
const trimmed = value.trim().toLowerCase();
|
|
@@ -118,7 +155,8 @@ function isHslColor(value) {
|
|
|
118
155
|
return trimmed.startsWith('hsl(') || trimmed.startsWith('hsla(');
|
|
119
156
|
}
|
|
120
157
|
function getColorType(value) {
|
|
121
|
-
const trimmed = value.trim()
|
|
158
|
+
const trimmed = value.trim();
|
|
159
|
+
const lower = trimmed.toLowerCase();
|
|
122
160
|
if (isCssVariable(trimmed))
|
|
123
161
|
return 'css-var';
|
|
124
162
|
if (isHexColor(trimmed))
|
|
@@ -127,13 +165,116 @@ function getColorType(value) {
|
|
|
127
165
|
return 'rgb';
|
|
128
166
|
if (isHslColor(trimmed))
|
|
129
167
|
return 'hsl';
|
|
130
|
-
if (trimmed
|
|
168
|
+
if (isOklchColor(trimmed))
|
|
169
|
+
return 'oklch';
|
|
170
|
+
if (isColorFunction(trimmed))
|
|
171
|
+
return 'color';
|
|
172
|
+
if (lower in NAMED_COLORS)
|
|
131
173
|
return 'named';
|
|
132
174
|
return 'unknown';
|
|
133
175
|
}
|
|
134
176
|
function extractCssVariableName(value) {
|
|
135
|
-
|
|
136
|
-
|
|
177
|
+
var _a;
|
|
178
|
+
const match = value.match(/var\(\s*(--[^,)]+)/);
|
|
179
|
+
return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.trim()) || value;
|
|
180
|
+
}
|
|
181
|
+
function parseCssVar(value) {
|
|
182
|
+
var _a;
|
|
183
|
+
const m = value.trim().match(/^var\(\s*(--[^,)]+)(?:,\s*([\s\S]+?))?\s*\)$/);
|
|
184
|
+
if (!m)
|
|
185
|
+
return null;
|
|
186
|
+
return { variableName: m[1].trim(), fallback: (_a = m[2]) === null || _a === void 0 ? void 0 : _a.trim() };
|
|
187
|
+
}
|
|
188
|
+
// ─── Section 1.1 — OKLCH / color() CSS string parsing ────────────────────────
|
|
189
|
+
function parseOklchString(str) {
|
|
190
|
+
const m = str.trim().match(/oklcha?\(\s*([\d.]+%?)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\s*\)/i);
|
|
191
|
+
if (!m)
|
|
192
|
+
return null;
|
|
193
|
+
let L = parseFloat(m[1]);
|
|
194
|
+
if (m[1].endsWith('%'))
|
|
195
|
+
L = L / 100;
|
|
196
|
+
const C = parseFloat(m[2]);
|
|
197
|
+
const H = parseFloat(m[3]);
|
|
198
|
+
const alpha = m[4] !== undefined ? parseFloat(m[4]) : 1;
|
|
199
|
+
return { L, C, H, alpha };
|
|
200
|
+
}
|
|
201
|
+
function parseColorFn(str) {
|
|
202
|
+
const m = str.trim().match(/color\(\s*([\w-]+)\s+([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+%?)(?:\s*\/\s*([\d.]+))?\s*\)/i);
|
|
203
|
+
if (!m)
|
|
204
|
+
return null;
|
|
205
|
+
const space = m[1].toLowerCase();
|
|
206
|
+
const parseVal = (v) => v.endsWith('%') ? parseFloat(v) / 100 : parseFloat(v);
|
|
207
|
+
const r = parseVal(m[2]);
|
|
208
|
+
const g = parseVal(m[3]);
|
|
209
|
+
const b = parseVal(m[4]);
|
|
210
|
+
const alpha = m[5] !== undefined ? parseFloat(m[5]) : 1;
|
|
211
|
+
return { space, r, g, b, alpha };
|
|
212
|
+
}
|
|
213
|
+
// ─── Section 1.2 — Display P3 ────────────────────────────────────────────────
|
|
214
|
+
function rgbToDisplayP3(rgb) {
|
|
215
|
+
// Remove sRGB gamma (linearize)
|
|
216
|
+
const rl = srgbChanToLinear(rgb.r / 255);
|
|
217
|
+
const gl = srgbChanToLinear(rgb.g / 255);
|
|
218
|
+
const bl = srgbChanToLinear(rgb.b / 255);
|
|
219
|
+
// sRGB-linear → P3-linear matrix
|
|
220
|
+
const pr = 0.8226 * rl + 0.1774 * gl + 0.0000 * bl;
|
|
221
|
+
const pg = 0.0332 * rl + 0.9669 * gl + 0.0000 * bl;
|
|
222
|
+
const pb = 0.0171 * rl + 0.0724 * gl + 0.9103 * bl;
|
|
223
|
+
// Apply P3 gamma (same as sRGB)
|
|
224
|
+
return {
|
|
225
|
+
r: linearChanToSrgb(pr),
|
|
226
|
+
g: linearChanToSrgb(pg),
|
|
227
|
+
b: linearChanToSrgb(pb),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function displayP3ToRgb(p3) {
|
|
231
|
+
// Remove P3 gamma
|
|
232
|
+
const rl = srgbChanToLinear(p3.r);
|
|
233
|
+
const gl = srgbChanToLinear(p3.g);
|
|
234
|
+
const bl = srgbChanToLinear(p3.b);
|
|
235
|
+
// P3-linear → sRGB-linear inverse matrix
|
|
236
|
+
const sr = 1.2247 * rl + (-0.2247) * gl + 0.0000 * bl;
|
|
237
|
+
const sg = (-0.0421) * rl + 1.0432 * gl + 0.0000 * bl;
|
|
238
|
+
const sb = (-0.0197) * rl + (-0.0786) * gl + 1.0983 * bl;
|
|
239
|
+
// Apply sRGB gamma
|
|
240
|
+
return {
|
|
241
|
+
r: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sr)))),
|
|
242
|
+
g: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sg)))),
|
|
243
|
+
b: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sb)))),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
// Helpers for Display P3 (operate on 0-1 values)
|
|
247
|
+
function srgbChanToLinear(v) {
|
|
248
|
+
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
249
|
+
}
|
|
250
|
+
function linearChanToSrgb(v) {
|
|
251
|
+
const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
|
|
252
|
+
return Math.max(0, Math.min(1, t));
|
|
253
|
+
}
|
|
254
|
+
function toDisplayP3Hex(color) {
|
|
255
|
+
const n = normalizeColorRaw(color);
|
|
256
|
+
const p3 = rgbToDisplayP3({ r: n.r, g: n.g, b: n.b });
|
|
257
|
+
const back = displayP3ToRgb(p3);
|
|
258
|
+
return rgbToHex(back);
|
|
259
|
+
}
|
|
260
|
+
// Internal raw normalizer (no cache, no circular dep issues)
|
|
261
|
+
function normalizeColorRaw(color) {
|
|
262
|
+
var _a, _b, _c, _d;
|
|
263
|
+
const n = normalizeColor(color);
|
|
264
|
+
return { r: (_a = n.r) !== null && _a !== void 0 ? _a : 0, g: (_b = n.g) !== null && _b !== void 0 ? _b : 0, b: (_c = n.b) !== null && _c !== void 0 ? _c : 0, a: (_d = n.a) !== null && _d !== void 0 ? _d : 1 };
|
|
265
|
+
}
|
|
266
|
+
// ─── Section 1.3 — Short RGBA hex #RGBA ──────────────────────────────────────
|
|
267
|
+
function shortHexToRgba(hex) {
|
|
268
|
+
let h = hex.trim();
|
|
269
|
+
if (!h.startsWith('#'))
|
|
270
|
+
h = `#${h}`;
|
|
271
|
+
if (!/^#[0-9a-f]{4}$/i.test(h))
|
|
272
|
+
return null;
|
|
273
|
+
const r = parseInt(h[1] + h[1], 16);
|
|
274
|
+
const g = parseInt(h[2] + h[2], 16);
|
|
275
|
+
const b = parseInt(h[3] + h[3], 16);
|
|
276
|
+
const a = parseInt(h[4] + h[4], 16) / 255;
|
|
277
|
+
return { r, g, b, a };
|
|
137
278
|
}
|
|
138
279
|
function normalizeHex(hex) {
|
|
139
280
|
let cleanHex = hex.trim();
|
|
@@ -563,6 +704,52 @@ function normalizeColor(input) {
|
|
|
563
704
|
}
|
|
564
705
|
if (str === 'transparent')
|
|
565
706
|
return { type: 'named', hex: '#000000', r: 0, g: 0, b: 0, a: 0 };
|
|
707
|
+
// 4-digit #RGBA hex
|
|
708
|
+
if (/^#[0-9a-f]{4}$/i.test(str)) {
|
|
709
|
+
const rgba = shortHexToRgba(str);
|
|
710
|
+
if (rgba) {
|
|
711
|
+
const { r, g, b, a } = rgba;
|
|
712
|
+
const hex = rgbToHex({ r, g, b });
|
|
713
|
+
const [h, s, l] = rgbToHsl({ r, g, b });
|
|
714
|
+
const [, , vv] = rgbToHsv({ r, g, b });
|
|
715
|
+
return { type: 'hex', hex, r, g, b, a, h, s, l, v: vv };
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
// oklch / oklcha
|
|
719
|
+
if (isOklchColor(str)) {
|
|
720
|
+
const parsed = parseOklchString(str);
|
|
721
|
+
if (parsed) {
|
|
722
|
+
const { r, g, b } = oklchToRgb({ L: parsed.L, C: parsed.C, H: parsed.H });
|
|
723
|
+
const hex = rgbToHex({ r, g, b });
|
|
724
|
+
const [h, s, l] = rgbToHsl({ r, g, b });
|
|
725
|
+
const [, , vv] = rgbToHsv({ r, g, b });
|
|
726
|
+
return { type: 'oklch', hex, r, g, b, a: parsed.alpha, h, s, l, v: vv };
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
// color(display-p3 ...) / color(srgb ...)
|
|
730
|
+
if (isColorFunction(str)) {
|
|
731
|
+
const parsed = parseColorFn(str);
|
|
732
|
+
if (parsed) {
|
|
733
|
+
const { r: p3r, g: p3g, b: p3b, alpha } = parsed;
|
|
734
|
+
// Convert from p3/srgb (0-1) to sRGB 0-255
|
|
735
|
+
let r, g, b;
|
|
736
|
+
if (parsed.space === 'display-p3') {
|
|
737
|
+
const srgb = displayP3ToRgb({ r: p3r, g: p3g, b: p3b });
|
|
738
|
+
r = srgb.r;
|
|
739
|
+
g = srgb.g;
|
|
740
|
+
b = srgb.b;
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
r = Math.round(Math.max(0, Math.min(255, p3r * 255)));
|
|
744
|
+
g = Math.round(Math.max(0, Math.min(255, p3g * 255)));
|
|
745
|
+
b = Math.round(Math.max(0, Math.min(255, p3b * 255)));
|
|
746
|
+
}
|
|
747
|
+
const hex = rgbToHex({ r, g, b });
|
|
748
|
+
const [h, s, l] = rgbToHsl({ r, g, b });
|
|
749
|
+
const [, , vv] = rgbToHsv({ r, g, b });
|
|
750
|
+
return { type: 'color', hex, r, g, b, a: alpha, h, s, l, v: vv };
|
|
751
|
+
}
|
|
752
|
+
}
|
|
566
753
|
return { type: 'unknown' };
|
|
567
754
|
}
|
|
568
755
|
function complement(color) {
|
|
@@ -613,47 +800,109 @@ function monochromatic(color, steps = 5) {
|
|
|
613
800
|
}
|
|
614
801
|
return result;
|
|
615
802
|
}
|
|
803
|
+
function _lerpHue(h1, h2, t, mode = 'shorter') {
|
|
804
|
+
let d = h2 - h1;
|
|
805
|
+
if (mode === 'shorter') {
|
|
806
|
+
if (d > 180)
|
|
807
|
+
d -= 360;
|
|
808
|
+
else if (d < -180)
|
|
809
|
+
d += 360;
|
|
810
|
+
}
|
|
811
|
+
else if (mode === 'longer') {
|
|
812
|
+
if (d > 0 && d < 180)
|
|
813
|
+
d -= 360;
|
|
814
|
+
else if (d < 0 && d > -180)
|
|
815
|
+
d += 360;
|
|
816
|
+
}
|
|
817
|
+
else if (mode === 'increasing') {
|
|
818
|
+
if (d < 0)
|
|
819
|
+
d += 360;
|
|
820
|
+
}
|
|
821
|
+
else if (mode === 'decreasing') {
|
|
822
|
+
if (d > 0)
|
|
823
|
+
d -= 360;
|
|
824
|
+
}
|
|
825
|
+
return h1 + d * t;
|
|
826
|
+
}
|
|
827
|
+
function _formatMixResult(r, g, b, a, out) {
|
|
828
|
+
if (out === 'hex')
|
|
829
|
+
return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
|
|
830
|
+
if (out === 'rgba')
|
|
831
|
+
return rgbToRgbaString({ r: Math.round(r), g: Math.round(g), b: Math.round(b) }, a);
|
|
832
|
+
if (out === 'rgb')
|
|
833
|
+
return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
|
|
834
|
+
if (out === 'hsl') {
|
|
835
|
+
const [hh, ss, ll] = rgbToHsl({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
|
|
836
|
+
return `hsl(${hh}, ${ss}%, ${ll}%)`;
|
|
837
|
+
}
|
|
838
|
+
return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
|
|
839
|
+
}
|
|
616
840
|
function mixColors(c1, c2, t, opts) {
|
|
617
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
841
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35;
|
|
618
842
|
const o1 = normalizeColor(c1);
|
|
619
843
|
const o2 = normalizeColor(c2);
|
|
620
844
|
t = Math.max(0, Math.min(1, t));
|
|
621
845
|
const mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'rgb';
|
|
622
846
|
const out = (opts === null || opts === void 0 ? void 0 : opts.format) || 'hex';
|
|
847
|
+
const hueMode = (_a = opts === null || opts === void 0 ? void 0 : opts.hueInterpolation) !== null && _a !== void 0 ? _a : 'shorter';
|
|
623
848
|
let r = 0, g = 0, b = 0, a = 1;
|
|
624
849
|
if (mode === 'hsl') {
|
|
625
|
-
const h1 = (
|
|
626
|
-
const s1 = (
|
|
627
|
-
const l1 = (
|
|
628
|
-
const h2 = (
|
|
629
|
-
const s2 = (
|
|
630
|
-
const l2 = (
|
|
631
|
-
const ih = h1
|
|
632
|
-
const
|
|
633
|
-
const il = l1 + (l2 - l1) * t;
|
|
634
|
-
const rgb = hslToRgb(ih, is, il);
|
|
850
|
+
const h1 = (_b = o1.h) !== null && _b !== void 0 ? _b : rgbToHsl({ r: (_c = o1.r) !== null && _c !== void 0 ? _c : 0, g: (_d = o1.g) !== null && _d !== void 0 ? _d : 0, b: (_e = o1.b) !== null && _e !== void 0 ? _e : 0 })[0];
|
|
851
|
+
const s1 = (_f = o1.s) !== null && _f !== void 0 ? _f : 0;
|
|
852
|
+
const l1 = (_g = o1.l) !== null && _g !== void 0 ? _g : 0;
|
|
853
|
+
const h2 = (_h = o2.h) !== null && _h !== void 0 ? _h : rgbToHsl({ r: (_j = o2.r) !== null && _j !== void 0 ? _j : 0, g: (_k = o2.g) !== null && _k !== void 0 ? _k : 0, b: (_l = o2.b) !== null && _l !== void 0 ? _l : 0 })[0];
|
|
854
|
+
const s2 = (_m = o2.s) !== null && _m !== void 0 ? _m : 0;
|
|
855
|
+
const l2 = (_o = o2.l) !== null && _o !== void 0 ? _o : 0;
|
|
856
|
+
const ih = _lerpHue(h1, h2, t, hueMode);
|
|
857
|
+
const rgb = hslToRgb(ih, s1 + (s2 - s1) * t, l1 + (l2 - l1) * t);
|
|
635
858
|
r = rgb.r;
|
|
636
859
|
g = rgb.g;
|
|
637
860
|
b = rgb.b;
|
|
638
|
-
a = ((
|
|
861
|
+
a = ((_p = o1.a) !== null && _p !== void 0 ? _p : 1) + (((_q = o2.a) !== null && _q !== void 0 ? _q : 1) - ((_r = o1.a) !== null && _r !== void 0 ? _r : 1)) * t;
|
|
639
862
|
}
|
|
640
|
-
else {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
863
|
+
else if (mode === 'lab') {
|
|
864
|
+
const lab1 = rgbToLab({ r: (_s = o1.r) !== null && _s !== void 0 ? _s : 0, g: (_t = o1.g) !== null && _t !== void 0 ? _t : 0, b: (_u = o1.b) !== null && _u !== void 0 ? _u : 0 });
|
|
865
|
+
const lab2 = rgbToLab({ r: (_v = o2.r) !== null && _v !== void 0 ? _v : 0, g: (_w = o2.g) !== null && _w !== void 0 ? _w : 0, b: (_x = o2.b) !== null && _x !== void 0 ? _x : 0 });
|
|
866
|
+
const mixed = labToRgb({ L: lab1.L + (lab2.L - lab1.L) * t, a: lab1.a + (lab2.a - lab1.a) * t, b: lab1.b + (lab2.b - lab1.b) * t });
|
|
867
|
+
r = mixed.r;
|
|
868
|
+
g = mixed.g;
|
|
869
|
+
b = mixed.b;
|
|
870
|
+
a = ((_y = o1.a) !== null && _y !== void 0 ? _y : 1) + (((_z = o2.a) !== null && _z !== void 0 ? _z : 1) - ((_0 = o1.a) !== null && _0 !== void 0 ? _0 : 1)) * t;
|
|
645
871
|
}
|
|
646
|
-
if (
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
return `hsl(${hh}, ${ss}%, ${ll}%)`;
|
|
872
|
+
else if (mode === 'lch') {
|
|
873
|
+
const lch1 = rgbToLch({ r: (_1 = o1.r) !== null && _1 !== void 0 ? _1 : 0, g: (_2 = o1.g) !== null && _2 !== void 0 ? _2 : 0, b: (_3 = o1.b) !== null && _3 !== void 0 ? _3 : 0 });
|
|
874
|
+
const lch2 = rgbToLch({ r: (_4 = o2.r) !== null && _4 !== void 0 ? _4 : 0, g: (_5 = o2.g) !== null && _5 !== void 0 ? _5 : 0, b: (_6 = o2.b) !== null && _6 !== void 0 ? _6 : 0 });
|
|
875
|
+
const mixed = lchToRgb({ L: lch1.L + (lch2.L - lch1.L) * t, C: lch1.C + (lch2.C - lch1.C) * t, H: _lerpHue(lch1.H, lch2.H, t, hueMode) });
|
|
876
|
+
r = mixed.r;
|
|
877
|
+
g = mixed.g;
|
|
878
|
+
b = mixed.b;
|
|
879
|
+
a = ((_7 = o1.a) !== null && _7 !== void 0 ? _7 : 1) + (((_8 = o2.a) !== null && _8 !== void 0 ? _8 : 1) - ((_9 = o1.a) !== null && _9 !== void 0 ? _9 : 1)) * t;
|
|
655
880
|
}
|
|
656
|
-
|
|
881
|
+
else if (mode === 'oklab') {
|
|
882
|
+
const ok1 = rgbToOklab({ r: (_10 = o1.r) !== null && _10 !== void 0 ? _10 : 0, g: (_11 = o1.g) !== null && _11 !== void 0 ? _11 : 0, b: (_12 = o1.b) !== null && _12 !== void 0 ? _12 : 0 });
|
|
883
|
+
const ok2 = rgbToOklab({ r: (_13 = o2.r) !== null && _13 !== void 0 ? _13 : 0, g: (_14 = o2.g) !== null && _14 !== void 0 ? _14 : 0, b: (_15 = o2.b) !== null && _15 !== void 0 ? _15 : 0 });
|
|
884
|
+
const mixed = oklabToRgb({ L: ok1.L + (ok2.L - ok1.L) * t, a: ok1.a + (ok2.a - ok1.a) * t, b: ok1.b + (ok2.b - ok1.b) * t });
|
|
885
|
+
r = mixed.r;
|
|
886
|
+
g = mixed.g;
|
|
887
|
+
b = mixed.b;
|
|
888
|
+
a = ((_16 = o1.a) !== null && _16 !== void 0 ? _16 : 1) + (((_17 = o2.a) !== null && _17 !== void 0 ? _17 : 1) - ((_18 = o1.a) !== null && _18 !== void 0 ? _18 : 1)) * t;
|
|
889
|
+
}
|
|
890
|
+
else if (mode === 'oklch') {
|
|
891
|
+
const ok1 = rgbToOklch({ r: (_19 = o1.r) !== null && _19 !== void 0 ? _19 : 0, g: (_20 = o1.g) !== null && _20 !== void 0 ? _20 : 0, b: (_21 = o1.b) !== null && _21 !== void 0 ? _21 : 0 });
|
|
892
|
+
const ok2 = rgbToOklch({ r: (_22 = o2.r) !== null && _22 !== void 0 ? _22 : 0, g: (_23 = o2.g) !== null && _23 !== void 0 ? _23 : 0, b: (_24 = o2.b) !== null && _24 !== void 0 ? _24 : 0 });
|
|
893
|
+
const mixed = oklchToRgb({ L: ok1.L + (ok2.L - ok1.L) * t, C: ok1.C + (ok2.C - ok1.C) * t, H: _lerpHue(ok1.H, ok2.H, t, hueMode) });
|
|
894
|
+
r = mixed.r;
|
|
895
|
+
g = mixed.g;
|
|
896
|
+
b = mixed.b;
|
|
897
|
+
a = ((_25 = o1.a) !== null && _25 !== void 0 ? _25 : 1) + (((_26 = o2.a) !== null && _26 !== void 0 ? _26 : 1) - ((_27 = o1.a) !== null && _27 !== void 0 ? _27 : 1)) * t;
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
r = ((_28 = o1.r) !== null && _28 !== void 0 ? _28 : 0) * (1 - t) + ((_29 = o2.r) !== null && _29 !== void 0 ? _29 : 0) * t;
|
|
901
|
+
g = ((_30 = o1.g) !== null && _30 !== void 0 ? _30 : 0) * (1 - t) + ((_31 = o2.g) !== null && _31 !== void 0 ? _31 : 0) * t;
|
|
902
|
+
b = ((_32 = o1.b) !== null && _32 !== void 0 ? _32 : 0) * (1 - t) + ((_33 = o2.b) !== null && _33 !== void 0 ? _33 : 0) * t;
|
|
903
|
+
a = ((_34 = o1.a) !== null && _34 !== void 0 ? _34 : 1) * (1 - t) + ((_35 = o2.a) !== null && _35 !== void 0 ? _35 : 1) * t;
|
|
904
|
+
}
|
|
905
|
+
return _formatMixResult(r, g, b, a, out);
|
|
657
906
|
}
|
|
658
907
|
function relativeLuminance(color) {
|
|
659
908
|
var _a, _b, _c;
|
|
@@ -974,3 +1223,188 @@ function toNearestNamedColor(color) {
|
|
|
974
1223
|
}
|
|
975
1224
|
return bestName;
|
|
976
1225
|
}
|
|
1226
|
+
// ─── Formatting ───────────────────────────────────────────────────────────────
|
|
1227
|
+
function toOklchString(color, alpha) {
|
|
1228
|
+
var _a, _b, _c;
|
|
1229
|
+
const n = normalizeColor(color);
|
|
1230
|
+
const { L, C, H } = rgbToOklch({ r: (_a = n.r) !== null && _a !== void 0 ? _a : 0, g: (_b = n.g) !== null && _b !== void 0 ? _b : 0, b: (_c = n.b) !== null && _c !== void 0 ? _c : 0 });
|
|
1231
|
+
const Lr = +L.toFixed(4), Cr = +C.toFixed(4), Hr = +H.toFixed(2);
|
|
1232
|
+
if (alpha !== undefined)
|
|
1233
|
+
return `oklch(${Lr} ${Cr} ${Hr} / ${+alpha.toFixed(3)})`;
|
|
1234
|
+
return `oklch(${Lr} ${Cr} ${Hr})`;
|
|
1235
|
+
}
|
|
1236
|
+
function toColorP3String(color, alpha) {
|
|
1237
|
+
var _a, _b, _c;
|
|
1238
|
+
const n = normalizeColor(color);
|
|
1239
|
+
const p3 = rgbToDisplayP3({ r: (_a = n.r) !== null && _a !== void 0 ? _a : 0, g: (_b = n.g) !== null && _b !== void 0 ? _b : 0, b: (_c = n.b) !== null && _c !== void 0 ? _c : 0 });
|
|
1240
|
+
const r = +p3.r.toFixed(4), g = +p3.g.toFixed(4), b = +p3.b.toFixed(4);
|
|
1241
|
+
if (alpha !== undefined)
|
|
1242
|
+
return `color(display-p3 ${r} ${g} ${b} / ${+alpha.toFixed(3)})`;
|
|
1243
|
+
return `color(display-p3 ${r} ${g} ${b})`;
|
|
1244
|
+
}
|
|
1245
|
+
// ─── Section 2: Interpolation ─────────────────────────────────────────────────
|
|
1246
|
+
function interpolateColors(color1, color2, steps, options) {
|
|
1247
|
+
if (steps < 2)
|
|
1248
|
+
return steps === 1 ? [mixColors(color1, color2, 0.5, options)] : [];
|
|
1249
|
+
const result = [];
|
|
1250
|
+
for (let i = 0; i < steps; i++) {
|
|
1251
|
+
result.push(mixColors(color1, color2, i / (steps - 1), options));
|
|
1252
|
+
}
|
|
1253
|
+
return result;
|
|
1254
|
+
}
|
|
1255
|
+
function createColorScale(anchors, steps, options) {
|
|
1256
|
+
const normalized = anchors.map((a, i, arr) => {
|
|
1257
|
+
var _a;
|
|
1258
|
+
return ({
|
|
1259
|
+
color: typeof a === 'string' ? a : a.color,
|
|
1260
|
+
position: typeof a === 'string' ? i / Math.max(arr.length - 1, 1) : ((_a = a.position) !== null && _a !== void 0 ? _a : i / Math.max(arr.length - 1, 1)),
|
|
1261
|
+
});
|
|
1262
|
+
});
|
|
1263
|
+
normalized.sort((a, b) => a.position - b.position);
|
|
1264
|
+
const result = [];
|
|
1265
|
+
for (let i = 0; i < steps; i++) {
|
|
1266
|
+
const t = steps === 1 ? 0 : i / (steps - 1);
|
|
1267
|
+
let lo = normalized[0], hi = normalized[normalized.length - 1];
|
|
1268
|
+
for (let j = 0; j < normalized.length - 1; j++) {
|
|
1269
|
+
if (t >= normalized[j].position && t <= normalized[j + 1].position) {
|
|
1270
|
+
lo = normalized[j];
|
|
1271
|
+
hi = normalized[j + 1];
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
const span = hi.position - lo.position;
|
|
1276
|
+
const localT = span === 0 ? 0 : (t - lo.position) / span;
|
|
1277
|
+
result.push(mixColors(lo.color, hi.color, localT, options));
|
|
1278
|
+
}
|
|
1279
|
+
return result;
|
|
1280
|
+
}
|
|
1281
|
+
function midpointColor(color1, color2, options) {
|
|
1282
|
+
var _a;
|
|
1283
|
+
return mixColors(color1, color2, 0.5, { mode: (_a = options === null || options === void 0 ? void 0 : options.space) !== null && _a !== void 0 ? _a : 'oklab' });
|
|
1284
|
+
}
|
|
1285
|
+
// ─── Section 3: Tints / Shades / Tones ───────────────────────────────────────
|
|
1286
|
+
function tints(color, steps = 5) {
|
|
1287
|
+
return interpolateColors(color, '#ffffff', steps, { space: 'oklab' });
|
|
1288
|
+
}
|
|
1289
|
+
function shades(color, steps = 5) {
|
|
1290
|
+
return interpolateColors(color, '#000000', steps, { space: 'oklab' });
|
|
1291
|
+
}
|
|
1292
|
+
function tones(color, steps = 5, gray = '#808080') {
|
|
1293
|
+
return interpolateColors(color, gray, steps, { space: 'oklab' });
|
|
1294
|
+
}
|
|
1295
|
+
// ─── Section 4: Color blindness simulation ────────────────────────────────────
|
|
1296
|
+
function _simulateCB(color, matrix) {
|
|
1297
|
+
var _a, _b, _c;
|
|
1298
|
+
const n = normalizeColor(color);
|
|
1299
|
+
// linearize
|
|
1300
|
+
const lin = (v) => { const c = v / 255; return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); };
|
|
1301
|
+
const enc = (v) => { const c = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055; return Math.round(Math.max(0, Math.min(1, c)) * 255); };
|
|
1302
|
+
const r = lin((_a = n.r) !== null && _a !== void 0 ? _a : 0), g = lin((_b = n.g) !== null && _b !== void 0 ? _b : 0), b = lin((_c = n.b) !== null && _c !== void 0 ? _c : 0);
|
|
1303
|
+
const nr = enc(matrix[0][0] * r + matrix[0][1] * g + matrix[0][2] * b);
|
|
1304
|
+
const ng = enc(matrix[1][0] * r + matrix[1][1] * g + matrix[1][2] * b);
|
|
1305
|
+
const nb = enc(matrix[2][0] * r + matrix[2][1] * g + matrix[2][2] * b);
|
|
1306
|
+
return rgbToHex({ r: nr, g: ng, b: nb });
|
|
1307
|
+
}
|
|
1308
|
+
const _PROTANOPIA_M = [[0.56667, 0.43333, 0.00000], [0.55833, 0.44167, 0.00000], [0.00000, 0.24167, 0.75833]];
|
|
1309
|
+
const _DEUTERANOPIA_M = [[0.62500, 0.37500, 0.00000], [0.70000, 0.30000, 0.00000], [0.00000, 0.30000, 0.70000]];
|
|
1310
|
+
const _TRITANOPIA_M = [[0.95000, 0.05000, 0.00000], [0.00000, 0.43333, 0.56667], [0.00000, 0.47500, 0.52500]];
|
|
1311
|
+
function simulateProtanopia(color) { return _simulateCB(color, _PROTANOPIA_M); }
|
|
1312
|
+
function simulateDeuteranopia(color) { return _simulateCB(color, _DEUTERANOPIA_M); }
|
|
1313
|
+
function simulateTritanopia(color) { return _simulateCB(color, _TRITANOPIA_M); }
|
|
1314
|
+
function simulateColorBlindness(color, type) {
|
|
1315
|
+
if (type === 'protanopia')
|
|
1316
|
+
return simulateProtanopia(color);
|
|
1317
|
+
if (type === 'deuteranopia')
|
|
1318
|
+
return simulateDeuteranopia(color);
|
|
1319
|
+
return simulateTritanopia(color);
|
|
1320
|
+
}
|
|
1321
|
+
function isReadableOnBackground(textColor, background, options) {
|
|
1322
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
1323
|
+
const level = (_a = options === null || options === void 0 ? void 0 : options.level) !== null && _a !== void 0 ? _a : 'AA';
|
|
1324
|
+
const large = (_b = options === null || options === void 0 ? void 0 : options.largeText) !== null && _b !== void 0 ? _b : false;
|
|
1325
|
+
const minRequired = level === 'AAA' ? (large ? 4.5 : 7) : (large ? 3 : 4.5);
|
|
1326
|
+
let minRatio;
|
|
1327
|
+
if (typeof background === 'string') {
|
|
1328
|
+
minRatio = contrastRatio(textColor, background);
|
|
1329
|
+
}
|
|
1330
|
+
else if (background.type === 'semi-transparent') {
|
|
1331
|
+
const underlay = (_c = background.underlay) !== null && _c !== void 0 ? _c : '#ffffff';
|
|
1332
|
+
const fg = normalizeColor(background.color);
|
|
1333
|
+
const bg = normalizeColor(underlay);
|
|
1334
|
+
const alpha = (_d = fg.a) !== null && _d !== void 0 ? _d : 1;
|
|
1335
|
+
const cr = Math.round(((_e = fg.r) !== null && _e !== void 0 ? _e : 0) * alpha + ((_f = bg.r) !== null && _f !== void 0 ? _f : 255) * (1 - alpha));
|
|
1336
|
+
const cg = Math.round(((_g = fg.g) !== null && _g !== void 0 ? _g : 0) * alpha + ((_h = bg.g) !== null && _h !== void 0 ? _h : 255) * (1 - alpha));
|
|
1337
|
+
const cb = Math.round(((_j = fg.b) !== null && _j !== void 0 ? _j : 0) * alpha + ((_k = bg.b) !== null && _k !== void 0 ? _k : 255) * (1 - alpha));
|
|
1338
|
+
minRatio = contrastRatio(textColor, rgbToHex({ r: cr, g: cg, b: cb }));
|
|
1339
|
+
}
|
|
1340
|
+
else {
|
|
1341
|
+
const ratios = background.stops.map(stop => contrastRatio(textColor, stop));
|
|
1342
|
+
minRatio = Math.min(...ratios);
|
|
1343
|
+
}
|
|
1344
|
+
const wLevel = wcagLevel(textColor, typeof background === 'string' ? background : '#ffffff');
|
|
1345
|
+
return { readable: minRatio >= minRequired, minContrastRatio: +minRatio.toFixed(2), wcagLevel: wLevel };
|
|
1346
|
+
}
|
|
1347
|
+
function bestContrastPalette(background, palettes, options) {
|
|
1348
|
+
let bestIdx = 0;
|
|
1349
|
+
let bestScore = -1;
|
|
1350
|
+
let bestMin = 0, bestAvg = 0;
|
|
1351
|
+
palettes.forEach((palette, idx) => {
|
|
1352
|
+
var _a;
|
|
1353
|
+
const ratios = palette.map(c => contrastRatio(background, c));
|
|
1354
|
+
const weights = (_a = options === null || options === void 0 ? void 0 : options.weights) !== null && _a !== void 0 ? _a : ratios.map(() => 1);
|
|
1355
|
+
const totalW = weights.reduce((s, w) => s + w, 0);
|
|
1356
|
+
const avg = ratios.reduce((s, r, i) => { var _a; return s + r * ((_a = weights[i]) !== null && _a !== void 0 ? _a : 1); }, 0) / totalW;
|
|
1357
|
+
const min = Math.min(...ratios);
|
|
1358
|
+
// Score: weighted avg with heavy penalty on minimum
|
|
1359
|
+
const score = avg * 0.4 + min * 0.6;
|
|
1360
|
+
if (score > bestScore) {
|
|
1361
|
+
bestScore = score;
|
|
1362
|
+
bestIdx = idx;
|
|
1363
|
+
bestMin = min;
|
|
1364
|
+
bestAvg = avg;
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
return {
|
|
1368
|
+
paletteIndex: bestIdx,
|
|
1369
|
+
palette: palettes[bestIdx],
|
|
1370
|
+
minContrastRatio: +bestMin.toFixed(2),
|
|
1371
|
+
avgContrastRatio: +bestAvg.toFixed(2),
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
// ─── Section 5.1: Caching ────────────────────────────────────────────────────
|
|
1375
|
+
let _cacheEnabled = true;
|
|
1376
|
+
const _normalizeCache = new Map();
|
|
1377
|
+
let _cacheHits = 0;
|
|
1378
|
+
function clearColorCache() { _normalizeCache.clear(); _cacheHits = 0; }
|
|
1379
|
+
function getCacheStats() { return { size: _normalizeCache.size, hits: _cacheHits }; }
|
|
1380
|
+
function enableCache() { _cacheEnabled = true; }
|
|
1381
|
+
function disableCache() { _cacheEnabled = false; }
|
|
1382
|
+
// Cached normalizer wrapper — use this in performance-sensitive contexts
|
|
1383
|
+
function normalizeColorCached(input) {
|
|
1384
|
+
if (!_cacheEnabled)
|
|
1385
|
+
return normalizeColor(input);
|
|
1386
|
+
const cached = _normalizeCache.get(input);
|
|
1387
|
+
if (cached) {
|
|
1388
|
+
_cacheHits++;
|
|
1389
|
+
return cached;
|
|
1390
|
+
}
|
|
1391
|
+
const result = normalizeColor(input);
|
|
1392
|
+
_normalizeCache.set(input, result);
|
|
1393
|
+
return result;
|
|
1394
|
+
}
|
|
1395
|
+
// ─── Section 5.2: Generator functions ────────────────────────────────────────
|
|
1396
|
+
function* generateGradientColors(start, end, steps, options) {
|
|
1397
|
+
for (let i = 0; i < steps; i++) {
|
|
1398
|
+
yield mixColors(start, end, steps === 1 ? 0 : i / (steps - 1), options);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function* generateTints(color, steps, options) {
|
|
1402
|
+
for (let i = 0; i < steps; i++) {
|
|
1403
|
+
yield mixColors(color, '#ffffff', steps === 1 ? 0 : i / (steps - 1), { mode: 'oklab', ...options });
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
function* generateShades(color, steps, options) {
|
|
1407
|
+
for (let i = 0; i < steps; i++) {
|
|
1408
|
+
yield mixColors(color, '#000000', steps === 1 ? 0 : i / (steps - 1), { mode: 'oklab', ...options });
|
|
1409
|
+
}
|
|
1410
|
+
}
|