color-value-tools 1.2.1 → 1.2.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.
Files changed (49) hide show
  1. package/README.md +18 -0
  2. package/dist/cjs/a11y.d.ts +40 -0
  3. package/dist/cjs/a11y.js +253 -0
  4. package/dist/cjs/cache.d.ts +9 -0
  5. package/dist/cjs/cache.js +38 -0
  6. package/dist/cjs/convert/format.d.ts +3 -0
  7. package/dist/cjs/convert/format.js +42 -0
  8. package/dist/cjs/convert/index.d.ts +2 -0
  9. package/dist/cjs/convert/index.js +18 -0
  10. package/dist/cjs/convert/spaces.d.ts +189 -0
  11. package/dist/cjs/convert/spaces.js +556 -0
  12. package/dist/cjs/index.d.ts +6 -397
  13. package/dist/cjs/index.js +20 -1466
  14. package/dist/cjs/internal/named-colors.d.ts +1 -0
  15. package/dist/cjs/internal/named-colors.js +165 -0
  16. package/dist/cjs/internal/transfer.d.ts +2 -0
  17. package/dist/cjs/internal/transfer.js +12 -0
  18. package/dist/cjs/manipulate.d.ts +15 -0
  19. package/dist/cjs/manipulate.js +223 -0
  20. package/dist/cjs/palette.d.ts +40 -0
  21. package/dist/cjs/palette.js +164 -0
  22. package/dist/cjs/parse.d.ts +102 -0
  23. package/dist/cjs/parse.js +257 -0
  24. package/dist/cli/{bin/cli.js → cli.js} +28 -26
  25. package/dist/esm/a11y.d.ts +40 -0
  26. package/dist/esm/a11y.js +237 -0
  27. package/dist/esm/cache.d.ts +9 -0
  28. package/dist/esm/cache.js +31 -0
  29. package/dist/esm/convert/format.d.ts +3 -0
  30. package/dist/esm/convert/format.js +37 -0
  31. package/dist/esm/convert/index.d.ts +2 -0
  32. package/dist/esm/convert/index.js +2 -0
  33. package/dist/esm/convert/spaces.d.ts +189 -0
  34. package/dist/esm/convert/spaces.js +520 -0
  35. package/dist/esm/index.d.ts +6 -0
  36. package/dist/esm/index.js +6 -1368
  37. package/dist/esm/internal/named-colors.d.ts +1 -0
  38. package/dist/esm/internal/named-colors.js +162 -0
  39. package/dist/esm/internal/transfer.d.ts +2 -0
  40. package/dist/esm/internal/transfer.js +8 -0
  41. package/dist/esm/manipulate.d.ts +15 -0
  42. package/dist/esm/manipulate.js +210 -0
  43. package/dist/esm/package.json +3 -1
  44. package/dist/esm/palette.d.ts +40 -0
  45. package/dist/esm/palette.js +145 -0
  46. package/dist/esm/parse.d.ts +102 -0
  47. package/dist/esm/parse.js +241 -0
  48. package/package.json +100 -8
  49. package/dist/cli/src/index.js +0 -1468
