vue-multiple-themes 5.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/color-B_YYiD1M.cjs +243 -0
- package/dist/color-B_YYiD1M.cjs.map +1 -0
- package/dist/color-Dvj1cP5e.js +244 -0
- package/dist/color-Dvj1cP5e.js.map +1 -0
- package/dist/tailwind-plugin.cjs +79 -43
- package/dist/tailwind-plugin.cjs.map +1 -1
- package/dist/tailwind-plugin.d.ts +74 -15
- package/dist/tailwind-plugin.js +79 -43
- package/dist/tailwind-plugin.js.map +1 -1
- package/dist/tailwind-v4-plugin.cjs +146 -0
- package/dist/tailwind-v4-plugin.cjs.map +1 -0
- package/dist/tailwind-v4-plugin.d.ts +112 -0
- package/dist/tailwind-v4-plugin.js +146 -0
- package/dist/tailwind-v4-plugin.js.map +1 -0
- package/dist/vue-multiple-themes.cjs +476 -428
- package/dist/vue-multiple-themes.cjs.map +1 -1
- package/dist/vue-multiple-themes.css +6 -6
- package/dist/vue-multiple-themes.d.ts +266 -87
- package/dist/vue-multiple-themes.js +485 -436
- package/dist/vue-multiple-themes.js.map +1 -1
- package/package.json +17 -15
- package/readme.md +252 -54
- package/src/components/VmtIcon.vue +36 -50
- package/src/components/VmtThemePicker.vue +190 -0
- package/src/components/VueMultipleThemes.vue +33 -18
- package/src/composables/createBrandContext.ts +95 -0
- package/src/composables/useTheme.ts +148 -38
- package/src/icons/index.ts +35 -29
- package/src/index.ts +15 -6
- package/src/plugin.ts +5 -3
- package/src/tailwind-plugin.ts +158 -76
- package/src/tailwind-v4-plugin.ts +216 -0
- package/src/themes/presets.ts +0 -7
- package/src/types.ts +74 -16
- package/src/utils/color.ts +80 -0
- package/src/utils/css-injector.ts +25 -9
- package/src/utils/generate-theme.ts +5 -6
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
function hexToRgb(hex) {
|
|
3
|
+
const h = hex.replace("#", "");
|
|
4
|
+
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
5
|
+
const n = Number.parseInt(full.slice(0, 6), 16);
|
|
6
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
7
|
+
}
|
|
8
|
+
function rgbToHex(r, g, b) {
|
|
9
|
+
return "#" + [r, g, b].map(
|
|
10
|
+
(v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0")
|
|
11
|
+
).join("");
|
|
12
|
+
}
|
|
13
|
+
function rgbToHsl(r, g, b) {
|
|
14
|
+
const rn = r / 255;
|
|
15
|
+
const gn = g / 255;
|
|
16
|
+
const bn = b / 255;
|
|
17
|
+
const max = Math.max(rn, gn, bn);
|
|
18
|
+
const min = Math.min(rn, gn, bn);
|
|
19
|
+
const l = (max + min) / 2;
|
|
20
|
+
if (max === min) return [0, 0, Math.round(l * 100)];
|
|
21
|
+
const d = max - min;
|
|
22
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
23
|
+
let h = 0;
|
|
24
|
+
if (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;
|
|
25
|
+
else if (max === gn) h = ((bn - rn) / d + 2) / 6;
|
|
26
|
+
else h = ((rn - gn) / d + 4) / 6;
|
|
27
|
+
return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
|
|
28
|
+
}
|
|
29
|
+
function hslToRgb(h, s, l) {
|
|
30
|
+
const hn = h / 360;
|
|
31
|
+
const sn = s / 100;
|
|
32
|
+
const ln = l / 100;
|
|
33
|
+
if (sn === 0) {
|
|
34
|
+
const v = Math.round(ln * 255);
|
|
35
|
+
return [v, v, v];
|
|
36
|
+
}
|
|
37
|
+
const q = ln < 0.5 ? ln * (1 + sn) : ln + sn - ln * sn;
|
|
38
|
+
const p = 2 * ln - q;
|
|
39
|
+
const hue2rgb = (t) => {
|
|
40
|
+
if (t < 0) t += 1;
|
|
41
|
+
if (t > 1) t -= 1;
|
|
42
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
43
|
+
if (t < 1 / 2) return q;
|
|
44
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
45
|
+
return p;
|
|
46
|
+
};
|
|
47
|
+
return [
|
|
48
|
+
Math.round(hue2rgb(hn + 1 / 3) * 255),
|
|
49
|
+
Math.round(hue2rgb(hn) * 255),
|
|
50
|
+
Math.round(hue2rgb(hn - 1 / 3) * 255)
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
function hexToHsl(hex) {
|
|
54
|
+
return rgbToHsl(...hexToRgb(hex));
|
|
55
|
+
}
|
|
56
|
+
function hslToHex(h, s, l) {
|
|
57
|
+
return rgbToHex(...hslToRgb(h, s, l));
|
|
58
|
+
}
|
|
59
|
+
function parseColor(color) {
|
|
60
|
+
const c = color.trim();
|
|
61
|
+
if (c.startsWith("#")) return hexToHsl(c);
|
|
62
|
+
const rgbMatch = c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
63
|
+
if (rgbMatch) {
|
|
64
|
+
return rgbToHsl(
|
|
65
|
+
Number.parseInt(rgbMatch[1]),
|
|
66
|
+
Number.parseInt(rgbMatch[2]),
|
|
67
|
+
Number.parseInt(rgbMatch[3])
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const hslMatch = c.match(/hsla?\((\d+),\s*(\d+)%?,\s*(\d+)%?/);
|
|
71
|
+
if (hslMatch) {
|
|
72
|
+
return [
|
|
73
|
+
Number.parseInt(hslMatch[1]),
|
|
74
|
+
Number.parseInt(hslMatch[2]),
|
|
75
|
+
Number.parseInt(hslMatch[3])
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
return [220, 90, 56];
|
|
79
|
+
}
|
|
80
|
+
function lighten(hex, amount) {
|
|
81
|
+
const [h, s, l] = hexToHsl(hex);
|
|
82
|
+
return hslToHex(h, s, Math.min(100, l + amount));
|
|
83
|
+
}
|
|
84
|
+
function darken(hex, amount) {
|
|
85
|
+
const [h, s, l] = hexToHsl(hex);
|
|
86
|
+
return hslToHex(h, s, Math.max(0, l - amount));
|
|
87
|
+
}
|
|
88
|
+
function saturate(hex, amount) {
|
|
89
|
+
const [h, s, l] = hexToHsl(hex);
|
|
90
|
+
return hslToHex(h, Math.min(100, Math.max(0, s + amount)), l);
|
|
91
|
+
}
|
|
92
|
+
function rotateHue(hex, degrees) {
|
|
93
|
+
const [h, s, l] = hexToHsl(hex);
|
|
94
|
+
return hslToHex((h + degrees + 360) % 360, s, l);
|
|
95
|
+
}
|
|
96
|
+
function mix(hexA, hexB, weight = 0.5) {
|
|
97
|
+
const [r1, g1, b1] = hexToRgb(hexA);
|
|
98
|
+
const [r2, g2, b2] = hexToRgb(hexB);
|
|
99
|
+
return rgbToHex(
|
|
100
|
+
Math.round(r1 + (r2 - r1) * weight),
|
|
101
|
+
Math.round(g1 + (g2 - g1) * weight),
|
|
102
|
+
Math.round(b1 + (b2 - b1) * weight)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
function withAlpha(hex, alpha) {
|
|
106
|
+
const [r, g, b] = hexToRgb(hex);
|
|
107
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
108
|
+
}
|
|
109
|
+
function luminance(hex) {
|
|
110
|
+
const [r, g, b] = hexToRgb(hex).map((v) => {
|
|
111
|
+
const c = v / 255;
|
|
112
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
113
|
+
});
|
|
114
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
115
|
+
}
|
|
116
|
+
function contrastRatio(hexA, hexB) {
|
|
117
|
+
const la = luminance(hexA);
|
|
118
|
+
const lb = luminance(hexB);
|
|
119
|
+
const lighter = Math.max(la, lb);
|
|
120
|
+
const darker = Math.min(la, lb);
|
|
121
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
122
|
+
}
|
|
123
|
+
function autoContrast(bg, onLight = "#ffffff", onDark = "#000000") {
|
|
124
|
+
const cl = contrastRatio(bg, onLight);
|
|
125
|
+
const cd = contrastRatio(bg, onDark);
|
|
126
|
+
return cl >= cd ? onLight : onDark;
|
|
127
|
+
}
|
|
128
|
+
function ensureContrast(text, bg, minRatio = 4.5) {
|
|
129
|
+
let candidate = text;
|
|
130
|
+
const [h, s] = hexToHsl(candidate);
|
|
131
|
+
const bgLum = luminance(bg);
|
|
132
|
+
const darkBg = bgLum < 0.5;
|
|
133
|
+
let l = hexToHsl(candidate)[2];
|
|
134
|
+
for (let i = 0; i < 20; i++) {
|
|
135
|
+
if (contrastRatio(candidate, bg) >= minRatio) break;
|
|
136
|
+
l = darkBg ? Math.min(100, l + 5) : Math.max(0, l - 5);
|
|
137
|
+
candidate = hslToHex(h, s, l);
|
|
138
|
+
}
|
|
139
|
+
return candidate;
|
|
140
|
+
}
|
|
141
|
+
function generateColorScale(hex) {
|
|
142
|
+
const [h, s] = hexToHsl(hex);
|
|
143
|
+
const stops = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
144
|
+
const lightnesses = [97, 93, 86, 76, 64, 54, 44, 35, 25, 15, 8];
|
|
145
|
+
const result = {};
|
|
146
|
+
stops.forEach((stop, i) => {
|
|
147
|
+
result[stop] = hslToHex(h, Math.min(s + 5, 100), lightnesses[i]);
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
function complementary(hex) {
|
|
152
|
+
return rotateHue(hex, 180);
|
|
153
|
+
}
|
|
154
|
+
function splitComplementary(hex) {
|
|
155
|
+
return [hex, rotateHue(hex, 150), rotateHue(hex, 210)];
|
|
156
|
+
}
|
|
157
|
+
function triadic(hex) {
|
|
158
|
+
return [hex, rotateHue(hex, 120), rotateHue(hex, 240)];
|
|
159
|
+
}
|
|
160
|
+
function analogous(hex, angle = 30) {
|
|
161
|
+
return [hex, rotateHue(hex, angle), rotateHue(hex, -angle)];
|
|
162
|
+
}
|
|
163
|
+
const NAMED_COLORS = {
|
|
164
|
+
black: "#000000",
|
|
165
|
+
white: "#ffffff",
|
|
166
|
+
red: "#ff0000",
|
|
167
|
+
green: "#008000",
|
|
168
|
+
blue: "#0000ff",
|
|
169
|
+
yellow: "#ffff00",
|
|
170
|
+
cyan: "#00ffff",
|
|
171
|
+
magenta: "#ff00ff",
|
|
172
|
+
orange: "#ffa500",
|
|
173
|
+
purple: "#800080",
|
|
174
|
+
pink: "#ffc0cb",
|
|
175
|
+
gray: "#808080",
|
|
176
|
+
grey: "#808080",
|
|
177
|
+
silver: "#c0c0c0",
|
|
178
|
+
maroon: "#800000",
|
|
179
|
+
olive: "#808000",
|
|
180
|
+
lime: "#00ff00",
|
|
181
|
+
aqua: "#00ffff",
|
|
182
|
+
teal: "#008080",
|
|
183
|
+
navy: "#000080",
|
|
184
|
+
fuchsia: "#ff00ff",
|
|
185
|
+
transparent: "#00000000"
|
|
186
|
+
};
|
|
187
|
+
function normalizeToRgbChannels(color) {
|
|
188
|
+
const c = color.trim();
|
|
189
|
+
if (/^\d{1,3}\s+\d{1,3}\s+\d{1,3}$/.test(c)) {
|
|
190
|
+
return c;
|
|
191
|
+
}
|
|
192
|
+
if (c.startsWith("#")) {
|
|
193
|
+
const [r, g, b] = hexToRgb(c);
|
|
194
|
+
return `${r} ${g} ${b}`;
|
|
195
|
+
}
|
|
196
|
+
const rgbMatch = c.match(/rgba?\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)/);
|
|
197
|
+
if (rgbMatch) {
|
|
198
|
+
return `${rgbMatch[1]} ${rgbMatch[2]} ${rgbMatch[3]}`;
|
|
199
|
+
}
|
|
200
|
+
const hslMatch = c.match(/hsla?\(\s*(\d+)\s*[,\s]\s*(\d+)%?\s*[,\s]\s*(\d+)%?/);
|
|
201
|
+
if (hslMatch) {
|
|
202
|
+
const [r, g, b] = hslToRgb(
|
|
203
|
+
Number.parseInt(hslMatch[1]),
|
|
204
|
+
Number.parseInt(hslMatch[2]),
|
|
205
|
+
Number.parseInt(hslMatch[3])
|
|
206
|
+
);
|
|
207
|
+
return `${r} ${g} ${b}`;
|
|
208
|
+
}
|
|
209
|
+
const named = NAMED_COLORS[c.toLowerCase()];
|
|
210
|
+
if (named) {
|
|
211
|
+
const [r, g, b] = hexToRgb(named);
|
|
212
|
+
return `${r} ${g} ${b}`;
|
|
213
|
+
}
|
|
214
|
+
if (/^[0-9a-fA-F]{3,8}$/.test(c)) {
|
|
215
|
+
const [r, g, b] = hexToRgb(`#${c}`);
|
|
216
|
+
return `${r} ${g} ${b}`;
|
|
217
|
+
}
|
|
218
|
+
return c;
|
|
219
|
+
}
|
|
220
|
+
exports.analogous = analogous;
|
|
221
|
+
exports.autoContrast = autoContrast;
|
|
222
|
+
exports.complementary = complementary;
|
|
223
|
+
exports.contrastRatio = contrastRatio;
|
|
224
|
+
exports.darken = darken;
|
|
225
|
+
exports.ensureContrast = ensureContrast;
|
|
226
|
+
exports.generateColorScale = generateColorScale;
|
|
227
|
+
exports.hexToHsl = hexToHsl;
|
|
228
|
+
exports.hexToRgb = hexToRgb;
|
|
229
|
+
exports.hslToHex = hslToHex;
|
|
230
|
+
exports.hslToRgb = hslToRgb;
|
|
231
|
+
exports.lighten = lighten;
|
|
232
|
+
exports.luminance = luminance;
|
|
233
|
+
exports.mix = mix;
|
|
234
|
+
exports.normalizeToRgbChannels = normalizeToRgbChannels;
|
|
235
|
+
exports.parseColor = parseColor;
|
|
236
|
+
exports.rgbToHex = rgbToHex;
|
|
237
|
+
exports.rgbToHsl = rgbToHsl;
|
|
238
|
+
exports.rotateHue = rotateHue;
|
|
239
|
+
exports.saturate = saturate;
|
|
240
|
+
exports.splitComplementary = splitComplementary;
|
|
241
|
+
exports.triadic = triadic;
|
|
242
|
+
exports.withAlpha = withAlpha;
|
|
243
|
+
//# sourceMappingURL=color-B_YYiD1M.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-B_YYiD1M.cjs","sources":["../src/utils/color.ts"],"sourcesContent":["/**\n * color.ts — Pure color manipulation utilities.\n *\n * All functions are SSR-safe (no DOM access).\n * Everything operates on standard color formats:\n * - Hex: '#rgb' | '#rrggbb' | '#rrggbbaa'\n * - HSL: [h, s, l] where h∈[0,360), s/l∈[0,100]\n * - RGB: [r, g, b] where each channel ∈[0,255]\n */\n\nexport type RGB = [number, number, number];\nexport type RGBA = [number, number, number, number];\nexport type HSL = [number, number, number];\n\n// ─── Parsing ──────────────────────────────────────────────────────────────────\n\nexport function hexToRgb(hex: string): RGB {\n\tconst h = hex.replace(\"#\", \"\");\n\tconst full =\n\t\th.length === 3\n\t\t\t? h\n\t\t\t\t\t.split(\"\")\n\t\t\t\t\t.map((c) => c + c)\n\t\t\t\t\t.join(\"\")\n\t\t\t: h;\n\tconst n = Number.parseInt(full.slice(0, 6), 16);\n\treturn [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];\n}\n\nexport function rgbToHex(r: number, g: number, b: number): string {\n\treturn (\n\t\t\"#\" +\n\t\t[r, g, b]\n\t\t\t.map((v) =>\n\t\t\t\tMath.round(Math.max(0, Math.min(255, v)))\n\t\t\t\t\t.toString(16)\n\t\t\t\t\t.padStart(2, \"0\"),\n\t\t\t)\n\t\t\t.join(\"\")\n\t);\n}\n\nexport function rgbToHsl(r: number, g: number, b: number): HSL {\n\tconst rn = r / 255;\n\tconst gn = g / 255;\n\tconst bn = b / 255;\n\tconst max = Math.max(rn, gn, bn);\n\tconst min = Math.min(rn, gn, bn);\n\tconst l = (max + min) / 2;\n\tif (max === min) return [0, 0, Math.round(l * 100)];\n\tconst d = max - min;\n\tconst s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n\tlet h = 0;\n\tif (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;\n\telse if (max === gn) h = ((bn - rn) / d + 2) / 6;\n\telse h = ((rn - gn) / d + 4) / 6;\n\treturn [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];\n}\n\nexport function hslToRgb(h: number, s: number, l: number): RGB {\n\tconst hn = h / 360;\n\tconst sn = s / 100;\n\tconst ln = l / 100;\n\tif (sn === 0) {\n\t\tconst v = Math.round(ln * 255);\n\t\treturn [v, v, v];\n\t}\n\tconst q = ln < 0.5 ? ln * (1 + sn) : ln + sn - ln * sn;\n\tconst p = 2 * ln - q;\n\tconst hue2rgb = (t: number) => {\n\t\tif (t < 0) t += 1;\n\t\tif (t > 1) t -= 1;\n\t\tif (t < 1 / 6) return p + (q - p) * 6 * t;\n\t\tif (t < 1 / 2) return q;\n\t\tif (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n\t\treturn p;\n\t};\n\treturn [\n\t\tMath.round(hue2rgb(hn + 1 / 3) * 255),\n\t\tMath.round(hue2rgb(hn) * 255),\n\t\tMath.round(hue2rgb(hn - 1 / 3) * 255),\n\t];\n}\n\nexport function hexToHsl(hex: string): HSL {\n\treturn rgbToHsl(...hexToRgb(hex));\n}\n\nexport function hslToHex(h: number, s: number, l: number): string {\n\treturn rgbToHex(...hslToRgb(h, s, l));\n}\n\n/** Parse any CSS color string (hex, rgb(), hsl()) into [h,s,l]. */\nexport function parseColor(color: string): HSL {\n\tconst c = color.trim();\n\tif (c.startsWith(\"#\")) return hexToHsl(c);\n\tconst rgbMatch = c.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n\tif (rgbMatch) {\n\t\treturn rgbToHsl(\n\t\t\tNumber.parseInt(rgbMatch[1]),\n\t\t\tNumber.parseInt(rgbMatch[2]),\n\t\t\tNumber.parseInt(rgbMatch[3]),\n\t\t);\n\t}\n\tconst hslMatch = c.match(/hsla?\\((\\d+),\\s*(\\d+)%?,\\s*(\\d+)%?/);\n\tif (hslMatch) {\n\t\treturn [\n\t\t\tNumber.parseInt(hslMatch[1]),\n\t\t\tNumber.parseInt(hslMatch[2]),\n\t\t\tNumber.parseInt(hslMatch[3]),\n\t\t];\n\t}\n\t// fallback: blue\n\treturn [220, 90, 56];\n}\n\n// ─── Manipulation ─────────────────────────────────────────────────────────────\n\n/** Lighten by a percentage of the current lightness delta toward 100. */\nexport function lighten(hex: string, amount: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex(h, s, Math.min(100, l + amount));\n}\n\n/** Darken by reducing lightness. */\nexport function darken(hex: string, amount: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex(h, s, Math.max(0, l - amount));\n}\n\n/** Saturate / desaturate. */\nexport function saturate(hex: string, amount: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex(h, Math.min(100, Math.max(0, s + amount)), l);\n}\n\n/** Shift hue by `degrees`. */\nexport function rotateHue(hex: string, degrees: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex((h + degrees + 360) % 360, s, l);\n}\n\n/** Mix two hex colors with a given weight (0 = full a, 1 = full b). */\nexport function mix(hexA: string, hexB: string, weight = 0.5): string {\n\tconst [r1, g1, b1] = hexToRgb(hexA);\n\tconst [r2, g2, b2] = hexToRgb(hexB);\n\treturn rgbToHex(\n\t\tMath.round(r1 + (r2 - r1) * weight),\n\t\tMath.round(g1 + (g2 - g1) * weight),\n\t\tMath.round(b1 + (b2 - b1) * weight),\n\t);\n}\n\n/** Add alpha to a hex color, returning an rgba() string. */\nexport function withAlpha(hex: string, alpha: number): string {\n\tconst [r, g, b] = hexToRgb(hex);\n\treturn `rgba(${r},${g},${b},${alpha})`;\n}\n\n// ─── Contrast & accessibility ─────────────────────────────────────────────────\n\n/** Relative luminance per WCAG 2.1. */\nexport function luminance(hex: string): number {\n\tconst [r, g, b] = hexToRgb(hex).map((v) => {\n\t\tconst c = v / 255;\n\t\treturn c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n\t});\n\treturn 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio (≥4.5 = AA, ≥7 = AAA). */\nexport function contrastRatio(hexA: string, hexB: string): number {\n\tconst la = luminance(hexA);\n\tconst lb = luminance(hexB);\n\tconst lighter = Math.max(la, lb);\n\tconst darker = Math.min(la, lb);\n\treturn (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Return whichever of `onLight` / `onDark` has higher contrast against `bg`.\n * Defaults to pure white / pure black.\n */\nexport function autoContrast(\n\tbg: string,\n\tonLight = \"#ffffff\",\n\tonDark = \"#000000\",\n): string {\n\tconst cl = contrastRatio(bg, onLight);\n\tconst cd = contrastRatio(bg, onDark);\n\treturn cl >= cd ? onLight : onDark;\n}\n\n/**\n * Ensure text color has at least WCAG AA contrast (4.5:1) against bg.\n * Iteratively darkens or lightens until the ratio is met.\n */\nexport function ensureContrast(\n\ttext: string,\n\tbg: string,\n\tminRatio = 4.5,\n): string {\n\tlet candidate = text;\n\tconst [h, s] = hexToHsl(candidate);\n\tconst bgLum = luminance(bg);\n\tconst darkBg = bgLum < 0.5;\n\tlet l = hexToHsl(candidate)[2];\n\n\tfor (let i = 0; i < 20; i++) {\n\t\tif (contrastRatio(candidate, bg) >= minRatio) break;\n\t\tl = darkBg ? Math.min(100, l + 5) : Math.max(0, l - 5);\n\t\tcandidate = hslToHex(h, s, l);\n\t}\n\treturn candidate;\n}\n\n// ─── Color scale generator ────────────────────────────────────────────────────\n\n/**\n * Generate a Tailwind-style 11-stop scale from a single base color.\n * Returns an object: { 50, 100, 200, …, 900, 950 }\n */\nexport function generateColorScale(hex: string): Record<string, string> {\n\tconst [h, s] = hexToHsl(hex);\n\tconst stops = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];\n\t// lightness curve: 50→97%, 500→mid, 950→4%\n\tconst lightnesses = [97, 93, 86, 76, 64, 54, 44, 35, 25, 15, 8];\n\tconst result: Record<string, string> = {};\n\tstops.forEach((stop, i) => {\n\t\tresult[stop] = hslToHex(h, Math.min(s + 5, 100), lightnesses[i]);\n\t});\n\treturn result;\n}\n\n// ─── Harmony generators ───────────────────────────────────────────────────────\n\n/** Return complementary color (180° opposite). */\nexport function complementary(hex: string): string {\n\treturn rotateHue(hex, 180);\n}\n\n/** Return [primary, split1, split2] for split-complementary harmony. */\nexport function splitComplementary(hex: string): [string, string, string] {\n\treturn [hex, rotateHue(hex, 150), rotateHue(hex, 210)];\n}\n\n/** Return [primary, triadic1, triadic2] for triadic harmony. */\nexport function triadic(hex: string): [string, string, string] {\n\treturn [hex, rotateHue(hex, 120), rotateHue(hex, 240)];\n}\n\n/** Return [primary, analogous1, analogous2] shifted by `angle` degrees. */\nexport function analogous(hex: string, angle = 30): [string, string, string] {\n\treturn [hex, rotateHue(hex, angle), rotateHue(hex, -angle)];\n}\n\n// ─── Named CSS colors (subset) ───────────────────────────────────────────────\n\nconst NAMED_COLORS: Record<string, string> = {\n\tblack: '#000000', white: '#ffffff', red: '#ff0000', green: '#008000',\n\tblue: '#0000ff', yellow: '#ffff00', cyan: '#00ffff', magenta: '#ff00ff',\n\torange: '#ffa500', purple: '#800080', pink: '#ffc0cb', gray: '#808080',\n\tgrey: '#808080', silver: '#c0c0c0', maroon: '#800000', olive: '#808000',\n\tlime: '#00ff00', aqua: '#00ffff', teal: '#008080', navy: '#000080',\n\tfuchsia: '#ff00ff', transparent: '#00000000',\n}\n\n// ─── Normalize any CSS color to space-separated RGB channels ──────────────────\n\n/**\n * Normalize any CSS color value to space-separated RGB channels: `\"R G B\"`.\n *\n * This is the format required by Tailwind CSS for opacity modifier support,\n * e.g. `bg-primary/50` → `rgb(var(--vmt-primary) / 0.5)`.\n *\n * Accepts:\n * - Hex: `'#3b82f6'`, `'#fff'`, `'#3b82f680'`\n * - RGB/RGBA: `'rgb(59, 130, 246)'`, `'rgba(59, 130, 246, 0.5)'`\n * - HSL/HSLA: `'hsl(220, 90%, 56%)'`, `'hsla(220, 90%, 56%, 0.5)'`\n * - Channel-only: `'59 130 246'` (returned as-is)\n * - Named: `'red'`, `'white'`, etc. (common subset)\n *\n * @example\n * normalizeToRgbChannels('#3b82f6') // '59 130 246'\n * normalizeToRgbChannels('rgb(59,130,246)') // '59 130 246'\n * normalizeToRgbChannels('hsl(220,90%,56%)') // '59 130 246'\n * normalizeToRgbChannels('59 130 246') // '59 130 246'\n */\nexport function normalizeToRgbChannels(color: string): string {\n\tconst c = color.trim()\n\n\t// Already in channel format: \"R G B\"\n\tif (/^\\d{1,3}\\s+\\d{1,3}\\s+\\d{1,3}$/.test(c)) {\n\t\treturn c\n\t}\n\n\t// Hex\n\tif (c.startsWith('#')) {\n\t\tconst [r, g, b] = hexToRgb(c)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// rgb() / rgba()\n\tconst rgbMatch = c.match(/rgba?\\(\\s*(\\d+)\\s*[,\\s]\\s*(\\d+)\\s*[,\\s]\\s*(\\d+)/)\n\tif (rgbMatch) {\n\t\treturn `${rgbMatch[1]} ${rgbMatch[2]} ${rgbMatch[3]}`\n\t}\n\n\t// hsl() / hsla()\n\tconst hslMatch = c.match(/hsla?\\(\\s*(\\d+)\\s*[,\\s]\\s*(\\d+)%?\\s*[,\\s]\\s*(\\d+)%?/)\n\tif (hslMatch) {\n\t\tconst [r, g, b] = hslToRgb(\n\t\t\tNumber.parseInt(hslMatch[1]),\n\t\t\tNumber.parseInt(hslMatch[2]),\n\t\t\tNumber.parseInt(hslMatch[3]),\n\t\t)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// Named color\n\tconst named = NAMED_COLORS[c.toLowerCase()]\n\tif (named) {\n\t\tconst [r, g, b] = hexToRgb(named)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// Fallback: try to parse as hex without #\n\tif (/^[0-9a-fA-F]{3,8}$/.test(c)) {\n\t\tconst [r, g, b] = hexToRgb(`#${c}`)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// Last resort: return original value (CSS will use it as-is)\n\treturn c\n}\n"],"names":[],"mappings":";AAgBO,SAAS,SAAS,KAAkB;AAC1C,QAAM,IAAI,IAAI,QAAQ,KAAK,EAAE;AAC7B,QAAM,OACL,EAAE,WAAW,IACV,EACC,MAAM,EAAE,EACR,IAAI,CAAC,MAAM,IAAI,CAAC,EAChB,KAAK,EAAE,IACR;AACJ,QAAM,IAAI,OAAO,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE;AAC9C,SAAO,CAAE,KAAK,KAAM,KAAO,KAAK,IAAK,KAAM,IAAI,GAAI;AACpD;AAEO,SAAS,SAAS,GAAW,GAAW,GAAmB;AACjE,SACC,MACA,CAAC,GAAG,GAAG,CAAC,EACN;AAAA,IAAI,CAAC,MACL,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EACtC,SAAS,EAAE,EACX,SAAS,GAAG,GAAG;AAAA,EAAA,EAEjB,KAAK,EAAE;AAEX;AAEO,SAAS,SAAS,GAAW,GAAW,GAAgB;AAC9D,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,IAAI;AACf,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE;AAC/B,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE;AAC/B,QAAM,KAAK,MAAM,OAAO;AACxB,MAAI,QAAQ,IAAK,QAAO,CAAC,GAAG,GAAG,KAAK,MAAM,IAAI,GAAG,CAAC;AAClD,QAAM,IAAI,MAAM;AAChB,QAAM,IAAI,IAAI,MAAM,KAAK,IAAI,MAAM,OAAO,KAAK,MAAM;AACrD,MAAI,IAAI;AACR,MAAI,QAAQ,GAAI,OAAM,KAAK,MAAM,KAAK,KAAK,KAAK,IAAI,MAAM;AAAA,WACjD,QAAQ,GAAI,OAAM,KAAK,MAAM,IAAI,KAAK;AAAA,MAC1C,OAAM,KAAK,MAAM,IAAI,KAAK;AAC/B,SAAO,CAAC,KAAK,MAAM,IAAI,GAAG,GAAG,KAAK,MAAM,IAAI,GAAG,GAAG,KAAK,MAAM,IAAI,GAAG,CAAC;AACtE;AAEO,SAAS,SAAS,GAAW,GAAW,GAAgB;AAC9D,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,IAAI;AACf,MAAI,OAAO,GAAG;AACb,UAAM,IAAI,KAAK,MAAM,KAAK,GAAG;AAC7B,WAAO,CAAC,GAAG,GAAG,CAAC;AAAA,EAChB;AACA,QAAM,IAAI,KAAK,MAAM,MAAM,IAAI,MAAM,KAAK,KAAK,KAAK;AACpD,QAAM,IAAI,IAAI,KAAK;AACnB,QAAM,UAAU,CAAC,MAAc;AAC9B,QAAI,IAAI,EAAG,MAAK;AAChB,QAAI,IAAI,EAAG,MAAK;AAChB,QAAI,IAAI,IAAI,UAAU,KAAK,IAAI,KAAK,IAAI;AACxC,QAAI,IAAI,IAAI,EAAG,QAAO;AACtB,QAAI,IAAI,IAAI,EAAG,QAAO,KAAK,IAAI,MAAM,IAAI,IAAI,KAAK;AAClD,WAAO;AAAA,EACR;AACA,SAAO;AAAA,IACN,KAAK,MAAM,QAAQ,KAAK,IAAI,CAAC,IAAI,GAAG;AAAA,IACpC,KAAK,MAAM,QAAQ,EAAE,IAAI,GAAG;AAAA,IAC5B,KAAK,MAAM,QAAQ,KAAK,IAAI,CAAC,IAAI,GAAG;AAAA,EAAA;AAEtC;AAEO,SAAS,SAAS,KAAkB;AAC1C,SAAO,SAAS,GAAG,SAAS,GAAG,CAAC;AACjC;AAEO,SAAS,SAAS,GAAW,GAAW,GAAmB;AACjE,SAAO,SAAS,GAAG,SAAS,GAAG,GAAG,CAAC,CAAC;AACrC;AAGO,SAAS,WAAW,OAAoB;AAC9C,QAAM,IAAI,MAAM,KAAA;AAChB,MAAI,EAAE,WAAW,GAAG,EAAG,QAAO,SAAS,CAAC;AACxC,QAAM,WAAW,EAAE,MAAM,gCAAgC;AACzD,MAAI,UAAU;AACb,WAAO;AAAA,MACN,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,MAC3B,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,MAC3B,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,IAAA;AAAA,EAE7B;AACA,QAAM,WAAW,EAAE,MAAM,oCAAoC;AAC7D,MAAI,UAAU;AACb,WAAO;AAAA,MACN,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,MAC3B,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,MAC3B,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,IAAA;AAAA,EAE7B;AAEA,SAAO,CAAC,KAAK,IAAI,EAAE;AACpB;AAKO,SAAS,QAAQ,KAAa,QAAwB;AAC5D,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG;AAC9B,SAAO,SAAS,GAAG,GAAG,KAAK,IAAI,KAAK,IAAI,MAAM,CAAC;AAChD;AAGO,SAAS,OAAO,KAAa,QAAwB;AAC3D,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG;AAC9B,SAAO,SAAS,GAAG,GAAG,KAAK,IAAI,GAAG,IAAI,MAAM,CAAC;AAC9C;AAGO,SAAS,SAAS,KAAa,QAAwB;AAC7D,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG;AAC9B,SAAO,SAAS,GAAG,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC;AAC7D;AAGO,SAAS,UAAU,KAAa,SAAyB;AAC/D,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG;AAC9B,SAAO,UAAU,IAAI,UAAU,OAAO,KAAK,GAAG,CAAC;AAChD;AAGO,SAAS,IAAI,MAAc,MAAc,SAAS,KAAa;AACrE,QAAM,CAAC,IAAI,IAAI,EAAE,IAAI,SAAS,IAAI;AAClC,QAAM,CAAC,IAAI,IAAI,EAAE,IAAI,SAAS,IAAI;AAClC,SAAO;AAAA,IACN,KAAK,MAAM,MAAM,KAAK,MAAM,MAAM;AAAA,IAClC,KAAK,MAAM,MAAM,KAAK,MAAM,MAAM;AAAA,IAClC,KAAK,MAAM,MAAM,KAAK,MAAM,MAAM;AAAA,EAAA;AAEpC;AAGO,SAAS,UAAU,KAAa,OAAuB;AAC7D,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG;AAC9B,SAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK;AACpC;AAKO,SAAS,UAAU,KAAqB;AAC9C,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG,EAAE,IAAI,CAAC,MAAM;AAC1C,UAAM,IAAI,IAAI;AACd,WAAO,KAAK,UAAU,IAAI,UAAU,IAAI,SAAS,UAAU;AAAA,EAC5D,CAAC;AACD,SAAO,SAAS,IAAI,SAAS,IAAI,SAAS;AAC3C;AAGO,SAAS,cAAc,MAAc,MAAsB;AACjE,QAAM,KAAK,UAAU,IAAI;AACzB,QAAM,KAAK,UAAU,IAAI;AACzB,QAAM,UAAU,KAAK,IAAI,IAAI,EAAE;AAC/B,QAAM,SAAS,KAAK,IAAI,IAAI,EAAE;AAC9B,UAAQ,UAAU,SAAS,SAAS;AACrC;AAMO,SAAS,aACf,IACA,UAAU,WACV,SAAS,WACA;AACT,QAAM,KAAK,cAAc,IAAI,OAAO;AACpC,QAAM,KAAK,cAAc,IAAI,MAAM;AACnC,SAAO,MAAM,KAAK,UAAU;AAC7B;AAMO,SAAS,eACf,MACA,IACA,WAAW,KACF;AACT,MAAI,YAAY;AAChB,QAAM,CAAC,GAAG,CAAC,IAAI,SAAS,SAAS;AACjC,QAAM,QAAQ,UAAU,EAAE;AAC1B,QAAM,SAAS,QAAQ;AACvB,MAAI,IAAI,SAAS,SAAS,EAAE,CAAC;AAE7B,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC5B,QAAI,cAAc,WAAW,EAAE,KAAK,SAAU;AAC9C,QAAI,SAAS,KAAK,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC;AACrD,gBAAY,SAAS,GAAG,GAAG,CAAC;AAAA,EAC7B;AACA,SAAO;AACR;AAQO,SAAS,mBAAmB,KAAqC;AACvE,QAAM,CAAC,GAAG,CAAC,IAAI,SAAS,GAAG;AAC3B,QAAM,QAAQ,CAAC,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AAEnE,QAAM,cAAc,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AAC9D,QAAM,SAAiC,CAAA;AACvC,QAAM,QAAQ,CAAC,MAAM,MAAM;AAC1B,WAAO,IAAI,IAAI,SAAS,GAAG,KAAK,IAAI,IAAI,GAAG,GAAG,GAAG,YAAY,CAAC,CAAC;AAAA,EAChE,CAAC;AACD,SAAO;AACR;AAKO,SAAS,cAAc,KAAqB;AAClD,SAAO,UAAU,KAAK,GAAG;AAC1B;AAGO,SAAS,mBAAmB,KAAuC;AACzE,SAAO,CAAC,KAAK,UAAU,KAAK,GAAG,GAAG,UAAU,KAAK,GAAG,CAAC;AACtD;AAGO,SAAS,QAAQ,KAAuC;AAC9D,SAAO,CAAC,KAAK,UAAU,KAAK,GAAG,GAAG,UAAU,KAAK,GAAG,CAAC;AACtD;AAGO,SAAS,UAAU,KAAa,QAAQ,IAA8B;AAC5E,SAAO,CAAC,KAAK,UAAU,KAAK,KAAK,GAAG,UAAU,KAAK,CAAC,KAAK,CAAC;AAC3D;AAIA,MAAM,eAAuC;AAAA,EAC5C,OAAO;AAAA,EAAW,OAAO;AAAA,EAAW,KAAK;AAAA,EAAW,OAAO;AAAA,EAC3D,MAAM;AAAA,EAAW,QAAQ;AAAA,EAAW,MAAM;AAAA,EAAW,SAAS;AAAA,EAC9D,QAAQ;AAAA,EAAW,QAAQ;AAAA,EAAW,MAAM;AAAA,EAAW,MAAM;AAAA,EAC7D,MAAM;AAAA,EAAW,QAAQ;AAAA,EAAW,QAAQ;AAAA,EAAW,OAAO;AAAA,EAC9D,MAAM;AAAA,EAAW,MAAM;AAAA,EAAW,MAAM;AAAA,EAAW,MAAM;AAAA,EACzD,SAAS;AAAA,EAAW,aAAa;AAClC;AAuBO,SAAS,uBAAuB,OAAuB;AAC7D,QAAM,IAAI,MAAM,KAAA;AAGhB,MAAI,gCAAgC,KAAK,CAAC,GAAG;AAC5C,WAAO;AAAA,EACR;AAGA,MAAI,EAAE,WAAW,GAAG,GAAG;AACtB,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC;AAC5B,WAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AAAA,EACtB;AAGA,QAAM,WAAW,EAAE,MAAM,iDAAiD;AAC1E,MAAI,UAAU;AACb,WAAO,GAAG,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;AAAA,EACpD;AAGA,QAAM,WAAW,EAAE,MAAM,qDAAqD;AAC9E,MAAI,UAAU;AACb,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI;AAAA,MACjB,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,MAC3B,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,MAC3B,OAAO,SAAS,SAAS,CAAC,CAAC;AAAA,IAAA;AAE5B,WAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AAAA,EACtB;AAGA,QAAM,QAAQ,aAAa,EAAE,YAAA,CAAa;AAC1C,MAAI,OAAO;AACV,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,KAAK;AAChC,WAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AAAA,EACtB;AAGA,MAAI,qBAAqB,KAAK,CAAC,GAAG;AACjC,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE;AAClC,WAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AAAA,EACtB;AAGA,SAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
function hexToRgb(hex) {
|
|
2
|
+
const h = hex.replace("#", "");
|
|
3
|
+
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
4
|
+
const n = Number.parseInt(full.slice(0, 6), 16);
|
|
5
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
6
|
+
}
|
|
7
|
+
function rgbToHex(r, g, b) {
|
|
8
|
+
return "#" + [r, g, b].map(
|
|
9
|
+
(v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0")
|
|
10
|
+
).join("");
|
|
11
|
+
}
|
|
12
|
+
function rgbToHsl(r, g, b) {
|
|
13
|
+
const rn = r / 255;
|
|
14
|
+
const gn = g / 255;
|
|
15
|
+
const bn = b / 255;
|
|
16
|
+
const max = Math.max(rn, gn, bn);
|
|
17
|
+
const min = Math.min(rn, gn, bn);
|
|
18
|
+
const l = (max + min) / 2;
|
|
19
|
+
if (max === min) return [0, 0, Math.round(l * 100)];
|
|
20
|
+
const d = max - min;
|
|
21
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
22
|
+
let h = 0;
|
|
23
|
+
if (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;
|
|
24
|
+
else if (max === gn) h = ((bn - rn) / d + 2) / 6;
|
|
25
|
+
else h = ((rn - gn) / d + 4) / 6;
|
|
26
|
+
return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
|
|
27
|
+
}
|
|
28
|
+
function hslToRgb(h, s, l) {
|
|
29
|
+
const hn = h / 360;
|
|
30
|
+
const sn = s / 100;
|
|
31
|
+
const ln = l / 100;
|
|
32
|
+
if (sn === 0) {
|
|
33
|
+
const v = Math.round(ln * 255);
|
|
34
|
+
return [v, v, v];
|
|
35
|
+
}
|
|
36
|
+
const q = ln < 0.5 ? ln * (1 + sn) : ln + sn - ln * sn;
|
|
37
|
+
const p = 2 * ln - q;
|
|
38
|
+
const hue2rgb = (t) => {
|
|
39
|
+
if (t < 0) t += 1;
|
|
40
|
+
if (t > 1) t -= 1;
|
|
41
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
42
|
+
if (t < 1 / 2) return q;
|
|
43
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
44
|
+
return p;
|
|
45
|
+
};
|
|
46
|
+
return [
|
|
47
|
+
Math.round(hue2rgb(hn + 1 / 3) * 255),
|
|
48
|
+
Math.round(hue2rgb(hn) * 255),
|
|
49
|
+
Math.round(hue2rgb(hn - 1 / 3) * 255)
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
function hexToHsl(hex) {
|
|
53
|
+
return rgbToHsl(...hexToRgb(hex));
|
|
54
|
+
}
|
|
55
|
+
function hslToHex(h, s, l) {
|
|
56
|
+
return rgbToHex(...hslToRgb(h, s, l));
|
|
57
|
+
}
|
|
58
|
+
function parseColor(color) {
|
|
59
|
+
const c = color.trim();
|
|
60
|
+
if (c.startsWith("#")) return hexToHsl(c);
|
|
61
|
+
const rgbMatch = c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
62
|
+
if (rgbMatch) {
|
|
63
|
+
return rgbToHsl(
|
|
64
|
+
Number.parseInt(rgbMatch[1]),
|
|
65
|
+
Number.parseInt(rgbMatch[2]),
|
|
66
|
+
Number.parseInt(rgbMatch[3])
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const hslMatch = c.match(/hsla?\((\d+),\s*(\d+)%?,\s*(\d+)%?/);
|
|
70
|
+
if (hslMatch) {
|
|
71
|
+
return [
|
|
72
|
+
Number.parseInt(hslMatch[1]),
|
|
73
|
+
Number.parseInt(hslMatch[2]),
|
|
74
|
+
Number.parseInt(hslMatch[3])
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
return [220, 90, 56];
|
|
78
|
+
}
|
|
79
|
+
function lighten(hex, amount) {
|
|
80
|
+
const [h, s, l] = hexToHsl(hex);
|
|
81
|
+
return hslToHex(h, s, Math.min(100, l + amount));
|
|
82
|
+
}
|
|
83
|
+
function darken(hex, amount) {
|
|
84
|
+
const [h, s, l] = hexToHsl(hex);
|
|
85
|
+
return hslToHex(h, s, Math.max(0, l - amount));
|
|
86
|
+
}
|
|
87
|
+
function saturate(hex, amount) {
|
|
88
|
+
const [h, s, l] = hexToHsl(hex);
|
|
89
|
+
return hslToHex(h, Math.min(100, Math.max(0, s + amount)), l);
|
|
90
|
+
}
|
|
91
|
+
function rotateHue(hex, degrees) {
|
|
92
|
+
const [h, s, l] = hexToHsl(hex);
|
|
93
|
+
return hslToHex((h + degrees + 360) % 360, s, l);
|
|
94
|
+
}
|
|
95
|
+
function mix(hexA, hexB, weight = 0.5) {
|
|
96
|
+
const [r1, g1, b1] = hexToRgb(hexA);
|
|
97
|
+
const [r2, g2, b2] = hexToRgb(hexB);
|
|
98
|
+
return rgbToHex(
|
|
99
|
+
Math.round(r1 + (r2 - r1) * weight),
|
|
100
|
+
Math.round(g1 + (g2 - g1) * weight),
|
|
101
|
+
Math.round(b1 + (b2 - b1) * weight)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
function withAlpha(hex, alpha) {
|
|
105
|
+
const [r, g, b] = hexToRgb(hex);
|
|
106
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
107
|
+
}
|
|
108
|
+
function luminance(hex) {
|
|
109
|
+
const [r, g, b] = hexToRgb(hex).map((v) => {
|
|
110
|
+
const c = v / 255;
|
|
111
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
112
|
+
});
|
|
113
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
114
|
+
}
|
|
115
|
+
function contrastRatio(hexA, hexB) {
|
|
116
|
+
const la = luminance(hexA);
|
|
117
|
+
const lb = luminance(hexB);
|
|
118
|
+
const lighter = Math.max(la, lb);
|
|
119
|
+
const darker = Math.min(la, lb);
|
|
120
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
121
|
+
}
|
|
122
|
+
function autoContrast(bg, onLight = "#ffffff", onDark = "#000000") {
|
|
123
|
+
const cl = contrastRatio(bg, onLight);
|
|
124
|
+
const cd = contrastRatio(bg, onDark);
|
|
125
|
+
return cl >= cd ? onLight : onDark;
|
|
126
|
+
}
|
|
127
|
+
function ensureContrast(text, bg, minRatio = 4.5) {
|
|
128
|
+
let candidate = text;
|
|
129
|
+
const [h, s] = hexToHsl(candidate);
|
|
130
|
+
const bgLum = luminance(bg);
|
|
131
|
+
const darkBg = bgLum < 0.5;
|
|
132
|
+
let l = hexToHsl(candidate)[2];
|
|
133
|
+
for (let i = 0; i < 20; i++) {
|
|
134
|
+
if (contrastRatio(candidate, bg) >= minRatio) break;
|
|
135
|
+
l = darkBg ? Math.min(100, l + 5) : Math.max(0, l - 5);
|
|
136
|
+
candidate = hslToHex(h, s, l);
|
|
137
|
+
}
|
|
138
|
+
return candidate;
|
|
139
|
+
}
|
|
140
|
+
function generateColorScale(hex) {
|
|
141
|
+
const [h, s] = hexToHsl(hex);
|
|
142
|
+
const stops = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
143
|
+
const lightnesses = [97, 93, 86, 76, 64, 54, 44, 35, 25, 15, 8];
|
|
144
|
+
const result = {};
|
|
145
|
+
stops.forEach((stop, i) => {
|
|
146
|
+
result[stop] = hslToHex(h, Math.min(s + 5, 100), lightnesses[i]);
|
|
147
|
+
});
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
function complementary(hex) {
|
|
151
|
+
return rotateHue(hex, 180);
|
|
152
|
+
}
|
|
153
|
+
function splitComplementary(hex) {
|
|
154
|
+
return [hex, rotateHue(hex, 150), rotateHue(hex, 210)];
|
|
155
|
+
}
|
|
156
|
+
function triadic(hex) {
|
|
157
|
+
return [hex, rotateHue(hex, 120), rotateHue(hex, 240)];
|
|
158
|
+
}
|
|
159
|
+
function analogous(hex, angle = 30) {
|
|
160
|
+
return [hex, rotateHue(hex, angle), rotateHue(hex, -angle)];
|
|
161
|
+
}
|
|
162
|
+
const NAMED_COLORS = {
|
|
163
|
+
black: "#000000",
|
|
164
|
+
white: "#ffffff",
|
|
165
|
+
red: "#ff0000",
|
|
166
|
+
green: "#008000",
|
|
167
|
+
blue: "#0000ff",
|
|
168
|
+
yellow: "#ffff00",
|
|
169
|
+
cyan: "#00ffff",
|
|
170
|
+
magenta: "#ff00ff",
|
|
171
|
+
orange: "#ffa500",
|
|
172
|
+
purple: "#800080",
|
|
173
|
+
pink: "#ffc0cb",
|
|
174
|
+
gray: "#808080",
|
|
175
|
+
grey: "#808080",
|
|
176
|
+
silver: "#c0c0c0",
|
|
177
|
+
maroon: "#800000",
|
|
178
|
+
olive: "#808000",
|
|
179
|
+
lime: "#00ff00",
|
|
180
|
+
aqua: "#00ffff",
|
|
181
|
+
teal: "#008080",
|
|
182
|
+
navy: "#000080",
|
|
183
|
+
fuchsia: "#ff00ff",
|
|
184
|
+
transparent: "#00000000"
|
|
185
|
+
};
|
|
186
|
+
function normalizeToRgbChannels(color) {
|
|
187
|
+
const c = color.trim();
|
|
188
|
+
if (/^\d{1,3}\s+\d{1,3}\s+\d{1,3}$/.test(c)) {
|
|
189
|
+
return c;
|
|
190
|
+
}
|
|
191
|
+
if (c.startsWith("#")) {
|
|
192
|
+
const [r, g, b] = hexToRgb(c);
|
|
193
|
+
return `${r} ${g} ${b}`;
|
|
194
|
+
}
|
|
195
|
+
const rgbMatch = c.match(/rgba?\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)/);
|
|
196
|
+
if (rgbMatch) {
|
|
197
|
+
return `${rgbMatch[1]} ${rgbMatch[2]} ${rgbMatch[3]}`;
|
|
198
|
+
}
|
|
199
|
+
const hslMatch = c.match(/hsla?\(\s*(\d+)\s*[,\s]\s*(\d+)%?\s*[,\s]\s*(\d+)%?/);
|
|
200
|
+
if (hslMatch) {
|
|
201
|
+
const [r, g, b] = hslToRgb(
|
|
202
|
+
Number.parseInt(hslMatch[1]),
|
|
203
|
+
Number.parseInt(hslMatch[2]),
|
|
204
|
+
Number.parseInt(hslMatch[3])
|
|
205
|
+
);
|
|
206
|
+
return `${r} ${g} ${b}`;
|
|
207
|
+
}
|
|
208
|
+
const named = NAMED_COLORS[c.toLowerCase()];
|
|
209
|
+
if (named) {
|
|
210
|
+
const [r, g, b] = hexToRgb(named);
|
|
211
|
+
return `${r} ${g} ${b}`;
|
|
212
|
+
}
|
|
213
|
+
if (/^[0-9a-fA-F]{3,8}$/.test(c)) {
|
|
214
|
+
const [r, g, b] = hexToRgb(`#${c}`);
|
|
215
|
+
return `${r} ${g} ${b}`;
|
|
216
|
+
}
|
|
217
|
+
return c;
|
|
218
|
+
}
|
|
219
|
+
export {
|
|
220
|
+
hslToHex as a,
|
|
221
|
+
autoContrast as b,
|
|
222
|
+
contrastRatio as c,
|
|
223
|
+
analogous as d,
|
|
224
|
+
ensureContrast as e,
|
|
225
|
+
complementary as f,
|
|
226
|
+
darken as g,
|
|
227
|
+
hexToHsl as h,
|
|
228
|
+
generateColorScale as i,
|
|
229
|
+
hexToRgb as j,
|
|
230
|
+
hslToRgb as k,
|
|
231
|
+
lighten as l,
|
|
232
|
+
luminance as m,
|
|
233
|
+
normalizeToRgbChannels as n,
|
|
234
|
+
mix as o,
|
|
235
|
+
parseColor as p,
|
|
236
|
+
rgbToHsl as q,
|
|
237
|
+
rgbToHex as r,
|
|
238
|
+
saturate as s,
|
|
239
|
+
rotateHue as t,
|
|
240
|
+
splitComplementary as u,
|
|
241
|
+
triadic as v,
|
|
242
|
+
withAlpha as w
|
|
243
|
+
};
|
|
244
|
+
//# sourceMappingURL=color-Dvj1cP5e.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-Dvj1cP5e.js","sources":["../src/utils/color.ts"],"sourcesContent":["/**\n * color.ts — Pure color manipulation utilities.\n *\n * All functions are SSR-safe (no DOM access).\n * Everything operates on standard color formats:\n * - Hex: '#rgb' | '#rrggbb' | '#rrggbbaa'\n * - HSL: [h, s, l] where h∈[0,360), s/l∈[0,100]\n * - RGB: [r, g, b] where each channel ∈[0,255]\n */\n\nexport type RGB = [number, number, number];\nexport type RGBA = [number, number, number, number];\nexport type HSL = [number, number, number];\n\n// ─── Parsing ──────────────────────────────────────────────────────────────────\n\nexport function hexToRgb(hex: string): RGB {\n\tconst h = hex.replace(\"#\", \"\");\n\tconst full =\n\t\th.length === 3\n\t\t\t? h\n\t\t\t\t\t.split(\"\")\n\t\t\t\t\t.map((c) => c + c)\n\t\t\t\t\t.join(\"\")\n\t\t\t: h;\n\tconst n = Number.parseInt(full.slice(0, 6), 16);\n\treturn [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];\n}\n\nexport function rgbToHex(r: number, g: number, b: number): string {\n\treturn (\n\t\t\"#\" +\n\t\t[r, g, b]\n\t\t\t.map((v) =>\n\t\t\t\tMath.round(Math.max(0, Math.min(255, v)))\n\t\t\t\t\t.toString(16)\n\t\t\t\t\t.padStart(2, \"0\"),\n\t\t\t)\n\t\t\t.join(\"\")\n\t);\n}\n\nexport function rgbToHsl(r: number, g: number, b: number): HSL {\n\tconst rn = r / 255;\n\tconst gn = g / 255;\n\tconst bn = b / 255;\n\tconst max = Math.max(rn, gn, bn);\n\tconst min = Math.min(rn, gn, bn);\n\tconst l = (max + min) / 2;\n\tif (max === min) return [0, 0, Math.round(l * 100)];\n\tconst d = max - min;\n\tconst s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n\tlet h = 0;\n\tif (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;\n\telse if (max === gn) h = ((bn - rn) / d + 2) / 6;\n\telse h = ((rn - gn) / d + 4) / 6;\n\treturn [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];\n}\n\nexport function hslToRgb(h: number, s: number, l: number): RGB {\n\tconst hn = h / 360;\n\tconst sn = s / 100;\n\tconst ln = l / 100;\n\tif (sn === 0) {\n\t\tconst v = Math.round(ln * 255);\n\t\treturn [v, v, v];\n\t}\n\tconst q = ln < 0.5 ? ln * (1 + sn) : ln + sn - ln * sn;\n\tconst p = 2 * ln - q;\n\tconst hue2rgb = (t: number) => {\n\t\tif (t < 0) t += 1;\n\t\tif (t > 1) t -= 1;\n\t\tif (t < 1 / 6) return p + (q - p) * 6 * t;\n\t\tif (t < 1 / 2) return q;\n\t\tif (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n\t\treturn p;\n\t};\n\treturn [\n\t\tMath.round(hue2rgb(hn + 1 / 3) * 255),\n\t\tMath.round(hue2rgb(hn) * 255),\n\t\tMath.round(hue2rgb(hn - 1 / 3) * 255),\n\t];\n}\n\nexport function hexToHsl(hex: string): HSL {\n\treturn rgbToHsl(...hexToRgb(hex));\n}\n\nexport function hslToHex(h: number, s: number, l: number): string {\n\treturn rgbToHex(...hslToRgb(h, s, l));\n}\n\n/** Parse any CSS color string (hex, rgb(), hsl()) into [h,s,l]. */\nexport function parseColor(color: string): HSL {\n\tconst c = color.trim();\n\tif (c.startsWith(\"#\")) return hexToHsl(c);\n\tconst rgbMatch = c.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n\tif (rgbMatch) {\n\t\treturn rgbToHsl(\n\t\t\tNumber.parseInt(rgbMatch[1]),\n\t\t\tNumber.parseInt(rgbMatch[2]),\n\t\t\tNumber.parseInt(rgbMatch[3]),\n\t\t);\n\t}\n\tconst hslMatch = c.match(/hsla?\\((\\d+),\\s*(\\d+)%?,\\s*(\\d+)%?/);\n\tif (hslMatch) {\n\t\treturn [\n\t\t\tNumber.parseInt(hslMatch[1]),\n\t\t\tNumber.parseInt(hslMatch[2]),\n\t\t\tNumber.parseInt(hslMatch[3]),\n\t\t];\n\t}\n\t// fallback: blue\n\treturn [220, 90, 56];\n}\n\n// ─── Manipulation ─────────────────────────────────────────────────────────────\n\n/** Lighten by a percentage of the current lightness delta toward 100. */\nexport function lighten(hex: string, amount: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex(h, s, Math.min(100, l + amount));\n}\n\n/** Darken by reducing lightness. */\nexport function darken(hex: string, amount: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex(h, s, Math.max(0, l - amount));\n}\n\n/** Saturate / desaturate. */\nexport function saturate(hex: string, amount: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex(h, Math.min(100, Math.max(0, s + amount)), l);\n}\n\n/** Shift hue by `degrees`. */\nexport function rotateHue(hex: string, degrees: number): string {\n\tconst [h, s, l] = hexToHsl(hex);\n\treturn hslToHex((h + degrees + 360) % 360, s, l);\n}\n\n/** Mix two hex colors with a given weight (0 = full a, 1 = full b). */\nexport function mix(hexA: string, hexB: string, weight = 0.5): string {\n\tconst [r1, g1, b1] = hexToRgb(hexA);\n\tconst [r2, g2, b2] = hexToRgb(hexB);\n\treturn rgbToHex(\n\t\tMath.round(r1 + (r2 - r1) * weight),\n\t\tMath.round(g1 + (g2 - g1) * weight),\n\t\tMath.round(b1 + (b2 - b1) * weight),\n\t);\n}\n\n/** Add alpha to a hex color, returning an rgba() string. */\nexport function withAlpha(hex: string, alpha: number): string {\n\tconst [r, g, b] = hexToRgb(hex);\n\treturn `rgba(${r},${g},${b},${alpha})`;\n}\n\n// ─── Contrast & accessibility ─────────────────────────────────────────────────\n\n/** Relative luminance per WCAG 2.1. */\nexport function luminance(hex: string): number {\n\tconst [r, g, b] = hexToRgb(hex).map((v) => {\n\t\tconst c = v / 255;\n\t\treturn c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n\t});\n\treturn 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio (≥4.5 = AA, ≥7 = AAA). */\nexport function contrastRatio(hexA: string, hexB: string): number {\n\tconst la = luminance(hexA);\n\tconst lb = luminance(hexB);\n\tconst lighter = Math.max(la, lb);\n\tconst darker = Math.min(la, lb);\n\treturn (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Return whichever of `onLight` / `onDark` has higher contrast against `bg`.\n * Defaults to pure white / pure black.\n */\nexport function autoContrast(\n\tbg: string,\n\tonLight = \"#ffffff\",\n\tonDark = \"#000000\",\n): string {\n\tconst cl = contrastRatio(bg, onLight);\n\tconst cd = contrastRatio(bg, onDark);\n\treturn cl >= cd ? onLight : onDark;\n}\n\n/**\n * Ensure text color has at least WCAG AA contrast (4.5:1) against bg.\n * Iteratively darkens or lightens until the ratio is met.\n */\nexport function ensureContrast(\n\ttext: string,\n\tbg: string,\n\tminRatio = 4.5,\n): string {\n\tlet candidate = text;\n\tconst [h, s] = hexToHsl(candidate);\n\tconst bgLum = luminance(bg);\n\tconst darkBg = bgLum < 0.5;\n\tlet l = hexToHsl(candidate)[2];\n\n\tfor (let i = 0; i < 20; i++) {\n\t\tif (contrastRatio(candidate, bg) >= minRatio) break;\n\t\tl = darkBg ? Math.min(100, l + 5) : Math.max(0, l - 5);\n\t\tcandidate = hslToHex(h, s, l);\n\t}\n\treturn candidate;\n}\n\n// ─── Color scale generator ────────────────────────────────────────────────────\n\n/**\n * Generate a Tailwind-style 11-stop scale from a single base color.\n * Returns an object: { 50, 100, 200, …, 900, 950 }\n */\nexport function generateColorScale(hex: string): Record<string, string> {\n\tconst [h, s] = hexToHsl(hex);\n\tconst stops = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];\n\t// lightness curve: 50→97%, 500→mid, 950→4%\n\tconst lightnesses = [97, 93, 86, 76, 64, 54, 44, 35, 25, 15, 8];\n\tconst result: Record<string, string> = {};\n\tstops.forEach((stop, i) => {\n\t\tresult[stop] = hslToHex(h, Math.min(s + 5, 100), lightnesses[i]);\n\t});\n\treturn result;\n}\n\n// ─── Harmony generators ───────────────────────────────────────────────────────\n\n/** Return complementary color (180° opposite). */\nexport function complementary(hex: string): string {\n\treturn rotateHue(hex, 180);\n}\n\n/** Return [primary, split1, split2] for split-complementary harmony. */\nexport function splitComplementary(hex: string): [string, string, string] {\n\treturn [hex, rotateHue(hex, 150), rotateHue(hex, 210)];\n}\n\n/** Return [primary, triadic1, triadic2] for triadic harmony. */\nexport function triadic(hex: string): [string, string, string] {\n\treturn [hex, rotateHue(hex, 120), rotateHue(hex, 240)];\n}\n\n/** Return [primary, analogous1, analogous2] shifted by `angle` degrees. */\nexport function analogous(hex: string, angle = 30): [string, string, string] {\n\treturn [hex, rotateHue(hex, angle), rotateHue(hex, -angle)];\n}\n\n// ─── Named CSS colors (subset) ───────────────────────────────────────────────\n\nconst NAMED_COLORS: Record<string, string> = {\n\tblack: '#000000', white: '#ffffff', red: '#ff0000', green: '#008000',\n\tblue: '#0000ff', yellow: '#ffff00', cyan: '#00ffff', magenta: '#ff00ff',\n\torange: '#ffa500', purple: '#800080', pink: '#ffc0cb', gray: '#808080',\n\tgrey: '#808080', silver: '#c0c0c0', maroon: '#800000', olive: '#808000',\n\tlime: '#00ff00', aqua: '#00ffff', teal: '#008080', navy: '#000080',\n\tfuchsia: '#ff00ff', transparent: '#00000000',\n}\n\n// ─── Normalize any CSS color to space-separated RGB channels ──────────────────\n\n/**\n * Normalize any CSS color value to space-separated RGB channels: `\"R G B\"`.\n *\n * This is the format required by Tailwind CSS for opacity modifier support,\n * e.g. `bg-primary/50` → `rgb(var(--vmt-primary) / 0.5)`.\n *\n * Accepts:\n * - Hex: `'#3b82f6'`, `'#fff'`, `'#3b82f680'`\n * - RGB/RGBA: `'rgb(59, 130, 246)'`, `'rgba(59, 130, 246, 0.5)'`\n * - HSL/HSLA: `'hsl(220, 90%, 56%)'`, `'hsla(220, 90%, 56%, 0.5)'`\n * - Channel-only: `'59 130 246'` (returned as-is)\n * - Named: `'red'`, `'white'`, etc. (common subset)\n *\n * @example\n * normalizeToRgbChannels('#3b82f6') // '59 130 246'\n * normalizeToRgbChannels('rgb(59,130,246)') // '59 130 246'\n * normalizeToRgbChannels('hsl(220,90%,56%)') // '59 130 246'\n * normalizeToRgbChannels('59 130 246') // '59 130 246'\n */\nexport function normalizeToRgbChannels(color: string): string {\n\tconst c = color.trim()\n\n\t// Already in channel format: \"R G B\"\n\tif (/^\\d{1,3}\\s+\\d{1,3}\\s+\\d{1,3}$/.test(c)) {\n\t\treturn c\n\t}\n\n\t// Hex\n\tif (c.startsWith('#')) {\n\t\tconst [r, g, b] = hexToRgb(c)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// rgb() / rgba()\n\tconst rgbMatch = c.match(/rgba?\\(\\s*(\\d+)\\s*[,\\s]\\s*(\\d+)\\s*[,\\s]\\s*(\\d+)/)\n\tif (rgbMatch) {\n\t\treturn `${rgbMatch[1]} ${rgbMatch[2]} ${rgbMatch[3]}`\n\t}\n\n\t// hsl() / hsla()\n\tconst hslMatch = c.match(/hsla?\\(\\s*(\\d+)\\s*[,\\s]\\s*(\\d+)%?\\s*[,\\s]\\s*(\\d+)%?/)\n\tif (hslMatch) {\n\t\tconst [r, g, b] = hslToRgb(\n\t\t\tNumber.parseInt(hslMatch[1]),\n\t\t\tNumber.parseInt(hslMatch[2]),\n\t\t\tNumber.parseInt(hslMatch[3]),\n\t\t)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// Named color\n\tconst named = NAMED_COLORS[c.toLowerCase()]\n\tif (named) {\n\t\tconst [r, g, b] = hexToRgb(named)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// Fallback: try to parse as hex without #\n\tif (/^[0-9a-fA-F]{3,8}$/.test(c)) {\n\t\tconst [r, g, b] = hexToRgb(`#${c}`)\n\t\treturn `${r} ${g} ${b}`\n\t}\n\n\t// Last resort: return original value (CSS will use it as-is)\n\treturn c\n}\n"],"names":[],"mappings":"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"}
|