package/dist/esm/index.js CHANGED
@@ -1,1368 +1,6 @@
1
- const NAMED_COLORS = {
2
- aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff',
3
- beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff',
4
- blueviolet: '#8a2be2', brown: '#a52a2a', burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00',
5
- chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c',
6
- cyan: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b', darkgray: '#a9a9a9',
7
- darkgreen: '#006400', darkgrey: '#a9a9a9', darkkhaki: '#bdb76b', darkmagenta: '#8b008b', darkolivegreen: '#556b2f',
8
- darkorange: '#ff8c00', darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f',
9
- darkslateblue: '#483d8b', darkslategray: '#2f4f4f', darkslategrey: '#2f4f4f', darkturquoise: '#00ced1',
10
- darkviolet: '#9400d3', deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dimgrey: '#696969',
11
- dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22', fuchsia: '#ff00ff',
12
- gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700', goldenrod: '#daa520', gray: '#808080',
13
- green: '#008000', greenyellow: '#adff2f', grey: '#808080', honeydew: '#f0fff0', hotpink: '#ff69b4',
14
- indianred: '#cd5c5c', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa',
15
- lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6', lightcoral: '#f08080',
16
- lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3', lightgreen: '#90ee90',
17
- lightgrey: '#d3d3d3', lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa', lightskyblue: '#87cefa',
18
- lightslategray: '#778899', lightslategrey: '#778899', lightsteelblue: '#b0c4de', lightyellow: '#ffffe0',
19
- lime: '#00ff00', limegreen: '#32cd32', linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000',
20
- mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370db',
21
- mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc',
22
- mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1', moccasin: '#ffe4b5',
23
- navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23',
24
- orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98',
25
- paleturquoise: '#afeeee', palevioletred: '#db7093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f',
26
- pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080', rebeccapurple: '#663399',
27
- red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1', saddlebrown: '#8b4513', salmon: '#fa8072',
28
- sandybrown: '#f4a460', seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0',
29
- skyblue: '#87ceeb', slateblue: '#6a5acd', slategray: '#708090', slategrey: '#708090', snow: '#fffafa',
30
- springgreen: '#00ff7f', steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080', thistle: '#d8bfd8',
31
- tomato: '#ff6347', turquoise: '#40e0d0', violet: '#ee82ee', wheat: '#f5deb3', white: '#ffffff',
32
- whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32',
33
- // 'transparent' is the only CSS-wide keyword with an actual fixed color (fully
34
- // transparent black) — normalizeColor() special-cases it before ever reaching
35
- // this table (see the `str === 'transparent'` check), so this entry only
36
- // matters for getColorType()/toNearestNamedColor(), which read the table
37
- // directly. 'currentcolor'/'inherit'/'initial'/'unset' are deliberately NOT
38
- // listed here: none of them has a color value of their own (they only resolve
39
- // to one given surrounding DOM/CSSOM context this library never has access
40
- // to), so treating them as resolvable named colors would mean fabricating an
41
- // arbitrary RGB value with no way for a caller to tell it apart from a real
42
- // one — see normalizeColor()'s `type: 'unknown'` fallback for these instead.
43
- transparent: 'transparent',
44
- };
45
- export function isCssVariable(value) {
46
- return value.trim().startsWith('var(--');
47
- }
48
- export function isHexColor(value) {
49
- const trimmed = value.trim();
50
- return /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(trimmed);
51
- }
52
- export function isOklchColor(value) {
53
- return /^\s*oklcha?\s*\(/i.test(value.trim());
54
- }
55
- export function isColorFunction(value) {
56
- return /^\s*color\s*\(\s*(display-p3|srgb|srgb-linear)\s/i.test(value.trim());
57
- }
58
- export function isRgbColor(value) {
59
- const trimmed = value.trim().toLowerCase();
60
- return trimmed.startsWith('rgb(') || trimmed.startsWith('rgba(');
61
- }
62
- export function isHslColor(value) {
63
- const trimmed = value.trim().toLowerCase();
64
- return trimmed.startsWith('hsl(') || trimmed.startsWith('hsla(');
65
- }
66
- export function getColorType(value) {
67
- const trimmed = value.trim();
68
- const lower = trimmed.toLowerCase();
69
- if (isCssVariable(trimmed))
70
- return 'css-var';
71
- if (isHexColor(trimmed))
72
- return 'hex';
73
- if (isRgbColor(trimmed))
74
- return 'rgb';
75
- if (isHslColor(trimmed))
76
- return 'hsl';
77
- if (isOklchColor(trimmed))
78
- return 'oklch';
79
- if (isColorFunction(trimmed))
80
- return 'color';
81
- if (lower in NAMED_COLORS)
82
- return 'named';
83
- return 'unknown';
84
- }
85
- export function extractCssVariableName(value) {
86
- var _a;
87
- const match = value.match(/var\(\s*(--[^,)]+)/);
88
- return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.trim()) || value;
89
- }
90
- export function parseCssVar(value) {
91
- var _a;
92
- const m = value.trim().match(/^var\(\s*(--[^,)]+)(?:,\s*([\s\S]+?))?\s*\)$/);
93
- if (!m)
94
- return null;
95
- return { variableName: m[1].trim(), fallback: (_a = m[2]) === null || _a === void 0 ? void 0 : _a.trim() };
96
- }
97
- // ─── Section 1.1 — OKLCH / color() CSS string parsing ────────────────────────
98
- export function parseOklchString(str) {
99
- const m = str.trim().match(/oklcha?\(\s*([\d.]+%?)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\s*\)/i);
100
- if (!m)
101
- return null;
102
- let L = parseFloat(m[1]);
103
- if (m[1].endsWith('%'))
104
- L = L / 100;
105
- const C = parseFloat(m[2]);
106
- const H = parseFloat(m[3]);
107
- const alpha = m[4] !== undefined ? parseFloat(m[4]) : 1;
108
- return { L, C, H, alpha };
109
- }
110
- export function parseColorFn(str) {
111
- const m = str.trim().match(/color\(\s*([\w-]+)\s+([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+%?)(?:\s*\/\s*([\d.]+))?\s*\)/i);
112
- if (!m)
113
- return null;
114
- const space = m[1].toLowerCase();
115
- const parseVal = (v) => v.endsWith('%') ? parseFloat(v) / 100 : parseFloat(v);
116
- const r = parseVal(m[2]);
117
- const g = parseVal(m[3]);
118
- const b = parseVal(m[4]);
119
- const alpha = m[5] !== undefined ? parseFloat(m[5]) : 1;
120
- return { space, r, g, b, alpha };
121
- }
122
- // ─── Section 1.2 — Display P3 ────────────────────────────────────────────────
123
- export function rgbToDisplayP3(rgb) {
124
- // Remove sRGB gamma (linearize)
125
- const rl = srgbChanToLinear(rgb.r / 255);
126
- const gl = srgbChanToLinear(rgb.g / 255);
127
- const bl = srgbChanToLinear(rgb.b / 255);
128
- // sRGB-linear → P3-linear matrix
129
- const pr = 0.8226 * rl + 0.1774 * gl + 0.0000 * bl;
130
- const pg = 0.0332 * rl + 0.9669 * gl + 0.0000 * bl;
131
- const pb = 0.0171 * rl + 0.0724 * gl + 0.9103 * bl;
132
- // Apply P3 gamma (same as sRGB)
133
- return {
134
- r: linearChanToSrgb(pr),
135
- g: linearChanToSrgb(pg),
136
- b: linearChanToSrgb(pb),
137
- };
138
- }
139
- export function displayP3ToRgb(p3) {
140
- // Remove P3 gamma
141
- const rl = srgbChanToLinear(p3.r);
142
- const gl = srgbChanToLinear(p3.g);
143
- const bl = srgbChanToLinear(p3.b);
144
- // P3-linear → sRGB-linear inverse matrix
145
- const sr = 1.2247 * rl + (-0.2247) * gl + 0.0000 * bl;
146
- const sg = (-0.0421) * rl + 1.0432 * gl + 0.0000 * bl;
147
- const sb = (-0.0197) * rl + (-0.0786) * gl + 1.0983 * bl;
148
- // Apply sRGB gamma and scale to 0-255
149
- return {
150
- r: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sr) * 255))),
151
- g: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sg) * 255))),
152
- b: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sb) * 255))),
153
- };
154
- }
155
- // Helpers for Display P3 (operate on 0-1 values)
156
- function srgbChanToLinear(v) {
157
- return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
158
- }
159
- function linearChanToSrgb(v) {
160
- const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
161
- return Math.max(0, Math.min(1, t));
162
- }
163
- export function toDisplayP3Hex(color) {
164
- const n = normalizeColorRaw(color);
165
- const p3 = rgbToDisplayP3({ r: n.r, g: n.g, b: n.b });
166
- return rgbToHex({
167
- r: Math.round(Math.max(0, Math.min(255, p3.r * 255))),
168
- g: Math.round(Math.max(0, Math.min(255, p3.g * 255))),
169
- b: Math.round(Math.max(0, Math.min(255, p3.b * 255))),
170
- });
171
- }
172
- // Internal raw normalizer (no cache, no circular dep issues)
173
- function normalizeColorRaw(color) {
174
- var _a, _b, _c, _d;
175
- const n = normalizeColor(color);
176
- 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 };
177
- }
178
- // ─── Section 1.3 — Short RGBA hex #RGBA ──────────────────────────────────────
179
- export function shortHexToRgba(hex) {
180
- let h = hex.trim();
181
- if (!h.startsWith('#'))
182
- h = `#${h}`;
183
- if (!/^#[0-9a-f]{4}$/i.test(h))
184
- return null;
185
- const r = parseInt(h[1] + h[1], 16);
186
- const g = parseInt(h[2] + h[2], 16);
187
- const b = parseInt(h[3] + h[3], 16);
188
- const a = parseInt(h[4] + h[4], 16) / 255;
189
- return { r, g, b, a };
190
- }
191
- export function normalizeHex(hex) {
192
- let cleanHex = hex.trim();
193
- if (!cleanHex.startsWith('#'))
194
- cleanHex = `#${cleanHex}`;
195
- if (cleanHex.length === 4) {
196
- cleanHex = `#${cleanHex.slice(1).split('').map(c => c + c).join('')}`;
197
- }
198
- if (!/^#[0-9A-Fa-f]{6}$/.test(cleanHex)) {
199
- return '#f5e477';
200
- }
201
- return cleanHex.toLowerCase();
202
- }
203
- export function hexToRgb(hex) {
204
- const normalizedHex = normalizeHex(hex);
205
- const r = parseInt(normalizedHex.slice(1, 3), 16);
206
- const g = parseInt(normalizedHex.slice(3, 5), 16);
207
- const b = parseInt(normalizedHex.slice(5, 7), 16);
208
- return [r, g, b];
209
- }
210
- export function hexToRgba(hex, opacity = 1) {
211
- const [r, g, b] = hexToRgb(hex);
212
- return `rgba(${r}, ${g}, ${b}, ${opacity})`;
213
- }
214
- export function hexToHsl(hex) {
215
- const [r, g, b] = hexToRgb(hex);
216
- const red = r / 255;
217
- const green = g / 255;
218
- const blue = b / 255;
219
- const max = Math.max(red, green, blue);
220
- const min = Math.min(red, green, blue);
221
- let h = 0, s = 0;
222
- const l = (max + min) / 2;
223
- if (max !== min) {
224
- const d = max - min;
225
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
226
- switch (max) {
227
- case red:
228
- h = (green - blue) / d + (green < blue ? 6 : 0);
229
- break;
230
- case green:
231
- h = (blue - red) / d + 2;
232
- break;
233
- case blue:
234
- h = (red - green) / d + 4;
235
- break;
236
- }
237
- h /= 6;
238
- }
239
- return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
240
- }
241
- export function hslToHex(h, s, l) {
242
- h = ((h % 360) + 360) % 360;
243
- s = Math.max(0, Math.min(100, s)) / 100;
244
- l = Math.max(0, Math.min(100, l)) / 100;
245
- const c = (1 - Math.abs(2 * l - 1)) * s;
246
- const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
247
- const m = l - c / 2;
248
- let r = 0, g = 0, b = 0;
249
- if (h >= 0 && h < 60) {
250
- r = c;
251
- g = x;
252
- b = 0;
253
- }
254
- else if (h >= 60 && h < 120) {
255
- r = x;
256
- g = c;
257
- b = 0;
258
- }
259
- else if (h >= 120 && h < 180) {
260
- r = 0;
261
- g = c;
262
- b = x;
263
- }
264
- else if (h >= 180 && h < 240) {
265
- r = 0;
266
- g = x;
267
- b = c;
268
- }
269
- else if (h >= 240 && h < 300) {
270
- r = x;
271
- g = 0;
272
- b = c;
273
- }
274
- else {
275
- r = c;
276
- g = 0;
277
- b = x;
278
- }
279
- r = Math.round((r + m) * 255);
280
- g = Math.round((g + m) * 255);
281
- b = Math.round((b + m) * 255);
282
- return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
283
- }
284
- export function adjustHexBrightness(hex, offsetPercent) {
285
- const normalizedHex = normalizeHex(hex);
286
- const p = Math.max(-100, Math.min(100, offsetPercent)) / 100;
287
- const r = parseInt(normalizedHex.slice(1, 3), 16);
288
- const g = parseInt(normalizedHex.slice(3, 5), 16);
289
- const b = parseInt(normalizedHex.slice(5, 7), 16);
290
- const adjustChannel = (channel) => {
291
- if (p > 0)
292
- return Math.min(255, Math.floor(channel + (255 - channel) * p));
293
- else if (p < 0)
294
- return Math.max(0, Math.floor(channel * (1 + p)));
295
- return channel;
296
- };
297
- const newR = adjustChannel(r);
298
- const newG = adjustChannel(g);
299
- const newB = adjustChannel(b);
300
- return `#${newR.toString(16).padStart(2, '0')}${newG.toString(16).padStart(2, '0')}${newB.toString(16).padStart(2, '0')}`;
301
- }
302
- export function rotateHue(hex, degrees) {
303
- const [h, s, l] = hexToHsl(hex);
304
- const newH = ((h + degrees) % 360 + 360) % 360;
305
- return hslToHex(newH, s, l);
306
- }
307
- export function lighten(color, amount) {
308
- var _a, _b, _c;
309
- const n = normalizeColor(color);
310
- const h = (_a = n.h) !== null && _a !== void 0 ? _a : 0;
311
- const s = (_b = n.s) !== null && _b !== void 0 ? _b : 0;
312
- const l = Math.min(100, ((_c = n.l) !== null && _c !== void 0 ? _c : 0) + amount);
313
- return hslToHex(h, s, l);
314
- }
315
- export function darken(color, amount) {
316
- var _a, _b, _c;
317
- const n = normalizeColor(color);
318
- const h = (_a = n.h) !== null && _a !== void 0 ? _a : 0;
319
- const s = (_b = n.s) !== null && _b !== void 0 ? _b : 0;
320
- const l = Math.max(0, ((_c = n.l) !== null && _c !== void 0 ? _c : 0) - amount);
321
- return hslToHex(h, s, l);
322
- }
323
- export function saturate(color, amount) {
324
- var _a, _b, _c;
325
- const n = normalizeColor(color);
326
- const h = (_a = n.h) !== null && _a !== void 0 ? _a : 0;
327
- const s = Math.min(100, ((_b = n.s) !== null && _b !== void 0 ? _b : 0) + amount);
328
- const l = (_c = n.l) !== null && _c !== void 0 ? _c : 0;
329
- return hslToHex(h, s, l);
330
- }
331
- export function desaturate(color, amount) {
332
- var _a, _b, _c;
333
- const n = normalizeColor(color);
334
- const h = (_a = n.h) !== null && _a !== void 0 ? _a : 0;
335
- const s = Math.max(0, ((_b = n.s) !== null && _b !== void 0 ? _b : 0) - amount);
336
- const l = (_c = n.l) !== null && _c !== void 0 ? _c : 0;
337
- return hslToHex(h, s, l);
338
- }
339
- export function setAlpha(color, alpha) {
340
- var _a, _b, _c;
341
- const n = normalizeColor(color);
342
- const r = (_a = n.r) !== null && _a !== void 0 ? _a : 0;
343
- const g = (_b = n.g) !== null && _b !== void 0 ? _b : 0;
344
- const b = (_c = n.b) !== null && _c !== void 0 ? _c : 0;
345
- const a = Math.max(0, Math.min(1, alpha));
346
- return `rgba(${r}, ${g}, ${b}, ${+a.toFixed(3)})`;
347
- }
348
- export function getAlpha(color) {
349
- var _a;
350
- const n = normalizeColor(color);
351
- return (_a = n.a) !== null && _a !== void 0 ? _a : 1;
352
- }
353
- export function invertColor(color) {
354
- var _a, _b, _c;
355
- const n = normalizeColor(color);
356
- const r = 255 - ((_a = n.r) !== null && _a !== void 0 ? _a : 0);
357
- const g = 255 - ((_b = n.g) !== null && _b !== void 0 ? _b : 0);
358
- const b = 255 - ((_c = n.b) !== null && _c !== void 0 ? _c : 0);
359
- return rgbToHex({ r, g, b });
360
- }
361
- export function grayscale(color) {
362
- var _a, _b, _c;
363
- const n = normalizeColor(color);
364
- const r = (_a = n.r) !== null && _a !== void 0 ? _a : 0;
365
- const g = (_b = n.g) !== null && _b !== void 0 ? _b : 0;
366
- const b = (_c = n.b) !== null && _c !== void 0 ? _c : 0;
367
- // Perceptual luminance weights (ITU-R BT.709)
368
- const gray = Math.round(0.2126 * r + 0.7152 * g + 0.0722 * b);
369
- return rgbToHex({ r: gray, g: gray, b: gray });
370
- }
371
- function clamp01(v) { return Math.max(0, Math.min(1, v)); }
372
- function toHex2(n) { return Math.round(n).toString(16).padStart(2, '0'); }
373
- export function rgbToHex({ r, g, b }) {
374
- return `#${toHex2(r)}${toHex2(g)}${toHex2(b)}`.toLowerCase();
375
- }
376
- export function rgbaToHex({ r, g, b, a }) {
377
- const aa = Math.round(clamp01(a) * 255);
378
- return `#${toHex2(r)}${toHex2(g)}${toHex2(b)}${toHex2(aa)}`.toLowerCase();
379
- }
380
- export function rgbToRgbaString({ r, g, b }, a) {
381
- return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${+a.toFixed(3)})`;
382
- }
383
- export function rgbaStringToRgba(str) {
384
- const s = str.trim().toLowerCase();
385
- const m = s.match(/rgba?\(([^)]+)\)/);
386
- if (!m)
387
- return null;
388
- const parts = m[1].split(/,\s*/).map(p => p.trim());
389
- if (parts.length < 3)
390
- return null;
391
- // Clamp channels to a valid byte and alpha to [0,1] — an out-of-range input
392
- // like "rgb(300, -20, 0)" or "rgba(0,0,0,2)" is invalid CSS, but previously
393
- // passed straight through unclamped, which could even corrupt downstream hex
394
- // output (a negative channel stringifies with a leading "-" via toString(16)).
395
- const parseChannel = (v) => {
396
- const n = v.endsWith('%') ? parseFloat(v) * 2.55 : parseFloat(v);
397
- return Math.round(Math.max(0, Math.min(255, n)));
398
- };
399
- const r = parseChannel(parts[0]);
400
- const g = parseChannel(parts[1]);
401
- const b = parseChannel(parts[2]);
402
- const a = parts[3] !== undefined ? Math.max(0, Math.min(1, parseFloat(parts[3]))) : 1;
403
- return { r, g, b, a };
404
- }
405
- export function rgbToHsl({ r, g, b }) {
406
- const rd = r / 255, gd = g / 255, bd = b / 255;
407
- const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
408
- let h = 0, s = 0;
409
- const l = (max + min) / 2;
410
- if (max !== min) {
411
- const d = max - min;
412
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
413
- switch (max) {
414
- case rd:
415
- h = (gd - bd) / d + (gd < bd ? 6 : 0);
416
- break;
417
- case gd:
418
- h = (bd - rd) / d + 2;
419
- break;
420
- case bd:
421
- h = (rd - gd) / d + 4;
422
- break;
423
- }
424
- h /= 6;
425
- }
426
- return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
427
- }
428
- export function hslToRgb(h, s, l) {
429
- h = ((h % 360) + 360) % 360;
430
- s = Math.max(0, Math.min(100, s)) / 100;
431
- l = Math.max(0, Math.min(100, l)) / 100;
432
- const c = (1 - Math.abs(2 * l - 1)) * s;
433
- const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
434
- const m = l - c / 2;
435
- let rd = 0, gd = 0, bd = 0;
436
- if (h >= 0 && h < 60) {
437
- rd = c;
438
- gd = x;
439
- bd = 0;
440
- }
441
- else if (h >= 60 && h < 120) {
442
- rd = x;
443
- gd = c;
444
- bd = 0;
445
- }
446
- else if (h >= 120 && h < 180) {
447
- rd = 0;
448
- gd = c;
449
- bd = x;
450
- }
451
- else if (h >= 180 && h < 240) {
452
- rd = 0;
453
- gd = x;
454
- bd = c;
455
- }
456
- else if (h >= 240 && h < 300) {
457
- rd = x;
458
- gd = 0;
459
- bd = c;
460
- }
461
- else {
462
- rd = c;
463
- gd = 0;
464
- bd = x;
465
- }
466
- return { r: Math.round((rd + m) * 255), g: Math.round((gd + m) * 255), b: Math.round((bd + m) * 255) };
467
- }
468
- export function rgbToHsv({ r, g, b }) {
469
- const rd = r / 255, gd = g / 255, bd = b / 255;
470
- const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
471
- const v = max;
472
- const d = max - min;
473
- const s = max === 0 ? 0 : d / max;
474
- let h = 0;
475
- if (d !== 0) {
476
- switch (max) {
477
- case rd:
478
- h = (gd - bd) / d + (gd < bd ? 6 : 0);
479
- break;
480
- case gd:
481
- h = (bd - rd) / d + 2;
482
- break;
483
- case bd:
484
- h = (rd - gd) / d + 4;
485
- break;
486
- }
487
- h /= 6;
488
- }
489
- return [Math.round(h * 360), Math.round(s * 100), Math.round(v * 100)];
490
- }
491
- export function hsvToRgb(h, s, v) {
492
- h = ((h % 360) + 360) % 360;
493
- s = Math.max(0, Math.min(100, s)) / 100;
494
- v = Math.max(0, Math.min(100, v)) / 100;
495
- const i = Math.floor(h / 60);
496
- const f = h / 60 - i;
497
- const p = v * (1 - s);
498
- const q = v * (1 - f * s);
499
- const t = v * (1 - (1 - f) * s);
500
- let rd = 0, gd = 0, bd = 0;
501
- switch (i) {
502
- case 0:
503
- rd = v;
504
- gd = t;
505
- bd = p;
506
- break;
507
- case 1:
508
- rd = q;
509
- gd = v;
510
- bd = p;
511
- break;
512
- case 2:
513
- rd = p;
514
- gd = v;
515
- bd = t;
516
- break;
517
- case 3:
518
- rd = p;
519
- gd = q;
520
- bd = v;
521
- break;
522
- case 4:
523
- rd = t;
524
- gd = p;
525
- bd = v;
526
- break;
527
- default:
528
- rd = v;
529
- gd = p;
530
- bd = q;
531
- break;
532
- }
533
- return { r: Math.round(rd * 255), g: Math.round(gd * 255), b: Math.round(bd * 255) };
534
- }
535
- export function hexToHsv(hex) {
536
- const [r, g, b] = hexToRgb(hex);
537
- return rgbToHsv({ r, g, b });
538
- }
539
- export function hsvToHex(h, s, v) {
540
- const { r, g, b } = hsvToRgb(h, s, v);
541
- return rgbToHex({ r, g, b });
542
- }
543
- export function hex8ToRgba(hex) {
544
- let h = hex.trim();
545
- if (!h.startsWith('#'))
546
- h = `#${h}`;
547
- if (h.length === 5) { // #rgba shorthand
548
- h = `#${h[1]}${h[1]}${h[2]}${h[2]}${h[3]}${h[3]}${h[4]}${h[4]}`;
549
- }
550
- if (h.length !== 9)
551
- return null;
552
- const r = parseInt(h.slice(1, 3), 16);
553
- const g = parseInt(h.slice(3, 5), 16);
554
- const b = parseInt(h.slice(5, 7), 16);
555
- const a = parseInt(h.slice(7, 9), 16) / 255;
556
- return { r, g, b, a };
557
- }
558
- export function rgbaToHex8({ r, g, b, a }) {
559
- return rgbaToHex({ r, g, b, a });
560
- }
561
- export function normalizeColor(input) {
562
- if (typeof input !== 'string') {
563
- if ('r' in input && 'g' in input && 'b' in input) {
564
- const { r, g, b } = input;
565
- const hex = rgbToHex({ r, g, b });
566
- const [h, s, l] = rgbToHsl({ r, g, b });
567
- const [hh, ss, vv] = rgbToHsv({ r, g, b });
568
- return { type: 'rgb', hex, r, g, b, a: 1, h, s, l, v: vv };
569
- }
570
- if ('h' in input && 's' in input && 'l' in input) {
571
- const { h, s, l } = input;
572
- const { r, g, b } = hslToRgb(h, s, l);
573
- const hex = rgbToHex({ r, g, b });
574
- const [hh, ss, vv] = rgbToHsv({ r, g, b });
575
- return { type: 'hsl', hex, r, g, b, a: 1, h, s, l, v: vv };
576
- }
577
- return { type: 'unknown' };
578
- }
579
- const str = input.trim();
580
- if (isCssVariable(str))
581
- return { type: 'css-var', raw: str };
582
- // 8-digit (#rrggbbaa) or 4-digit (#rgba) — check before isHexColor
583
- const hex8 = str.match(/^#([0-9a-f]{8}|[0-9a-f]{4})$/i);
584
- if (hex8) {
585
- const rgba = hex8ToRgba(str);
586
- if (rgba) {
587
- const { r, g, b, a } = rgba;
588
- const hex = rgbToHex({ r, g, b });
589
- const [h, s, l] = rgbToHsl({ r, g, b });
590
- const [hh, ss, vv] = rgbToHsv({ r, g, b });
591
- return { type: 'hex', hex, r, g, b, a, h, s, l, v: vv };
592
- }
593
- }
594
- if (isHexColor(str)) {
595
- const hex = normalizeHex(str);
596
- const [r, g, b] = hexToRgb(hex);
597
- const [h, s, l] = hexToHsl(hex);
598
- const [hh, ss, vv] = rgbToHsv({ r, g, b });
599
- return { type: 'hex', hex, r, g, b, a: 1, h, s, l, v: vv };
600
- }
601
- if (isRgbColor(str)) {
602
- const rgba = rgbaStringToRgba(str);
603
- if (rgba) {
604
- const { r, g, b, a } = rgba;
605
- const hex = rgbToHex({ r, g, b });
606
- const [h, s, l] = rgbToHsl({ r, g, b });
607
- const [hh, ss, vv] = rgbToHsv({ r, g, b });
608
- return { type: 'rgb', hex, r, g, b, a, h, s, l, v: vv };
609
- }
610
- }
611
- if (isHslColor(str)) {
612
- const m = str.match(/hsla?\(([^)]+)\)/i);
613
- if (m) {
614
- const parts = m[1].split(/,\s*/);
615
- const h = parseFloat(parts[0]);
616
- const s = parseFloat(parts[1]);
617
- const l = parseFloat(parts[2]);
618
- const a = parts[3] ? parseFloat(parts[3]) : 1;
619
- const { r, g, b } = hslToRgb(h, s, l);
620
- const hex = rgbToHex({ r, g, b });
621
- const [hh, ss, vv] = rgbToHsv({ r, g, b });
622
- return { type: 'hsl', hex, r, g, b, a, h, s, l, v: vv };
623
- }
624
- }
625
- if (str === 'transparent')
626
- return { type: 'named', hex: '#000000', r: 0, g: 0, b: 0, a: 0 };
627
- const lowerStr = str.toLowerCase();
628
- if (lowerStr in NAMED_COLORS) {
629
- const hex = NAMED_COLORS[lowerStr];
630
- const [r, g, b] = hexToRgb(hex);
631
- const [h, s, l] = hexToHsl(hex);
632
- const [, , vv] = rgbToHsv({ r, g, b });
633
- return { type: 'named', hex, r, g, b, a: 1, h, s, l, v: vv };
634
- }
635
- // oklch / oklcha
636
- if (isOklchColor(str)) {
637
- const parsed = parseOklchString(str);
638
- if (parsed) {
639
- const { r, g, b } = oklchToRgb({ L: parsed.L, C: parsed.C, H: parsed.H });
640
- const hex = rgbToHex({ r, g, b });
641
- const [h, s, l] = rgbToHsl({ r, g, b });
642
- const [, , vv] = rgbToHsv({ r, g, b });
643
- return { type: 'oklch', hex, r, g, b, a: parsed.alpha, h, s, l, v: vv };
644
- }
645
- }
646
- // color(display-p3 ...) / color(srgb ...)
647
- if (isColorFunction(str)) {
648
- const parsed = parseColorFn(str);
649
- if (parsed) {
650
- const { r: p3r, g: p3g, b: p3b, alpha } = parsed;
651
- // Convert from p3/srgb (0-1) to sRGB 0-255
652
- let r, g, b;
653
- if (parsed.space === 'display-p3') {
654
- const srgb = displayP3ToRgb({ r: p3r, g: p3g, b: p3b });
655
- r = srgb.r;
656
- g = srgb.g;
657
- b = srgb.b;
658
- }
659
- else if (parsed.space === 'srgb-linear') {
660
- // srgb-linear components are linear-light, not gamma-encoded — unlike
661
- // plain srgb below, they need the linear-to-sRGB transfer function
662
- // applied before scaling to a 0-255 byte. Without this, only the 0/1
663
- // extremes happened to come out correct; any mid-range value (e.g. 0.5)
664
- // converted as if it were already gamma-encoded, which is wrong.
665
- r = Math.round(linearChanToSrgb(p3r) * 255);
666
- g = Math.round(linearChanToSrgb(p3g) * 255);
667
- b = Math.round(linearChanToSrgb(p3b) * 255);
668
- }
669
- else {
670
- r = Math.round(Math.max(0, Math.min(255, p3r * 255)));
671
- g = Math.round(Math.max(0, Math.min(255, p3g * 255)));
672
- b = Math.round(Math.max(0, Math.min(255, p3b * 255)));
673
- }
674
- const hex = rgbToHex({ r, g, b });
675
- const [h, s, l] = rgbToHsl({ r, g, b });
676
- const [, , vv] = rgbToHsv({ r, g, b });
677
- return { type: 'color', hex, r, g, b, a: alpha, h, s, l, v: vv };
678
- }
679
- }
680
- return { type: 'unknown' };
681
- }
682
- export function complement(color) {
683
- var _a;
684
- return rotateHue((_a = normalizeColor(color).hex) !== null && _a !== void 0 ? _a : '#000000', 180);
685
- }
686
- export function triadic(color) {
687
- var _a;
688
- const hex = (_a = normalizeColor(color).hex) !== null && _a !== void 0 ? _a : '#000000';
689
- return [hex, rotateHue(hex, 120), rotateHue(hex, 240)];
690
- }
691
- export function analogous(color, angle = 30) {
692
- var _a;
693
- const hex = (_a = normalizeColor(color).hex) !== null && _a !== void 0 ? _a : '#000000';
694
- return [rotateHue(hex, -angle), hex, rotateHue(hex, angle)];
695
- }
696
- export function splitComplementary(color) {
697
- var _a;
698
- const hex = (_a = normalizeColor(color).hex) !== null && _a !== void 0 ? _a : '#000000';
699
- return [hex, rotateHue(hex, 150), rotateHue(hex, 210)];
700
- }
701
- export function tetradic(color) {
702
- var _a;
703
- const hex = (_a = normalizeColor(color).hex) !== null && _a !== void 0 ? _a : '#000000';
704
- return [hex, rotateHue(hex, 90), rotateHue(hex, 180), rotateHue(hex, 270)];
705
- }
706
- export function colorShades(color, steps = 9) {
707
- var _a, _b, _c;
708
- const n = normalizeColor(color);
709
- const h = (_a = n.h) !== null && _a !== void 0 ? _a : 0;
710
- const s = (_b = n.s) !== null && _b !== void 0 ? _b : 0;
711
- // steps - 1 === 0 below would otherwise divide by zero (NaN lightness, then
712
- // a literal "#NaNNaNNaN" hex string) — a single shade is just the color's
713
- // own lightness, same idea as interpolateColors() returning the midpoint
714
- // for a 2-color, 1-step request.
715
- if (steps <= 1)
716
- return steps === 1 ? [hslToHex(h, s, (_c = n.l) !== null && _c !== void 0 ? _c : 50)] : [];
717
- const result = [];
718
- for (let i = 0; i < steps; i++) {
719
- const l = Math.round(100 - (i / (steps - 1)) * 100);
720
- result.push(hslToHex(h, s, l));
721
- }
722
- return result;
723
- }
724
- export function monochromatic(color, steps = 5) {
725
- var _a, _b, _c;
726
- const n = normalizeColor(color);
727
- const h = (_a = n.h) !== null && _a !== void 0 ? _a : 0;
728
- const l = (_b = n.l) !== null && _b !== void 0 ? _b : 50;
729
- // Same steps === 1 divide-by-zero guard as colorShades() above.
730
- if (steps <= 1)
731
- return steps === 1 ? [hslToHex(h, (_c = n.s) !== null && _c !== void 0 ? _c : 0, l)] : [];
732
- const result = [];
733
- for (let i = 0; i < steps; i++) {
734
- const s = Math.round((i / (steps - 1)) * 100);
735
- result.push(hslToHex(h, s, l));
736
- }
737
- return result;
738
- }
739
- function _lerpHue(h1, h2, t, mode = 'shorter') {
740
- let d = h2 - h1;
741
- if (mode === 'shorter') {
742
- if (d > 180)
743
- d -= 360;
744
- else if (d < -180)
745
- d += 360;
746
- }
747
- else if (mode === 'longer') {
748
- if (d > 0 && d < 180)
749
- d -= 360;
750
- else if (d < 0 && d > -180)
751
- d += 360;
752
- }
753
- else if (mode === 'increasing') {
754
- if (d < 0)
755
- d += 360;
756
- }
757
- else if (mode === 'decreasing') {
758
- if (d > 0)
759
- d -= 360;
760
- }
761
- return h1 + d * t;
762
- }
763
- function _formatMixResult(r, g, b, a, out) {
764
- if (out === 'hex')
765
- return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
766
- if (out === 'rgba')
767
- return rgbToRgbaString({ r: Math.round(r), g: Math.round(g), b: Math.round(b) }, a);
768
- if (out === 'rgb')
769
- return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
770
- if (out === 'hsl') {
771
- const [hh, ss, ll] = rgbToHsl({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
772
- return `hsl(${hh}, ${ss}%, ${ll}%)`;
773
- }
774
- return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
775
- }
776
- export function mixColors(c1, c2, t, opts) {
777
- 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;
778
- const o1 = normalizeColor(c1);
779
- const o2 = normalizeColor(c2);
780
- t = Math.max(0, Math.min(1, t));
781
- const mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'rgb';
782
- const out = (opts === null || opts === void 0 ? void 0 : opts.format) || 'hex';
783
- const hueMode = (_a = opts === null || opts === void 0 ? void 0 : opts.hueInterpolation) !== null && _a !== void 0 ? _a : 'shorter';
784
- let r = 0, g = 0, b = 0, a = 1;
785
- if (mode === 'hsl') {
786
- 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];
787
- const s1 = (_f = o1.s) !== null && _f !== void 0 ? _f : 0;
788
- const l1 = (_g = o1.l) !== null && _g !== void 0 ? _g : 0;
789
- 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];
790
- const s2 = (_m = o2.s) !== null && _m !== void 0 ? _m : 0;
791
- const l2 = (_o = o2.l) !== null && _o !== void 0 ? _o : 0;
792
- const ih = _lerpHue(h1, h2, t, hueMode);
793
- const rgb = hslToRgb(ih, s1 + (s2 - s1) * t, l1 + (l2 - l1) * t);
794
- r = rgb.r;
795
- g = rgb.g;
796
- b = rgb.b;
797
- 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;
798
- }
799
- else if (mode === 'lab') {
800
- 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 });
801
- 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 });
802
- 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 });
803
- r = mixed.r;
804
- g = mixed.g;
805
- b = mixed.b;
806
- 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;
807
- }
808
- else if (mode === 'lch') {
809
- 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 });
810
- 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 });
811
- 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) });
812
- r = mixed.r;
813
- g = mixed.g;
814
- b = mixed.b;
815
- 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;
816
- }
817
- else if (mode === 'oklab') {
818
- 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 });
819
- 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 });
820
- 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 });
821
- r = mixed.r;
822
- g = mixed.g;
823
- b = mixed.b;
824
- 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;
825
- }
826
- else if (mode === 'oklch') {
827
- 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 });
828
- 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 });
829
- 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) });
830
- r = mixed.r;
831
- g = mixed.g;
832
- b = mixed.b;
833
- 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;
834
- }
835
- else {
836
- r = ((_28 = o1.r) !== null && _28 !== void 0 ? _28 : 0) * (1 - t) + ((_29 = o2.r) !== null && _29 !== void 0 ? _29 : 0) * t;
837
- g = ((_30 = o1.g) !== null && _30 !== void 0 ? _30 : 0) * (1 - t) + ((_31 = o2.g) !== null && _31 !== void 0 ? _31 : 0) * t;
838
- b = ((_32 = o1.b) !== null && _32 !== void 0 ? _32 : 0) * (1 - t) + ((_33 = o2.b) !== null && _33 !== void 0 ? _33 : 0) * t;
839
- a = ((_34 = o1.a) !== null && _34 !== void 0 ? _34 : 1) * (1 - t) + ((_35 = o2.a) !== null && _35 !== void 0 ? _35 : 1) * t;
840
- }
841
- return _formatMixResult(r, g, b, a, out);
842
- }
843
- export function relativeLuminance(color) {
844
- var _a, _b, _c;
845
- const n = normalizeColor(color);
846
- const r = ((_a = n.r) !== null && _a !== void 0 ? _a : 0) / 255;
847
- const g = ((_b = n.g) !== null && _b !== void 0 ? _b : 0) / 255;
848
- const b = ((_c = n.b) !== null && _c !== void 0 ? _c : 0) / 255;
849
- const srgbToLin = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
850
- const R = srgbToLin(r), G = srgbToLin(g), B = srgbToLin(b);
851
- return 0.2126 * R + 0.7152 * G + 0.0722 * B;
852
- }
853
- export function contrastRatio(a, b) {
854
- const L1 = relativeLuminance(a);
855
- const L2 = relativeLuminance(b);
856
- const light = Math.max(L1, L2);
857
- const dark = Math.min(L1, L2);
858
- return +((light + 0.05) / (dark + 0.05)).toFixed(2);
859
- }
860
- export function isDark(color, threshold = 0.5) {
861
- return relativeLuminance(color) < threshold;
862
- }
863
- export function isLight(color, threshold = 0.5) {
864
- return !isDark(color, threshold);
865
- }
866
- export function rgbToCmyk({ r, g, b }) {
867
- const rd = r / 255, gd = g / 255, bd = b / 255;
868
- const k = 1 - Math.max(rd, gd, bd);
869
- if (k === 1)
870
- return { c: 0, m: 0, y: 0, k: 1 };
871
- const c = (1 - rd - k) / (1 - k);
872
- const m = (1 - gd - k) / (1 - k);
873
- const y = (1 - bd - k) / (1 - k);
874
- return { c, m, y, k };
875
- }
876
- export function cmykToRgb({ c, m, y, k }) {
877
- const r = 255 * (1 - c) * (1 - k);
878
- const g = 255 * (1 - m) * (1 - k);
879
- const b = 255 * (1 - y) * (1 - k);
880
- return { r: Math.round(r), g: Math.round(g), b: Math.round(b) };
881
- }
882
- function srgbToLinear(v) {
883
- v = v / 255;
884
- return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
885
- }
886
- function linearToSrgb(v) {
887
- const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
888
- return Math.round(Math.max(0, Math.min(1, t)) * 255);
889
- }
890
- function rgbToXyz({ r, g, b }) {
891
- const R = srgbToLinear(r);
892
- const G = srgbToLinear(g);
893
- const B = srgbToLinear(b);
894
- const X = R * 0.4124564 + G * 0.3575761 + B * 0.1804375;
895
- const Y = R * 0.2126729 + G * 0.7151522 + B * 0.0721750;
896
- const Z = R * 0.0193339 + G * 0.1191920 + B * 0.9503041;
897
- return { X: X * 100, Y: Y * 100, Z: Z * 100 };
898
- }
899
- function xyzToRgb({ X, Y, Z }) {
900
- X = X / 100;
901
- Y = Y / 100;
902
- Z = Z / 100;
903
- let R = X * 3.2404542 + Y * -1.5371385 + Z * -0.4985314;
904
- let G = X * -0.9692660 + Y * 1.8760108 + Z * 0.0415560;
905
- let B = X * 0.0556434 + Y * -0.2040259 + Z * 1.0572252;
906
- R = linearToSrgb(R);
907
- G = linearToSrgb(G);
908
- B = linearToSrgb(B);
909
- return { r: R, g: G, b: B };
910
- }
911
- export function rgbToLab({ r, g, b }) {
912
- const { X, Y, Z } = rgbToXyz({ r, g, b });
913
- const refX = 95.047, refY = 100.0, refZ = 108.883;
914
- const x = X / refX, y = Y / refY, z = Z / refZ;
915
- const fx = x > 0.008856 ? Math.cbrt(x) : (7.787 * x) + 16 / 116;
916
- const fy = y > 0.008856 ? Math.cbrt(y) : (7.787 * y) + 16 / 116;
917
- const fz = z > 0.008856 ? Math.cbrt(z) : (7.787 * z) + 16 / 116;
918
- const L = (116 * fy) - 16;
919
- const a = 500 * (fx - fy);
920
- const b2 = 200 * (fy - fz);
921
- return { L, a, b: b2 };
922
- }
923
- export function labToRgb({ L, a, b }) {
924
- const refX = 95.047, refY = 100.0, refZ = 108.883;
925
- let fy = (L + 16) / 116;
926
- let fx = a / 500 + fy;
927
- let fz = fy - b / 200;
928
- const fx3 = Math.pow(fx, 3);
929
- const fz3 = Math.pow(fz, 3);
930
- const fy3 = Math.pow(fy, 3);
931
- const xr = fx3 > 0.008856 ? fx3 : (fx - 16 / 116) / 7.787;
932
- const yr = L > (903.3 * 0.008856) ? fy3 : L / 903.3;
933
- const zr = fz3 > 0.008856 ? fz3 : (fz - 16 / 116) / 7.787;
934
- const X = xr * refX, Y = yr * refY, Z = zr * refZ;
935
- return xyzToRgb({ X, Y, Z });
936
- }
937
- export function rgbToLch({ r, g, b }) {
938
- const { L, a, b: bb } = rgbToLab({ r, g, b });
939
- const C = Math.sqrt(a * a + bb * bb);
940
- let H = Math.atan2(bb, a) * (180 / Math.PI);
941
- if (H < 0)
942
- H += 360;
943
- return { L, C, H };
944
- }
945
- export function lchToRgb({ L, C, H }) {
946
- const a = Math.cos(H * Math.PI / 180) * C;
947
- const b = Math.sin(H * Math.PI / 180) * C;
948
- return labToRgb({ L, a, b });
949
- }
950
- export function wcagLevel(foreground, background) {
951
- const ratio = contrastRatio(foreground, background);
952
- if (ratio >= 7)
953
- return 'AAA';
954
- if (ratio >= 4.5)
955
- return 'AA';
956
- if (ratio >= 3)
957
- return 'AA-large';
958
- return 'fail';
959
- }
960
- export function bestTextColor(background) {
961
- const onBlack = contrastRatio(background, '#000000');
962
- const onWhite = contrastRatio(background, '#ffffff');
963
- return onBlack >= onWhite ? '#000000' : '#ffffff';
964
- }
965
- export function bestContrastColor(background, candidates) {
966
- let best = candidates[0];
967
- let bestRatio = -1;
968
- for (const c of candidates) {
969
- const ratio = contrastRatio(background, c);
970
- if (ratio > bestRatio) {
971
- bestRatio = ratio;
972
- best = c;
973
- }
974
- }
975
- return best;
976
- }
977
- // ─── HWB ─────────────────────────────────────────────────────────────────────
978
- export function rgbToHwb({ r, g, b }) {
979
- const rd = r / 255, gd = g / 255, bd = b / 255;
980
- const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
981
- const w = min;
982
- const bl = 1 - max;
983
- let h = 0;
984
- if (max !== min) {
985
- const d = max - min;
986
- switch (max) {
987
- case rd:
988
- h = (gd - bd) / d + (gd < bd ? 6 : 0);
989
- break;
990
- case gd:
991
- h = (bd - rd) / d + 2;
992
- break;
993
- case bd:
994
- h = (rd - gd) / d + 4;
995
- break;
996
- }
997
- h /= 6;
998
- }
999
- return [Math.round(h * 360), Math.round(w * 100), Math.round(bl * 100)];
1000
- }
1001
- export function hwbToRgb(H, W, B) {
1002
- H = ((H % 360) + 360) % 360;
1003
- const w = W / 100, b = B / 100;
1004
- if (w + b >= 1) {
1005
- const gray = Math.round((w / (w + b)) * 255);
1006
- return { r: gray, g: gray, b: gray };
1007
- }
1008
- const { r, g, b: rb } = hslToRgb(H, 100, 50);
1009
- const factor = 1 - w - b;
1010
- return {
1011
- r: Math.round(r / 255 * factor * 255 + w * 255),
1012
- g: Math.round(g / 255 * factor * 255 + w * 255),
1013
- b: Math.round(rb / 255 * factor * 255 + w * 255),
1014
- };
1015
- }
1016
- export function toHwbString(H, W, B, alpha) {
1017
- if (alpha !== undefined)
1018
- return `hwb(${H} ${W}% ${B}% / ${+alpha.toFixed(3)})`;
1019
- return `hwb(${H} ${W}% ${B}%)`;
1020
- }
1021
- export function parseHwbString(str) {
1022
- const m = str.trim().match(/hwb\(\s*([\d.+-]+)\s+([\d.]+)%\s+([\d.]+)%(?:\s*\/\s*([\d.]+))?\s*\)/i);
1023
- if (!m)
1024
- return null;
1025
- return { H: parseFloat(m[1]), W: parseFloat(m[2]), B: parseFloat(m[3]), alpha: m[4] !== undefined ? parseFloat(m[4]) : 1 };
1026
- }
1027
- // ─── OKLCH / OKLAB ───────────────────────────────────────────────────────────
1028
- function srgbToOkLinear(v) {
1029
- v = v / 255;
1030
- return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
1031
- }
1032
- function okLinearToSrgb(v) {
1033
- const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
1034
- return Math.round(Math.max(0, Math.min(1, t)) * 255);
1035
- }
1036
- export function rgbToOklab({ r, g, b }) {
1037
- const R = srgbToOkLinear(r), G = srgbToOkLinear(g), B = srgbToOkLinear(b);
1038
- const l = Math.cbrt(0.4122214708 * R + 0.5363325363 * G + 0.0514459929 * B);
1039
- const m = Math.cbrt(0.2119034982 * R + 0.6806995451 * G + 0.1073969566 * B);
1040
- const s = Math.cbrt(0.0883024619 * R + 0.2817188376 * G + 0.6299787005 * B);
1041
- return {
1042
- L: 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
1043
- a: 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
1044
- b: 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
1045
- };
1046
- }
1047
- export function oklabToRgb({ L, a, b }) {
1048
- const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3);
1049
- const m = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3);
1050
- const s = Math.pow(L - 0.0894841775 * a - 1.2914855480 * b, 3);
1051
- const R = okLinearToSrgb(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s);
1052
- const G = okLinearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s);
1053
- const B = okLinearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s);
1054
- return { r: R, g: G, b: B };
1055
- }
1056
- export function rgbToOklch({ r, g, b }) {
1057
- const { L, a, b: bb } = rgbToOklab({ r, g, b });
1058
- const C = Math.sqrt(a * a + bb * bb);
1059
- let H = Math.atan2(bb, a) * (180 / Math.PI);
1060
- if (H < 0)
1061
- H += 360;
1062
- return { L, C, H };
1063
- }
1064
- export function oklchToRgb({ L, C, H }) {
1065
- const a = Math.cos(H * Math.PI / 180) * C;
1066
- const b = Math.sin(H * Math.PI / 180) * C;
1067
- return oklabToRgb({ L, a, b });
1068
- }
1069
- // ─── Utilities ───────────────────────────────────────────────────────────────
1070
- export function toHslString(h, s, l, alpha) {
1071
- if (alpha !== undefined)
1072
- return `hsla(${h}, ${s}%, ${l}%, ${+alpha.toFixed(3)})`;
1073
- return `hsl(${h}, ${s}%, ${l}%)`;
1074
- }
1075
- export function colorDeltaE(c1, c2) {
1076
- var _a, _b, _c, _d, _e, _f;
1077
- const n1 = normalizeColor(c1);
1078
- const n2 = normalizeColor(c2);
1079
- const lab1 = rgbToLab({ r: (_a = n1.r) !== null && _a !== void 0 ? _a : 0, g: (_b = n1.g) !== null && _b !== void 0 ? _b : 0, b: (_c = n1.b) !== null && _c !== void 0 ? _c : 0 });
1080
- const lab2 = rgbToLab({ r: (_d = n2.r) !== null && _d !== void 0 ? _d : 0, g: (_e = n2.g) !== null && _e !== void 0 ? _e : 0, b: (_f = n2.b) !== null && _f !== void 0 ? _f : 0 });
1081
- // CIEDE2000
1082
- const deg = (rad) => rad * (180 / Math.PI);
1083
- const rad = (d) => d * (Math.PI / 180);
1084
- const { L: L1, a: a1, b: b1 } = lab1;
1085
- const { L: L2, a: a2, b: b2 } = lab2;
1086
- const dL = L2 - L1;
1087
- const Lm = (L1 + L2) / 2;
1088
- const C1 = Math.sqrt(a1 * a1 + b1 * b1);
1089
- const C2 = Math.sqrt(a2 * a2 + b2 * b2);
1090
- const Cm = (C1 + C2) / 2;
1091
- const Cm7 = Math.pow(Cm, 7);
1092
- const G = 0.5 * (1 - Math.sqrt(Cm7 / (Cm7 + Math.pow(25, 7))));
1093
- const a1p = a1 * (1 + G), a2p = a2 * (1 + G);
1094
- const C1p = Math.sqrt(a1p * a1p + b1 * b1);
1095
- const C2p = Math.sqrt(a2p * a2p + b2 * b2);
1096
- const dCp = C2p - C1p;
1097
- const Cmp = (C1p + C2p) / 2;
1098
- let h1p = deg(Math.atan2(b1, a1p));
1099
- if (h1p < 0)
1100
- h1p += 360;
1101
- let h2p = deg(Math.atan2(b2, a2p));
1102
- if (h2p < 0)
1103
- h2p += 360;
1104
- let dhp;
1105
- if (Math.abs(h1p - h2p) <= 180)
1106
- dhp = h2p - h1p;
1107
- else if (h2p <= h1p)
1108
- dhp = h2p - h1p + 360;
1109
- else
1110
- dhp = h2p - h1p - 360;
1111
- const dHp = 2 * Math.sqrt(C1p * C2p) * Math.sin(rad(dhp / 2));
1112
- let Hmp;
1113
- if (Math.abs(h1p - h2p) <= 180)
1114
- Hmp = (h1p + h2p) / 2;
1115
- else if (h1p + h2p < 360)
1116
- Hmp = (h1p + h2p + 360) / 2;
1117
- else
1118
- Hmp = (h1p + h2p - 360) / 2;
1119
- const T = 1
1120
- - 0.17 * Math.cos(rad(Hmp - 30))
1121
- + 0.24 * Math.cos(rad(2 * Hmp))
1122
- + 0.32 * Math.cos(rad(3 * Hmp + 6))
1123
- - 0.20 * Math.cos(rad(4 * Hmp - 63));
1124
- const SL = 1 + 0.015 * Math.pow(Lm - 50, 2) / Math.sqrt(20 + Math.pow(Lm - 50, 2));
1125
- const SC = 1 + 0.045 * Cmp;
1126
- const SH = 1 + 0.015 * Cmp * T;
1127
- const Cmp7 = Math.pow(Cmp, 7);
1128
- const RC = 2 * Math.sqrt(Cmp7 / (Cmp7 + Math.pow(25, 7)));
1129
- const dTheta = 30 * Math.exp(-Math.pow((Hmp - 275) / 25, 2));
1130
- const RT = -Math.sin(rad(2 * dTheta)) * RC;
1131
- return +Math.sqrt(Math.pow(dL / SL, 2) +
1132
- Math.pow(dCp / SC, 2) +
1133
- Math.pow(dHp / SH, 2) +
1134
- RT * (dCp / SC) * (dHp / SH)).toFixed(4);
1135
- }
1136
- export function randomColor(options) {
1137
- var _a, _b, _c;
1138
- const [hMin, hMax] = (_a = options === null || options === void 0 ? void 0 : options.hRange) !== null && _a !== void 0 ? _a : [0, 360];
1139
- const [sMin, sMax] = (_b = options === null || options === void 0 ? void 0 : options.sRange) !== null && _b !== void 0 ? _b : [40, 90];
1140
- const [lMin, lMax] = (_c = options === null || options === void 0 ? void 0 : options.lRange) !== null && _c !== void 0 ? _c : [30, 70];
1141
- const h = Math.floor(Math.random() * (hMax - hMin)) + hMin;
1142
- const s = Math.floor(Math.random() * (sMax - sMin)) + sMin;
1143
- const l = Math.floor(Math.random() * (lMax - lMin)) + lMin;
1144
- return hslToHex(h, s, l);
1145
- }
1146
- export function toNearestNamedColor(color) {
1147
- var _a, _b, _c;
1148
- const n = normalizeColor(color);
1149
- const 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;
1150
- let bestName = 'black';
1151
- let bestDist = Infinity;
1152
- for (const [name, hex] of Object.entries(NAMED_COLORS)) {
1153
- // Skip any non-hex entry (currently just a defensive guard — 'transparent'
1154
- // is the only such value left in the table, and it has no real RGB to
1155
- // compare against; hexToRgb() would otherwise silently fall back to
1156
- // normalizeHex()'s placeholder color for it).
1157
- if (hex[0] !== '#')
1158
- continue;
1159
- const [nr, ng, nb] = hexToRgb(hex);
1160
- const dist = Math.sqrt(Math.pow(r - nr, 2) + Math.pow(g - ng, 2) + Math.pow(b - nb, 2));
1161
- if (dist < bestDist) {
1162
- bestDist = dist;
1163
- bestName = name;
1164
- }
1165
- }
1166
- return bestName;
1167
- }
1168
- // ─── Formatting ───────────────────────────────────────────────────────────────
1169
- export function toOklchString(color, alpha) {
1170
- var _a, _b, _c;
1171
- const n = normalizeColor(color);
1172
- 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 });
1173
- const Lr = +L.toFixed(4), Cr = +C.toFixed(4), Hr = +H.toFixed(2);
1174
- if (alpha !== undefined)
1175
- return `oklch(${Lr} ${Cr} ${Hr} / ${+alpha.toFixed(3)})`;
1176
- return `oklch(${Lr} ${Cr} ${Hr})`;
1177
- }
1178
- export function toColorP3String(color, alpha) {
1179
- var _a, _b, _c;
1180
- const n = normalizeColor(color);
1181
- 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 });
1182
- const r = +p3.r.toFixed(4), g = +p3.g.toFixed(4), b = +p3.b.toFixed(4);
1183
- if (alpha !== undefined)
1184
- return `color(display-p3 ${r} ${g} ${b} / ${+alpha.toFixed(3)})`;
1185
- return `color(display-p3 ${r} ${g} ${b})`;
1186
- }
1187
- // ─── Section 2: Interpolation ─────────────────────────────────────────────────
1188
- export function interpolateColors(color1, color2, steps, options) {
1189
- if (steps < 2)
1190
- return steps === 1 ? [mixColors(color1, color2, 0.5, options)] : [];
1191
- const result = [];
1192
- for (let i = 0; i < steps; i++) {
1193
- result.push(mixColors(color1, color2, i / (steps - 1), options));
1194
- }
1195
- return result;
1196
- }
1197
- export function createColorScale(anchors, steps, options) {
1198
- const normalized = anchors.map((a, i, arr) => {
1199
- var _a;
1200
- return ({
1201
- color: typeof a === 'string' ? a : a.color,
1202
- 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)),
1203
- });
1204
- });
1205
- normalized.sort((a, b) => a.position - b.position);
1206
- const result = [];
1207
- for (let i = 0; i < steps; i++) {
1208
- const t = steps === 1 ? 0 : i / (steps - 1);
1209
- let lo = normalized[0], hi = normalized[normalized.length - 1];
1210
- for (let j = 0; j < normalized.length - 1; j++) {
1211
- if (t >= normalized[j].position && t <= normalized[j + 1].position) {
1212
- lo = normalized[j];
1213
- hi = normalized[j + 1];
1214
- break;
1215
- }
1216
- }
1217
- const span = hi.position - lo.position;
1218
- const localT = span === 0 ? 0 : (t - lo.position) / span;
1219
- result.push(mixColors(lo.color, hi.color, localT, options));
1220
- }
1221
- return result;
1222
- }
1223
- export function midpointColor(color1, color2, options) {
1224
- var _a;
1225
- return mixColors(color1, color2, 0.5, { mode: (_a = options === null || options === void 0 ? void 0 : options.space) !== null && _a !== void 0 ? _a : 'oklab' });
1226
- }
1227
- // ─── Section 3: Tints / Shades / Tones ───────────────────────────────────────
1228
- export function tints(color, steps = 5) {
1229
- return interpolateColors(color, '#ffffff', steps, { space: 'oklab' });
1230
- }
1231
- export function shades(color, steps = 5) {
1232
- return interpolateColors(color, '#000000', steps, { space: 'oklab' });
1233
- }
1234
- export function tones(color, steps = 5, gray = '#808080') {
1235
- return interpolateColors(color, gray, steps, { space: 'oklab' });
1236
- }
1237
- // ─── Section 4: Color blindness simulation ────────────────────────────────────
1238
- function _simulateCB(color, matrix) {
1239
- var _a, _b, _c;
1240
- const n = normalizeColor(color);
1241
- // linearize
1242
- const lin = (v) => { const c = v / 255; return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); };
1243
- 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); };
1244
- 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);
1245
- const nr = enc(matrix[0][0] * r + matrix[0][1] * g + matrix[0][2] * b);
1246
- const ng = enc(matrix[1][0] * r + matrix[1][1] * g + matrix[1][2] * b);
1247
- const nb = enc(matrix[2][0] * r + matrix[2][1] * g + matrix[2][2] * b);
1248
- return rgbToHex({ r: nr, g: ng, b: nb });
1249
- }
1250
- const _PROTANOPIA_M = [[0.56667, 0.43333, 0.00000], [0.55833, 0.44167, 0.00000], [0.00000, 0.24167, 0.75833]];
1251
- const _DEUTERANOPIA_M = [[0.62500, 0.37500, 0.00000], [0.70000, 0.30000, 0.00000], [0.00000, 0.30000, 0.70000]];
1252
- const _TRITANOPIA_M = [[0.95000, 0.05000, 0.00000], [0.00000, 0.43333, 0.56667], [0.00000, 0.47500, 0.52500]];
1253
- export function simulateProtanopia(color) { return _simulateCB(color, _PROTANOPIA_M); }
1254
- export function simulateDeuteranopia(color) { return _simulateCB(color, _DEUTERANOPIA_M); }
1255
- export function simulateTritanopia(color) { return _simulateCB(color, _TRITANOPIA_M); }
1256
- export function simulateColorBlindness(color, type) {
1257
- if (type === 'protanopia')
1258
- return simulateProtanopia(color);
1259
- if (type === 'deuteranopia')
1260
- return simulateDeuteranopia(color);
1261
- return simulateTritanopia(color);
1262
- }
1263
- export function isReadableOnBackground(textColor, background, options) {
1264
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1265
- const level = (_a = options === null || options === void 0 ? void 0 : options.level) !== null && _a !== void 0 ? _a : 'AA';
1266
- const large = (_b = options === null || options === void 0 ? void 0 : options.largeText) !== null && _b !== void 0 ? _b : false;
1267
- const minRequired = level === 'AAA' ? (large ? 4.5 : 7) : (large ? 3 : 4.5);
1268
- // The specific effective background color that minRatio was actually
1269
- // computed against — wcagLevel below is derived from this same color
1270
- // (never a hardcoded '#ffffff') so it can't disagree with minRatio/readable.
1271
- let minRatio;
1272
- let effectiveBackground;
1273
- if (typeof background === 'string') {
1274
- effectiveBackground = background;
1275
- minRatio = contrastRatio(textColor, background);
1276
- }
1277
- else if (background.type === 'semi-transparent') {
1278
- const underlay = (_c = background.underlay) !== null && _c !== void 0 ? _c : '#ffffff';
1279
- const fg = normalizeColor(background.color);
1280
- const bg = normalizeColor(underlay);
1281
- const alpha = (_d = fg.a) !== null && _d !== void 0 ? _d : 1;
1282
- 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));
1283
- 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));
1284
- 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));
1285
- effectiveBackground = rgbToHex({ r: cr, g: cg, b: cb });
1286
- minRatio = contrastRatio(textColor, effectiveBackground);
1287
- }
1288
- else {
1289
- // The worst-case stop (lowest ratio) drives both minRatio and wcagLevel.
1290
- let worstStop = background.stops[0];
1291
- let worstRatio = Infinity;
1292
- for (const stop of background.stops) {
1293
- const ratio = contrastRatio(textColor, stop);
1294
- if (ratio < worstRatio) {
1295
- worstRatio = ratio;
1296
- worstStop = stop;
1297
- }
1298
- }
1299
- effectiveBackground = worstStop;
1300
- minRatio = worstRatio;
1301
- }
1302
- const wLevel = wcagLevel(textColor, effectiveBackground);
1303
- return { readable: minRatio >= minRequired, minContrastRatio: +minRatio.toFixed(2), wcagLevel: wLevel };
1304
- }
1305
- export function bestContrastPalette(background, palettes, options) {
1306
- let bestIdx = 0;
1307
- let bestScore = -1;
1308
- let bestMin = 0, bestAvg = 0;
1309
- palettes.forEach((palette, idx) => {
1310
- var _a;
1311
- const ratios = palette.map(c => contrastRatio(background, c));
1312
- const weights = (_a = options === null || options === void 0 ? void 0 : options.weights) !== null && _a !== void 0 ? _a : ratios.map(() => 1);
1313
- const totalW = weights.reduce((s, w) => s + w, 0);
1314
- const avg = ratios.reduce((s, r, i) => { var _a; return s + r * ((_a = weights[i]) !== null && _a !== void 0 ? _a : 1); }, 0) / totalW;
1315
- const min = Math.min(...ratios);
1316
- // Score: weighted avg with heavy penalty on minimum
1317
- const score = avg * 0.4 + min * 0.6;
1318
- if (score > bestScore) {
1319
- bestScore = score;
1320
- bestIdx = idx;
1321
- bestMin = min;
1322
- bestAvg = avg;
1323
- }
1324
- });
1325
- return {
1326
- paletteIndex: bestIdx,
1327
- palette: palettes[bestIdx],
1328
- minContrastRatio: +bestMin.toFixed(2),
1329
- avgContrastRatio: +bestAvg.toFixed(2),
1330
- };
1331
- }
1332
- // ─── Section 5.1: Caching ────────────────────────────────────────────────────
1333
- let _cacheEnabled = true;
1334
- const _normalizeCache = new Map();
1335
- let _cacheHits = 0;
1336
- export function clearColorCache() { _normalizeCache.clear(); _cacheHits = 0; }
1337
- export function getCacheStats() { return { size: _normalizeCache.size, hits: _cacheHits }; }
1338
- export function enableCache() { _cacheEnabled = true; }
1339
- export function disableCache() { _cacheEnabled = false; }
1340
- // Cached normalizer wrapper — use this in performance-sensitive contexts
1341
- export function normalizeColorCached(input) {
1342
- if (!_cacheEnabled)
1343
- return normalizeColor(input);
1344
- const cached = _normalizeCache.get(input);
1345
- if (cached) {
1346
- _cacheHits++;
1347
- return cached;
1348
- }
1349
- const result = normalizeColor(input);
1350
- _normalizeCache.set(input, result);
1351
- return result;
1352
- }
1353
- // ─── Section 5.2: Generator functions ────────────────────────────────────────
1354
- export function* generateGradientColors(start, end, steps, options) {
1355
- for (let i = 0; i < steps; i++) {
1356
- yield mixColors(start, end, steps === 1 ? 0 : i / (steps - 1), options);
1357
- }
1358
- }
1359
- export function* generateTints(color, steps, options) {
1360
- for (let i = 0; i < steps; i++) {
1361
- yield mixColors(color, '#ffffff', steps === 1 ? 0 : i / (steps - 1), { mode: 'oklab', ...options });
1362
- }
1363
- }
1364
- export function* generateShades(color, steps, options) {
1365
- for (let i = 0; i < steps; i++) {
1366
- yield mixColors(color, '#000000', steps === 1 ? 0 : i / (steps - 1), { mode: 'oklab', ...options });
1367
- }
1368
- }
1
+ export * from './parse.js';
2
+ export * from './convert/index.js';
3
+ export * from './manipulate.js';
4
+ export * from './palette.js';
5
+ export * from './a11y.js';
6
+ export * from './cache.js';