rainbowindex 0.5.0 → 0.6.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.
@@ -1,622 +1,3 @@
1
- // src/theme/colors.ts
2
- function isValidColorSuffix(n) {
3
- return Number.isInteger(n) && n >= 1 && n <= 999;
4
- }
5
- var STOP_LO = 50;
6
- var STOP_STEP = 50;
7
- var L_PROFILE = [
8
- 0.9668,
9
- // 50
10
- 0.936756,
11
- // 100
12
- 0.905984,
13
- // 150
14
- 0.87442,
15
- // 200
16
- 0.841988,
17
- // 250
18
- 0.808602,
19
- // 300
20
- 0.774158,
21
- // 350
22
- 0.73853,
23
- // 400
24
- 0.701568,
25
- // 450
26
- 0.663082,
27
- // 500
28
- 0.622832,
29
- // 550
30
- 0.580504,
31
- // 600
32
- 0.535677,
33
- // 650
34
- 0.487755,
35
- // 700
36
- 0.435847,
37
- // 750
38
- 0.378506,
39
- // 800
40
- 0.313034,
41
- // 850
42
- 0.232988,
43
- // 900
44
- 0.045
45
- // 950
46
- ];
47
- var REF_CHROMA = 0.05;
48
- var C_REF = [
49
- 0.006863863034589374,
50
- // 50
51
- 0.01319862716849036,
52
- // 100
53
- 0.019388384668336325,
54
- // 150
55
- 0.025429811853816625,
56
- // 200
57
- 0.03131716290796655,
58
- // 250
59
- 0.03704281264548738,
60
- // 300
61
- 0.04259815284255128,
62
- // 350
63
- 0.04797259847240491,
64
- // 400
65
- 0.053139652622711774,
66
- // 450
67
- 0.05807254410984486,
68
- // 500
69
- 0.057890440330825195,
70
- // 550
71
- 0.05222950205066024,
72
- // 600
73
- 0.04663166172903349,
74
- // 650
75
- 0.04108661482545571,
76
- // 700
77
- 0.035584106512529856,
78
- // 750
79
- 0.030108630901677546,
80
- // 800
81
- 0.02463007273923548,
82
- // 850
83
- 0.01911062262055484,
84
- // 900
85
- 0.013470720217073236
86
- // 950
87
- ];
88
- var H_DRIFT = [
89
- -0.93815866691077,
90
- // 50
91
- -0.88308454198904,
92
- // 100
93
- -0.82986570023714,
94
- // 150
95
- -0.77925524218887,
96
- // 200
97
- -0.73231968077941,
98
- // 250
99
- -0.69055370894147,
100
- // 300
101
- -0.65608113228225,
102
- // 350
103
- -0.6319804472577,
104
- // 400
105
- -0.62282301825498,
106
- // 450
107
- -0.63542113918918,
108
- // 500
109
- -0.65047025648765,
110
- // 550
111
- -0.64293938410323,
112
- // 600
113
- -0.63626978185344,
114
- // 650
115
- -0.63057786221076,
116
- // 700
117
- -0.62611168444876,
118
- // 750
119
- -0.62332854799372,
120
- // 800
121
- -0.62317507759171,
122
- // 850
123
- -0.62789446720379,
124
- // 900
125
- -0.64378251527182
126
- // 950
127
- ];
128
- function sampleProfile(values, suffix, monotone) {
129
- const last = values.length - 1;
130
- const fx = (suffix - STOP_LO) / STOP_STEP;
131
- if (fx <= 0) return values[0] + (values[1] - values[0]) * fx;
132
- if (fx >= last) return values[last] + (values[last] - values[last - 1]) * (fx - last);
133
- const i = Math.floor(fx);
134
- const t = fx - i;
135
- if (t === 0) return values[i];
136
- const seg = values[i + 1] - values[i];
137
- const tangent = (k) => {
138
- if (k <= 0) return values[1] - values[0];
139
- if (k >= last) return values[last] - values[last - 1];
140
- return (values[k + 1] - values[k - 1]) / 2;
141
- };
142
- let m0 = tangent(i);
143
- let m1 = tangent(i + 1);
144
- if (monotone) {
145
- if (seg === 0) {
146
- m0 = 0;
147
- m1 = 0;
148
- } else {
149
- const a = m0 / seg;
150
- const b = m1 / seg;
151
- const s = a * a + b * b;
152
- if (s > 9) {
153
- const tau = 3 / Math.sqrt(s);
154
- m0 = tau * a * seg;
155
- m1 = tau * b * seg;
156
- }
157
- }
158
- }
159
- const t2 = t * t;
160
- const t3 = t2 * t;
161
- return (2 * t3 - 3 * t2 + 1) * values[i] + (t3 - 2 * t2 + t) * m0 + (-2 * t3 + 3 * t2) * values[i + 1] + (t3 - t2) * m1;
162
- }
163
- function lightnessFromSuffix(suffix) {
164
- return Math.min(0.98, Math.max(0.02, sampleProfile(L_PROFILE, suffix, true)));
165
- }
166
- var DEFAULT_COLORS = Object.freeze({
167
- theme: {
168
- type: "generative",
169
- chroma: 0,
170
- hue: 0,
171
- parabolic: false,
172
- chromaBoost: false
173
- }
174
- });
175
- var DEFAULT_DARK_CONFIG = {
176
- mode: "auto",
177
- // 0 → dark keeps the light stop's chroma exactly (same tone in both modes).
178
- // Raise it via `@color dark { chroma-boost }` for punchier dark-mode colors.
179
- chromaBoost: 0,
180
- hueShift: 0
181
- };
182
- var GAMUT_CLAMP_ITERATIONS = 20;
183
- function oklchToOklab(l, c, h) {
184
- const hRad = h * Math.PI / 180;
185
- return [l, c * Math.cos(hRad), c * Math.sin(hRad)];
186
- }
187
- function oklabToLinearSrgb(l, a, b) {
188
- const l_ = l + 0.3963377774 * a + 0.2158037573 * b;
189
- const m_ = l - 0.1055613458 * a - 0.0638541728 * b;
190
- const s_ = l - 0.0894841775 * a - 1.291485548 * b;
191
- const lc = l_ * l_ * l_;
192
- const mc = m_ * m_ * m_;
193
- const sc = s_ * s_ * s_;
194
- const r = 4.0767416621 * lc - 3.3077115913 * mc + 0.2309699292 * sc;
195
- const g = -1.2684380046 * lc + 2.6097574011 * mc - 0.3413193965 * sc;
196
- const bv = -0.0041960863 * lc - 0.7034186147 * mc + 1.707614701 * sc;
197
- return [r, g, bv];
198
- }
199
- function isInSrgbGamut(l, c, h) {
200
- const [la, a, b] = oklchToOklab(l, c, h);
201
- const [r, g, bv] = oklabToLinearSrgb(la, a, b);
202
- const eps = 1e-3;
203
- return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && bv >= -eps && bv <= 1 + eps;
204
- }
205
- function gamutSafeChroma(l, requestedChroma, h) {
206
- if (requestedChroma <= 0) return 0;
207
- if (isInSrgbGamut(l, requestedChroma, h)) return requestedChroma;
208
- let lo = 0;
209
- let hi = requestedChroma;
210
- for (let i = 0; i < GAMUT_CLAMP_ITERATIONS; i++) {
211
- const mid = (lo + hi) / 2;
212
- if (isInSrgbGamut(l, mid, h)) {
213
- lo = mid;
214
- } else {
215
- hi = mid;
216
- }
217
- if (hi - lo < 5e-4) break;
218
- }
219
- return lo;
220
- }
221
- var APCA_MAIN_TRC = 2.4;
222
- var APCA_NORM_BG = 0.56;
223
- var APCA_NORM_TXT = 0.57;
224
- var APCA_REV_TXT = 0.62;
225
- var APCA_REV_BG = 0.65;
226
- var APCA_BLK_THRS = 0.022;
227
- var APCA_BLK_CLMP = Math.SQRT2;
228
- var APCA_SCALE = 1.14;
229
- var APCA_LO_BOW_OFFSET = 0.027;
230
- var APCA_LO_WOB_OFFSET = 0.027;
231
- var APCA_DELTA_Y_MIN = 5e-4;
232
- var APCA_LO_CLIP = 0.1;
233
- function linearToSrgb(c) {
234
- const x = Math.max(0, Math.min(1, c));
235
- return x <= 31308e-7 ? 12.92 * x : 1.055 * x ** (1 / 2.4) - 0.055;
236
- }
237
- function oklchToApcaY(l, c, h) {
238
- const [la, a, b] = oklchToOklab(l, c, h);
239
- const [lr, lg, lb] = oklabToLinearSrgb(la, a, b);
240
- const r = linearToSrgb(lr);
241
- const g = linearToSrgb(lg);
242
- const bv = linearToSrgb(lb);
243
- return 0.2126 * r ** APCA_MAIN_TRC + 0.7152 * g ** APCA_MAIN_TRC + 0.0722 * bv ** APCA_MAIN_TRC;
244
- }
245
- function apcaContrast(yText, yBg) {
246
- if (yText < 0 || yBg < 0) return 0;
247
- if (Math.max(yText, yBg) < APCA_BLK_THRS) return 0;
248
- const txtY = yText < APCA_BLK_THRS ? yText + (APCA_BLK_THRS - yText) ** APCA_BLK_CLMP : yText;
249
- const bgY = yBg < APCA_BLK_THRS ? yBg + (APCA_BLK_THRS - yBg) ** APCA_BLK_CLMP : yBg;
250
- if (Math.abs(bgY - txtY) < APCA_DELTA_Y_MIN) return 0;
251
- if (bgY > txtY) {
252
- const sapc2 = (bgY ** APCA_NORM_BG - txtY ** APCA_NORM_TXT) * APCA_SCALE;
253
- return sapc2 < APCA_LO_CLIP ? 0 : (sapc2 - APCA_LO_BOW_OFFSET) * 100;
254
- }
255
- const sapc = (bgY ** APCA_REV_BG - txtY ** APCA_REV_TXT) * APCA_SCALE;
256
- return sapc > -APCA_LO_CLIP ? 0 : (sapc + APCA_LO_WOB_OFFSET) * 100;
257
- }
258
- var _stopMemo = /* @__PURE__ */ new WeakMap();
259
- function generateStop(def, suffix) {
260
- let bySuffix = _stopMemo.get(def);
261
- if (!bySuffix) {
262
- bySuffix = /* @__PURE__ */ new Map();
263
- _stopMemo.set(def, bySuffix);
264
- }
265
- const cached = bySuffix.get(suffix);
266
- if (cached) return cached;
267
- const stop = computeStop(def, suffix);
268
- bySuffix.set(suffix, stop);
269
- return stop;
270
- }
271
- function computeStop(def, suffix) {
272
- const roundedL = Math.round(lightnessFromSuffix(suffix) * 1e3) / 1e3;
273
- const h = def.chroma > 0 ? def.hue + sampleProfile(H_DRIFT, suffix, false) : def.hue;
274
- const roundedH = Math.round(h * 1e3) / 1e3;
275
- const shapedChroma = def.parabolic === false ? def.chroma : def.chroma * sampleProfile(C_REF, suffix, false) / REF_CHROMA;
276
- const safeC = gamutSafeChroma(roundedL, shapedChroma, roundedH);
277
- const roundedC = Math.floor(safeC * 1e4) / 1e4;
278
- return {
279
- stop: suffix,
280
- l: roundedL,
281
- c: roundedC,
282
- h: roundedH
283
- };
284
- }
285
- function computeDarkStop(def, suffix, darkConfig, darkOverride) {
286
- const lightStop = generateStop(def, suffix);
287
- const darkL = generateStop(def, 1e3 - suffix).l;
288
- let extraChroma = 0;
289
- let extraHue = 0;
290
- if (darkOverride?.strategy === "shift") {
291
- extraChroma = darkOverride.chromaDelta;
292
- extraHue = darkOverride.hueDelta;
293
- }
294
- const darkH = lightStop.h + darkConfig.hueShift + extraHue;
295
- const globalChromaBoost = def.chromaBoost !== false ? darkConfig.chromaBoost : 0;
296
- const requestedChroma = lightStop.c + globalChromaBoost + extraChroma;
297
- const darkC = Math.floor(gamutSafeChroma(darkL, requestedChroma, darkH) * 1e4) / 1e4;
298
- return { stop: suffix, l: darkL, c: darkC, h: darkH };
299
- }
300
- function formatOklch(l, c, h) {
301
- return `oklch(${l} ${c} ${h})`;
302
- }
303
- function generateColorVariables(name, def, suffixes, darkConfig = DEFAULT_DARK_CONFIG, darkOverride) {
304
- const vars = [];
305
- const strategy = darkOverride?.strategy ?? "mirror";
306
- for (const suffix of suffixes) {
307
- const lightStop = generateStop(def, suffix);
308
- const lightValue = formatOklch(lightStop.l, lightStop.c, lightStop.h);
309
- if (darkConfig.mode === "off" || strategy === "fixed") {
310
- if (darkConfig.mode === "off") {
311
- vars.push(`--color-${name}-${suffix}: ${lightValue};`);
312
- } else {
313
- vars.push(`--color-${name}-${suffix}: light-dark(${lightValue}, ${lightValue});`);
314
- }
315
- } else {
316
- const darkStop = computeDarkStop(def, suffix, darkConfig, darkOverride);
317
- const darkValue = formatOklch(darkStop.l, darkStop.c, darkStop.h);
318
- vars.push(`--color-${name}-${suffix}: light-dark(${lightValue}, ${darkValue});`);
319
- }
320
- }
321
- return vars;
322
- }
323
- var SEMANTIC_COLORS = Object.freeze([
324
- "--color-paper: light-dark(oklch(1 0 0), oklch(0 0 0));",
325
- "--color-ink: light-dark(oklch(0 0 0), oklch(1 0 0));"
326
- ]);
327
- var DEFAULT_PALETTE_CONTRAST_LC = 60;
328
- function checkPaletteContrast(colors, usedColorStops, options = {}) {
329
- const minLc = options.minLc ?? DEFAULT_PALETTE_CONTRAST_LC;
330
- const warnings = [];
331
- const paperY = oklchToApcaY(1, 0, 0);
332
- const inkY = oklchToApcaY(0, 0, 0);
333
- for (const [name, def] of Object.entries(colors)) {
334
- if (def.type !== "generative") continue;
335
- const stops = usedColorStops.get(name);
336
- if (!stops || stops.size === 0) continue;
337
- const sorted = [...stops].sort((a, b) => a - b);
338
- for (const suffix of sorted) {
339
- const stop = generateStop(def, suffix);
340
- const fgY = oklchToApcaY(stop.l, stop.c, stop.h);
341
- const lcPaper = Math.abs(apcaContrast(fgY, paperY));
342
- const lcInk = Math.abs(apcaContrast(fgY, inkY));
343
- const best = Math.max(lcPaper, lcInk);
344
- if (best < minLc) {
345
- warnings.push(
346
- `[RI-1106] @color "${name}-${suffix}" has low APCA contrast (best |Lc| ${best.toFixed(0)} < ${minLc}; paper ${lcPaper.toFixed(0)}, ink ${lcInk.toFixed(0)}). Stop is unsuitable as text on either --color-paper or --color-ink \u2014 use a darker (lower suffix) or lighter (higher suffix) variant for text roles.`
347
- );
348
- }
349
- }
350
- }
351
- return warnings;
352
- }
353
- function generateAllColorVariables(colors = DEFAULT_COLORS, darkConfig = DEFAULT_DARK_CONFIG, usedColorStops) {
354
- const allVars = [];
355
- allVars.push(...SEMANTIC_COLORS);
356
- for (const [name, def] of Object.entries(colors)) {
357
- switch (def.type) {
358
- case "generative": {
359
- const suffixes = usedColorStops?.get(name);
360
- if (!suffixes || suffixes.size === 0) break;
361
- const sorted = [...suffixes].sort((a, b) => a - b);
362
- allVars.push(...generateColorVariables(name, def, sorted, darkConfig, def.dark));
363
- break;
364
- }
365
- case "explicit": {
366
- allVars.push(`--color-${name}: ${def.value};`);
367
- break;
368
- }
369
- case "pair": {
370
- if (darkConfig.mode === "off") {
371
- allVars.push(`--color-${name}: ${def.light};`);
372
- } else {
373
- allVars.push(`--color-${name}: light-dark(${def.light}, ${def.dark});`);
374
- }
375
- break;
376
- }
377
- case "alias": {
378
- const source = colors[def.source];
379
- if (source && source.type === "generative") {
380
- const suffixes = usedColorStops?.get(name);
381
- if (!suffixes || suffixes.size === 0) break;
382
- const sorted = [...suffixes].sort((a, b) => a - b);
383
- for (const suffix of sorted) {
384
- allVars.push(`--color-${name}-${suffix}: var(--color-${def.source}-${suffix});`);
385
- }
386
- } else if (source) {
387
- allVars.push(`--color-${name}: var(--color-${def.source});`);
388
- }
389
- break;
390
- }
391
- }
392
- }
393
- return allVars;
394
- }
395
- function generateThemeOverrides(colors, usedThemeSuffixes) {
396
- if (!Object.hasOwn(colors, "theme")) return [];
397
- if (!usedThemeSuffixes || usedThemeSuffixes.size === 0) return [];
398
- const sorted = [...usedThemeSuffixes].sort((a, b) => a - b);
399
- const blocks = [];
400
- for (const [name, def] of Object.entries(colors)) {
401
- if (name === "theme") continue;
402
- if (def.type !== "generative") continue;
403
- if (!def.inline) continue;
404
- const vars = [];
405
- for (const suffix of sorted) {
406
- vars.push(` --color-theme-${suffix}: var(--color-${name}-${suffix});`);
407
- }
408
- const safeName = name.replace(/[\\"'\]]/g, "");
409
- if (!safeName) continue;
410
- blocks.push(`[data-theme="${safeName}"] {
411
- ${vars.join("\n")}
412
- }`);
413
- }
414
- return blocks;
415
- }
416
-
417
- // src/theme/index.ts
418
- var DEFAULT_TEXT = {
419
- "2xs": { fontSize: "0.625rem", lineHeight: "1.2" },
420
- xs: { fontSize: "0.75rem", lineHeight: "1.3" },
421
- sm: { fontSize: "0.875rem", lineHeight: "1.4" },
422
- md: { fontSize: "1rem", lineHeight: "1.5" },
423
- lg: { fontSize: "1.25rem", lineHeight: "1.4" },
424
- xl: { fontSize: "1.5rem", lineHeight: "1.3" },
425
- "2xl": { fontSize: "1.875rem", lineHeight: "1.2" },
426
- "3xl": { fontSize: "2.25rem", lineHeight: "1.15" },
427
- "4xl": { fontSize: "2.813rem", lineHeight: "1.1" },
428
- "5xl": { fontSize: "3.5rem", lineHeight: "1.05" },
429
- "6xl": { fontSize: "4.375rem", lineHeight: "1.05" },
430
- "7xl": { fontSize: "5.5rem", lineHeight: "1" },
431
- "8xl": { fontSize: "6.875rem", lineHeight: "1" },
432
- "9xl": { fontSize: "8.5rem", lineHeight: "1" }
433
- };
434
- var DEFAULT_BREAKPOINTS = {
435
- sm: "40rem",
436
- md: "48rem",
437
- lg: "64rem",
438
- xl: "80rem"
439
- };
440
- var DEFAULT_ROUNDED_ROOF = "1.5rem";
441
- var DEFAULT_ROUNDED = {
442
- "2xs": "max(0px, calc(var(--rounded-roof) - var(--spacing) * 5))",
443
- xs: "max(0px, calc(var(--rounded-roof) - var(--spacing) * 4))",
444
- sm: "max(0px, calc(var(--rounded-roof) - var(--spacing) * 3))",
445
- md: "max(0px, calc(var(--rounded-roof) - var(--spacing) * 2))",
446
- lg: "max(0px, calc(var(--rounded-roof) - var(--spacing)))",
447
- xl: "var(--rounded-roof)",
448
- full: "calc(infinity * 1px)"
449
- };
450
- var CORNER_SHAPE_KEYWORDS = [
451
- "round",
452
- "scoop",
453
- "bevel",
454
- "notch",
455
- "square",
456
- "squircle"
457
- ];
458
- var DEFAULT_CORNER_SCALE = {
459
- round: 1,
460
- square: 1,
461
- notch: 1,
462
- bevel: 0.8,
463
- scoop: 1.2,
464
- squircle: 1.6
465
- };
466
- var DEFAULT_SUPERELLIPSE_SCALE = 1.6;
467
- var DEFAULT_SHADOWS = {
468
- // Building blocks: colors
469
- line: "light-dark(oklch(0 0 0 / 0.06), oklch(1 0 0 / 0.02))",
470
- drop: "light-dark(oklch(0 0 0 / 0.06), oklch(0 0 0 / 0.18))",
471
- "hi-1": "light-dark(transparent, oklch(1 0 0 / 0.01))",
472
- "hi-2": "light-dark(transparent, oklch(1 0 0 / 0.02))",
473
- "hi-3": "light-dark(transparent, oklch(1 0 0 / 0.04))",
474
- "hi-4": "light-dark(transparent, oklch(1 0 0 / 0.06))",
475
- "dark-line": "light-dark(transparent, oklch(0 0 0 / 0.14))",
476
- // Building blocks: composed shadow layers
477
- ring: "0 0 0 1px var(--shadow-line)",
478
- "layer-1": "0 1px 1px -0.5px var(--shadow-drop)",
479
- "layer-2": "0 3px 3px -1.5px var(--shadow-drop)",
480
- "layer-3": "0 6px 6px -3px var(--shadow-drop)",
481
- "layer-4": "0 12px 12px -6px var(--shadow-drop)",
482
- "layer-5": "0 24px 24px -12px var(--shadow-drop)",
483
- "layer-6": "0 48px 48px -24px var(--shadow-drop)",
484
- "layer-7": "0 96px 96px -48px var(--shadow-drop)",
485
- // Class-facing
486
- px: "var(--shadow-ring)",
487
- "2xs": "inset 0 1px 0 0 var(--shadow-hi-1), var(--shadow-ring), var(--shadow-layer-1)",
488
- xs: "inset 0 1px 0 0 var(--shadow-hi-2), var(--shadow-ring), 0 0 0 1px var(--shadow-dark-line), var(--shadow-layer-1), var(--shadow-layer-2)",
489
- sm: "inset 0 1px 0 0 var(--shadow-hi-2), var(--shadow-ring), 0 0 0 1px var(--shadow-dark-line), var(--shadow-layer-1), var(--shadow-layer-2), var(--shadow-layer-3)",
490
- md: "inset 0 1px 0 0 var(--shadow-hi-3), var(--shadow-ring), 0 0 0 1px var(--shadow-dark-line), var(--shadow-layer-1), var(--shadow-layer-2), var(--shadow-layer-3), var(--shadow-layer-4)",
491
- lg: "inset 0 1px 0 0 var(--shadow-hi-3), var(--shadow-ring), 0 0 0 1px var(--shadow-dark-line), var(--shadow-layer-1), var(--shadow-layer-2), var(--shadow-layer-3), var(--shadow-layer-4), var(--shadow-layer-5)",
492
- xl: "inset 0 1px 0 0 var(--shadow-hi-4), var(--shadow-ring), 0 0 0 1px var(--shadow-dark-line), var(--shadow-layer-1), var(--shadow-layer-2), var(--shadow-layer-3), var(--shadow-layer-4), var(--shadow-layer-5), var(--shadow-layer-6)",
493
- "2xl": "inset 0 1px 0 0 var(--shadow-hi-4), var(--shadow-ring), 0 0 0 1px var(--shadow-dark-line), var(--shadow-layer-1), var(--shadow-layer-2), var(--shadow-layer-3), var(--shadow-layer-4), var(--shadow-layer-5), var(--shadow-layer-6), var(--shadow-layer-7)",
494
- none: "0 0 #0000"
495
- };
496
- var DEFAULT_WEIGHTS = {
497
- thin: 100,
498
- extralight: 200,
499
- light: 300,
500
- normal: 400,
501
- medium: 500,
502
- semibold: 600,
503
- bold: 700,
504
- extrabold: 800,
505
- black: 900
506
- };
507
- var DEFAULT_EASING = {
508
- in: "cubic-bezier(0.4, 0, 1, 1)",
509
- out: "cubic-bezier(0, 0, 0.2, 1)",
510
- "in-out": "cubic-bezier(0.4, 0, 0.2, 1)",
511
- linear: "linear"
512
- };
513
- var DEFAULT_BLUR = {
514
- xs: "2px",
515
- sm: "4px",
516
- DEFAULT: "8px",
517
- md: "12px",
518
- lg: "16px",
519
- xl: "24px",
520
- "2xl": "40px",
521
- "3xl": "64px",
522
- none: "0"
523
- };
524
- var DEFAULT_ANIMATIONS = {
525
- spin: {
526
- shorthand: "spin 1s linear infinite",
527
- keyframes: `from { transform: rotate(0deg); }
528
- to { transform: rotate(360deg); }`
529
- },
530
- pulse: {
531
- shorthand: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
532
- keyframes: `0%, 100% { opacity: 1; }
533
- 50% { opacity: 0.5; }`
534
- },
535
- bounce: {
536
- shorthand: "bounce 1s infinite",
537
- keyframes: `0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
538
- 50% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }`
539
- },
540
- ping: {
541
- shorthand: "ping 1s cubic-bezier(0, 0, 0.2, 1) infinite",
542
- keyframes: "75%, 100% { transform: scale(2); opacity: 0; }"
543
- },
544
- // Disclosure animations for the @rainbowindex/ui Accordion + Collapsible
545
- // primitives. Both expand from 0 → the natural content size published by
546
- // the package's `useCollapseSize` hook as `--ri-collapsible-content-height`
547
- // (a single shared variable — the primitives do not emit separate accordion
548
- // vs collapsible vars). The `auto` fallback keeps the keyframe valid on the
549
- // first frame before the hook measures.
550
- "accordion-down": {
551
- shorthand: "accordion-down 0.2s ease-out",
552
- keyframes: `from { height: 0; }
553
- to { height: var(--ri-collapsible-content-height, auto); }`
554
- },
555
- "accordion-up": {
556
- shorthand: "accordion-up 0.2s ease-out",
557
- keyframes: `from { height: var(--ri-collapsible-content-height, auto); }
558
- to { height: 0; }`
559
- },
560
- "collapsible-down": {
561
- shorthand: "collapsible-down 0.2s ease-out",
562
- keyframes: `from { height: 0; }
563
- to { height: var(--ri-collapsible-content-height, auto); }`
564
- },
565
- "collapsible-up": {
566
- shorthand: "collapsible-up 0.2s ease-out",
567
- keyframes: `from { height: var(--ri-collapsible-content-height, auto); }
568
- to { height: 0; }`
569
- },
570
- "caret-blink": {
571
- shorthand: "caret-blink 1.25s ease-out infinite",
572
- keyframes: `0%, 70%, 100% { opacity: 1; }
573
- 20%, 50% { opacity: 0; }`
574
- }
575
- };
576
- var DEFAULT_FLUID = {
577
- min: "20rem",
578
- max: "80rem"
579
- };
580
- var DEFAULT_TRACKING = {
581
- tighter: "-0.05em",
582
- tight: "-0.025em",
583
- normal: "0em",
584
- wide: "0.025em",
585
- wider: "0.05em",
586
- widest: "0.1em"
587
- };
588
- var DEFAULT_LEADING = {
589
- "3": "0.75rem",
590
- "4": "1rem",
591
- "5": "1.25rem",
592
- "6": "1.5rem",
593
- "7": "1.75rem",
594
- "8": "2rem",
595
- "9": "2.25rem",
596
- "10": "2.5rem",
597
- none: "1",
598
- tight: "1.25",
599
- snug: "1.375",
600
- normal: "1.5",
601
- relaxed: "1.625",
602
- loose: "2"
603
- };
604
- var defaultTheme = Object.freeze({
605
- spacing: {
606
- base: "0.25rem"
607
- },
608
- colors: DEFAULT_COLORS,
609
- text: DEFAULT_TEXT,
610
- breakpoints: DEFAULT_BREAKPOINTS,
611
- rounded: DEFAULT_ROUNDED,
612
- shadows: DEFAULT_SHADOWS,
613
- weights: DEFAULT_WEIGHTS,
614
- easing: DEFAULT_EASING,
615
- fluid: DEFAULT_FLUID,
616
- animations: DEFAULT_ANIMATIONS,
617
- blur: DEFAULT_BLUR
618
- });
619
-
620
1
  // src/runtime.ts
621
2
  var IS_PROD = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
622
3
  var IS_DEV = !IS_PROD;
@@ -774,10 +155,11 @@ var BUILTIN_STATIC_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
774
155
  // outline shorthand + offset (claiming all sub-properties); outline-none just sets style.
775
156
  outline: ["outline-width"],
776
157
  "outline-hidden": ["outline-style", "outline-width", "outline-color", "outline-offset"],
777
- // Shadow / ring (static bare + reset forms; valued forms via PREFIX_PROPS).
158
+ // Shadow / ring (static reset + bare ring forms; valued forms via PREFIX_PROPS).
778
159
  // Each composable family claims box-shadow (shared) + its own slot var, so
779
160
  // shadow/inset-shadow/ring/inset-ring coexist while same-family repeats dedupe.
780
- shadow: ["box-shadow", "--ri-shadow"],
161
+ // Bare `shadow` is not static: with no default shadow scale it only resolves
162
+ // when the theme defines a `DEFAULT` (or `md`) token via @shadow.
781
163
  "shadow-none": ["box-shadow", "--ri-shadow"],
782
164
  ring: ["box-shadow", "--ri-ring-shadow"],
783
165
  "inset-ring": ["box-shadow", "--ri-inset-ring-shadow"],
@@ -1230,7 +612,7 @@ addAll(
1230
612
  ],
1231
613
  ["border-style"]
1232
614
  );
1233
- addAll(["rounded", "rounded-none", "rounded-full"], ["border-radius"]);
615
+ addAll(["rounded-none", "rounded-full"], ["border-radius"]);
1234
616
  addAll(
1235
617
  [
1236
618
  "corner-round",
@@ -1322,23 +704,7 @@ addAll(
1322
704
  ],
1323
705
  ["~divide:border-style"]
1324
706
  );
1325
- addAll(
1326
- [
1327
- "animate-spin",
1328
- "animate-pulse",
1329
- "animate-bounce",
1330
- "animate-ping",
1331
- "animate-in",
1332
- "animate-out",
1333
- "animate-none",
1334
- "animate-accordion-down",
1335
- "animate-accordion-up",
1336
- "animate-collapsible-down",
1337
- "animate-collapsible-up",
1338
- "animate-caret-blink"
1339
- ],
1340
- ["animation"]
1341
- );
707
+ addAll(["animate-in", "animate-out", "animate-none"], ["animation"]);
1342
708
  addAll(["animate-infinite", "animate-once", "animate-twice"], ["animation-iteration-count"]);
1343
709
  addAll(
1344
710
  ["animate-fill-none", "animate-fill-forwards", "animate-fill-both", "animate-fill-backwards"],
@@ -1541,6 +907,9 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1541
907
  ...MARGIN_MAP,
1542
908
  ...GAP_MAP,
1543
909
  ...FLUID_SPACING_PROPS,
910
+ // fluid-<name> scope classes all claim the same scope-var pair, so a later
911
+ // range wins over an earlier one.
912
+ fluid: ["--fluid-scope-min", "--fluid-scope-max"],
1544
913
  "space-x": ["~space:margin-inline-start", "~space:margin-inline-end"],
1545
914
  "space-y": ["~space:margin-block-start", "~space:margin-block-end"],
1546
915
  ...INSET_MAP,
@@ -1688,10 +1057,13 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1688
1057
  "from-position": ["--ri-gradient-from-position"],
1689
1058
  "via-position": ["--ri-gradient-via-position"],
1690
1059
  "to-position": ["--ri-gradient-to-position"],
1691
- // Animations
1060
+ // Animations. `animate` is last of the four by length, so the three
1061
+ // specific prefixes match first and `animate-{name}` catches every
1062
+ // @animate token and arbitrary shorthand.
1692
1063
  "animate-duration": ["animation-duration"],
1693
1064
  "animate-delay": ["animation-delay"],
1694
1065
  "animate-ease": ["animation-timing-function"],
1066
+ animate: ["animation"],
1695
1067
  "break-before": ["break-before"],
1696
1068
  "break-after": ["break-after"],
1697
1069
  "break-inside": ["break-inside"],
@@ -2042,17 +1414,6 @@ function scanBracketAware(input, onChar, options) {
2042
1414
  }
2043
1415
 
2044
1416
  // src/merge/resolve.ts
2045
- var DEFAULT_TEXT_SIZES = [
2046
- "xs",
2047
- "sm",
2048
- "base",
2049
- "lg",
2050
- "xl",
2051
- "2xl",
2052
- "3xl",
2053
- "4xl",
2054
- "5xl"
2055
- ];
2056
1417
  var DEFAULT_FONT_FAMILIES = ["sans", "serif", "mono"];
2057
1418
  function stripTextModifier(value) {
2058
1419
  if (value.indexOf("/") === -1) return value;
@@ -2182,7 +1543,7 @@ var DIRECTIONAL_BORDER_COLOR_PROPS = new Map(
2182
1543
  "border-y"
2183
1544
  ].map((prefix) => [prefix, Object.freeze([PREFIX_PROPS[prefix][0].replace("width", "color")])])
2184
1545
  );
2185
- function resolvePropsWith(utility, customStaticProps, textSizes, fontFamilies, colorNames) {
1546
+ function resolvePropsWith(utility, customStaticProps, customFunctionalProps, textSizes, fontFamilies, colorNames) {
2186
1547
  if (utility.charCodeAt(0) === 91) {
2187
1548
  const colonIdx = utility.indexOf(":");
2188
1549
  if (colonIdx !== -1) {
@@ -2198,6 +1559,10 @@ function resolvePropsWith(utility, customStaticProps, textSizes, fontFamilies, c
2198
1559
  if (Object.hasOwn(customStaticProps, name)) return customStaticProps[name];
2199
1560
  const builtin = BUILTIN_STATIC_PROPS[name];
2200
1561
  if (builtin !== void 0) return builtin;
1562
+ for (const [root, properties] of customFunctionalProps) {
1563
+ if (name.length <= root.length) continue;
1564
+ if (name.startsWith(root) && name.charCodeAt(root.length) === 45) return properties;
1565
+ }
2201
1566
  const firstDash = name.indexOf("-");
2202
1567
  const firstSeg = firstDash === -1 ? name : name.slice(0, firstDash);
2203
1568
  const candidates = PREFIX_FIRST_SEGMENT_MAP.get(firstSeg);
@@ -2223,20 +1588,62 @@ function resolvePropsWith(utility, customStaticProps, textSizes, fontFamilies, c
2223
1588
 
2224
1589
  // src/merge/context.ts
2225
1590
  var _customStaticProps = {};
2226
- var _textSizes = new Set(DEFAULT_TEXT_SIZES);
1591
+ var _customFunctionalProps = [];
1592
+ var _textSizes = /* @__PURE__ */ new Set();
2227
1593
  var _fontFamilies = new Set(DEFAULT_FONT_FAMILIES);
2228
1594
  var _colorNames = /* @__PURE__ */ new Set();
2229
1595
  var _latestSnapshot = null;
2230
1596
  function hasFinalizedSnapshot() {
2231
1597
  return _latestSnapshot !== null;
2232
1598
  }
2233
- var defaultRiCache = /* @__PURE__ */ new Map();
1599
+ var RI_CACHE_MAX = 500;
1600
+ var RiCache = class {
1601
+ constructor(maxSize) {
1602
+ this.maxSize = maxSize;
1603
+ }
1604
+ maxSize;
1605
+ current = /* @__PURE__ */ new Map();
1606
+ previous = /* @__PURE__ */ new Map();
1607
+ get(key) {
1608
+ const hit = this.current.get(key);
1609
+ if (hit !== void 0) return hit;
1610
+ const promoted = this.previous.get(key);
1611
+ if (promoted !== void 0) this.put(key, promoted);
1612
+ return promoted;
1613
+ }
1614
+ put(key, value) {
1615
+ this.current.set(key, value);
1616
+ if (this.current.size >= this.maxSize) {
1617
+ this.previous = this.current;
1618
+ this.current = /* @__PURE__ */ new Map();
1619
+ }
1620
+ }
1621
+ has(key) {
1622
+ return this.current.has(key) || this.previous.has(key);
1623
+ }
1624
+ get size() {
1625
+ return this.current.size + this.previous.size;
1626
+ }
1627
+ clear() {
1628
+ this.current.clear();
1629
+ this.previous.clear();
1630
+ }
1631
+ };
1632
+ var defaultRiCache = new RiCache(RI_CACHE_MAX);
2234
1633
  function resolveProps(utility) {
2235
- return resolvePropsWith(utility, _customStaticProps, _textSizes, _fontFamilies, _colorNames);
1634
+ return resolvePropsWith(
1635
+ utility,
1636
+ _customStaticProps,
1637
+ _customFunctionalProps,
1638
+ _textSizes,
1639
+ _fontFamilies,
1640
+ _colorNames
1641
+ );
2236
1642
  }
2237
1643
  function resolverFor(snapshot) {
2238
1644
  const snap = snapshot ?? _latestSnapshot ?? {
2239
1645
  customStaticProps: _customStaticProps,
1646
+ customFunctionalProps: _customFunctionalProps,
2240
1647
  textSizes: _textSizes,
2241
1648
  fontFamilies: _fontFamilies,
2242
1649
  colorNames: _colorNames
@@ -2244,6 +1651,7 @@ function resolverFor(snapshot) {
2244
1651
  return (utility) => resolvePropsWith(
2245
1652
  utility,
2246
1653
  snap.customStaticProps,
1654
+ snap.customFunctionalProps,
2247
1655
  snap.textSizes,
2248
1656
  snap.fontFamilies,
2249
1657
  snap.colorNames
@@ -2252,12 +1660,13 @@ function resolverFor(snapshot) {
2252
1660
  function createCompilationContext() {
2253
1661
  return {
2254
1662
  customStaticProps: {},
2255
- textSizes: new Set(DEFAULT_TEXT_SIZES),
1663
+ customFunctionalProps: {},
1664
+ textSizes: /* @__PURE__ */ new Set(),
2256
1665
  fontFamilies: new Set(DEFAULT_FONT_FAMILIES),
2257
1666
  colorNames: /* @__PURE__ */ new Set()
2258
1667
  };
2259
1668
  }
2260
- function registerCustomUtility(ctx, name, properties) {
1669
+ function registerCustomUtility(ctx, name, properties, functional = false) {
2261
1670
  if (IS_DEV) {
2262
1671
  if (!name) {
2263
1672
  devWarn("[RI-1301] registerCustomUtility() called with empty name \u2014 skipping.");
@@ -2269,7 +1678,8 @@ function registerCustomUtility(ctx, name, properties) {
2269
1678
  );
2270
1679
  }
2271
1680
  }
2272
- ctx.customStaticProps[name] = properties;
1681
+ if (functional) ctx.customFunctionalProps[name] = properties;
1682
+ else ctx.customStaticProps[name] = properties;
2273
1683
  }
2274
1684
  function registerCustomTextSizes(ctx, sizes) {
2275
1685
  for (const s of sizes) ctx.textSizes.add(s);
@@ -2285,6 +1695,7 @@ function snapshotCompilationContext(ctx) {
2285
1695
  customStaticProps: Object.fromEntries(
2286
1696
  Object.entries(ctx.customStaticProps).map(([k, v]) => [k, [...v]])
2287
1697
  ),
1698
+ customFunctionalProps: Object.entries(ctx.customFunctionalProps).map(([k, v]) => [k, [...v]]).sort((a, b) => b[0].length - a[0].length),
2288
1699
  textSizes: new Set(ctx.textSizes),
2289
1700
  fontFamilies: new Set(ctx.fontFamilies),
2290
1701
  colorNames: new Set(ctx.colorNames)
@@ -2293,6 +1704,7 @@ function snapshotCompilationContext(ctx) {
2293
1704
  function finalizeCompilationContext(ctx) {
2294
1705
  const snapshot = snapshotCompilationContext(ctx);
2295
1706
  _customStaticProps = snapshot.customStaticProps;
1707
+ _customFunctionalProps = snapshot.customFunctionalProps;
2296
1708
  _textSizes = snapshot.textSizes;
2297
1709
  _fontFamilies = snapshot.fontFamilies;
2298
1710
  _colorNames = snapshot.colorNames;
@@ -2302,7 +1714,6 @@ function finalizeCompilationContext(ctx) {
2302
1714
  }
2303
1715
 
2304
1716
  // src/merge/index.ts
2305
- var RI_CACHE_MAX = 500;
2306
1717
  var RI_CACHE_KEY_MAX_LEN = 2048;
2307
1718
  var WS_SPLIT_RE = /\s+/;
2308
1719
  function findVariantSplit(cls) {
@@ -2381,231 +1792,635 @@ function mergeUncached(classes, resolve, trace) {
2381
1792
  }
2382
1793
  }
2383
1794
  }
2384
- result.push(cls);
1795
+ result.push(cls);
1796
+ }
1797
+ result.reverse();
1798
+ return result.join(" ");
1799
+ }
1800
+ function mergeClasses(classes, resolve, cache) {
1801
+ const cacheKey = classes.join("\0");
1802
+ const useCache = cacheKey.length <= RI_CACHE_KEY_MAX_LEN;
1803
+ if (useCache) {
1804
+ const cached = cache.get(cacheKey);
1805
+ if (cached !== void 0) return cached;
1806
+ }
1807
+ const output = mergeUncached(classes, resolve);
1808
+ if (useCache) cache.put(cacheKey, output);
1809
+ return output;
1810
+ }
1811
+ function mergeFrom(inputs, resolve, cache) {
1812
+ let fastPath = true;
1813
+ let rawKey = "";
1814
+ for (let i = 0; i < inputs.length; i++) {
1815
+ const input = inputs[i];
1816
+ if (typeof input === "string") {
1817
+ if (input !== "") rawKey = rawKey === "" ? input : `${rawKey}\0${input}`;
1818
+ } else if (input) {
1819
+ fastPath = false;
1820
+ break;
1821
+ }
1822
+ }
1823
+ if (fastPath) {
1824
+ if (rawKey === "") return "";
1825
+ if (rawKey.length <= RI_CACHE_KEY_MAX_LEN) {
1826
+ const cached = cache.get(rawKey);
1827
+ if (cached !== void 0) return cached;
1828
+ const classes2 = flattenInputs(inputs);
1829
+ const output = classes2.length === 0 ? "" : classes2.length === 1 ? classes2[0] : mergeUncached(classes2, resolve);
1830
+ cache.put(rawKey, output);
1831
+ return output;
1832
+ }
1833
+ }
1834
+ const classes = flattenInputs(inputs);
1835
+ if (classes.length === 0) return "";
1836
+ if (classes.length === 1) return classes[0];
1837
+ return mergeClasses(classes, resolve, cache);
1838
+ }
1839
+ var _isSSR = null;
1840
+ function detectSSR() {
1841
+ if (_isSSR !== null) return _isSSR;
1842
+ _isSSR = typeof window === "undefined" && typeof process !== "undefined" && !!process.versions?.node;
1843
+ return _isSSR;
1844
+ }
1845
+ var WARNING_THROTTLE_MS = 6e4;
1846
+ function throttledWarn(message, emit = console.warn) {
1847
+ let last = Number.NEGATIVE_INFINITY;
1848
+ return (...args) => {
1849
+ const now = Date.now();
1850
+ if (now - last >= WARNING_THROTTLE_MS) {
1851
+ last = now;
1852
+ emit(message(...args));
1853
+ }
1854
+ };
1855
+ }
1856
+ var warnSSRUsage = throttledWarn(
1857
+ () => !hasFinalizedSnapshot() ? (
1858
+ // No compilation has ever finalized — module-level state is uninitialized
1859
+ // defaults. In SSR this almost certainly means ri() is being called without
1860
+ // a preceding compile pass, which will produce incorrect merge results
1861
+ // for custom utilities, text sizes, and font families.
1862
+ "[RI-2004] ri() called in an SSR environment before any compilation has finalized. The default ri() export reads module-level state that has not been initialized. Custom utilities, text sizes, and font families will not be recognized. Use createRi(snapshot) for concurrent-safe class merging. See: https://rainbowindex.dev/docs/ssr"
1863
+ ) : "[RI-2004] The default ri() export uses module-level state and is not safe for concurrent SSR requests. Use createRi(snapshot) for isolation. See: https://rainbowindex.dev/docs/ssr\nThis warning repeats every 60 s until resolved. In production SSR, switch to createRi(snapshot) to prevent silent data corruption between concurrent requests."
1864
+ );
1865
+ var warnFlattenDepth = throttledWarn(
1866
+ () => `[RI-2011] ri() input nesting exceeds maximum depth of ${MAX_FLATTEN_DEPTH}. Deeply nested inputs are silently dropped. Flatten your class arrays to avoid this limit.`
1867
+ );
1868
+ var warnTotalClasses = throttledWarn(
1869
+ () => `[RI-2012] ri() input exceeds ${MAX_TOTAL_CLASSES} class limit. Excess classes are dropped to prevent memory exhaustion.`
1870
+ );
1871
+ var warnClassLength = throttledWarn(
1872
+ (cls) => `[RI-2006] Class name exceeds ${MAX_CLASS_NAME_LENGTH} character limit and was dropped: "${cls.slice(0, 40)}\u2026". This is a safety guard against adversarial input in SSR. If this is intentional, shorten the class name.`
1873
+ );
1874
+ var warnNonStringInput = throttledWarn(
1875
+ (input) => `ri() inputs must be strings, arrays, or falsy \u2014 got ${typeof input}; value skipped. Object syntax ({ class: condition }) is not supported; use \`condition && "class"\` instead.`,
1876
+ devWarn
1877
+ );
1878
+ function ri(...inputs) {
1879
+ if (detectSSR()) {
1880
+ warnSSRUsage();
1881
+ }
1882
+ return mergeFrom(inputs, resolveProps, defaultRiCache);
1883
+ }
1884
+ function createRi(snapshot) {
1885
+ const resolve = resolverFor(snapshot);
1886
+ const cache = new RiCache(RI_CACHE_MAX);
1887
+ return function boundRi(...inputs) {
1888
+ return mergeFrom(inputs, resolve, cache);
1889
+ };
1890
+ }
1891
+ var MAX_FLATTEN_DEPTH = 10;
1892
+ var MAX_CLASS_NAME_LENGTH = 500;
1893
+ var MAX_TOTAL_CLASSES = 1e4;
1894
+ function flattenInputs(inputs) {
1895
+ const result = [];
1896
+ const stack = [[inputs, 0, 0]];
1897
+ while (stack.length > 0) {
1898
+ const top = stack[stack.length - 1];
1899
+ const [arr, , depth] = top;
1900
+ if (top[1] >= arr.length) {
1901
+ stack.pop();
1902
+ continue;
1903
+ }
1904
+ const input = arr[top[1]++];
1905
+ if (!input) continue;
1906
+ if (Array.isArray(input)) {
1907
+ if (depth + 1 > MAX_FLATTEN_DEPTH) {
1908
+ warnFlattenDepth();
1909
+ continue;
1910
+ }
1911
+ stack.push([input, 0, depth + 1]);
1912
+ } else if (typeof input !== "string") {
1913
+ if (IS_DEV) {
1914
+ warnNonStringInput(input);
1915
+ }
1916
+ } else {
1917
+ const trimmed = input.trim();
1918
+ if (trimmed) {
1919
+ for (const cls of splitBracketAware(trimmed)) {
1920
+ if (result.length >= MAX_TOTAL_CLASSES) {
1921
+ warnTotalClasses();
1922
+ return result;
1923
+ }
1924
+ if (cls.length <= MAX_CLASS_NAME_LENGTH) {
1925
+ result.push(cls);
1926
+ } else {
1927
+ warnClassLength(cls);
1928
+ }
1929
+ }
1930
+ }
1931
+ }
1932
+ }
1933
+ return result;
1934
+ }
1935
+ function splitBracketAware(input) {
1936
+ if (!input.includes("[") && !input.includes("(")) {
1937
+ const trimmed = input.trim();
1938
+ return trimmed === "" ? [] : trimmed.split(WS_SPLIT_RE);
1939
+ }
1940
+ const result = [];
1941
+ let tokenStart = -1;
1942
+ let depth = 0;
1943
+ for (let i = 0; i < input.length; i++) {
1944
+ const ch = input[i];
1945
+ if (ch === "\\" && i + 1 < input.length) {
1946
+ if (tokenStart === -1) tokenStart = i;
1947
+ i++;
1948
+ continue;
1949
+ }
1950
+ if (ch === "[" || ch === "(") {
1951
+ depth++;
1952
+ if (tokenStart === -1) tokenStart = i;
1953
+ } else if (ch === "]" || ch === ")") {
1954
+ if (depth > 0) depth--;
1955
+ if (tokenStart === -1) tokenStart = i;
1956
+ } else if (depth === 0 && (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f")) {
1957
+ if (tokenStart !== -1) {
1958
+ result.push(input.slice(tokenStart, i));
1959
+ tokenStart = -1;
1960
+ }
1961
+ } else {
1962
+ if (tokenStart === -1) tokenStart = i;
1963
+ }
1964
+ }
1965
+ if (tokenStart !== -1) result.push(input.slice(tokenStart));
1966
+ return result;
1967
+ }
1968
+
1969
+ // src/theme/colors.ts
1970
+ function isValidColorSuffix(n) {
1971
+ return Number.isInteger(n) && n >= 1 && n <= 999;
1972
+ }
1973
+ var STOP_LO = 50;
1974
+ var STOP_STEP = 50;
1975
+ var L_PROFILE = [
1976
+ 0.9668,
1977
+ // 50
1978
+ 0.936756,
1979
+ // 100
1980
+ 0.905984,
1981
+ // 150
1982
+ 0.87442,
1983
+ // 200
1984
+ 0.841988,
1985
+ // 250
1986
+ 0.808602,
1987
+ // 300
1988
+ 0.774158,
1989
+ // 350
1990
+ 0.73853,
1991
+ // 400
1992
+ 0.701568,
1993
+ // 450
1994
+ 0.663082,
1995
+ // 500
1996
+ 0.622832,
1997
+ // 550
1998
+ 0.580504,
1999
+ // 600
2000
+ 0.535677,
2001
+ // 650
2002
+ 0.487755,
2003
+ // 700
2004
+ 0.435847,
2005
+ // 750
2006
+ 0.378506,
2007
+ // 800
2008
+ 0.313034,
2009
+ // 850
2010
+ 0.232988,
2011
+ // 900
2012
+ 0.045
2013
+ // 950
2014
+ ];
2015
+ var REF_CHROMA = 0.05;
2016
+ var C_REF = [
2017
+ 0.006863863034589374,
2018
+ // 50
2019
+ 0.01319862716849036,
2020
+ // 100
2021
+ 0.019388384668336325,
2022
+ // 150
2023
+ 0.025429811853816625,
2024
+ // 200
2025
+ 0.03131716290796655,
2026
+ // 250
2027
+ 0.03704281264548738,
2028
+ // 300
2029
+ 0.04259815284255128,
2030
+ // 350
2031
+ 0.04797259847240491,
2032
+ // 400
2033
+ 0.053139652622711774,
2034
+ // 450
2035
+ 0.05807254410984486,
2036
+ // 500
2037
+ 0.057890440330825195,
2038
+ // 550
2039
+ 0.05222950205066024,
2040
+ // 600
2041
+ 0.04663166172903349,
2042
+ // 650
2043
+ 0.04108661482545571,
2044
+ // 700
2045
+ 0.035584106512529856,
2046
+ // 750
2047
+ 0.030108630901677546,
2048
+ // 800
2049
+ 0.02463007273923548,
2050
+ // 850
2051
+ 0.01911062262055484,
2052
+ // 900
2053
+ 0.013470720217073236
2054
+ // 950
2055
+ ];
2056
+ var H_DRIFT = [
2057
+ -0.93815866691077,
2058
+ // 50
2059
+ -0.88308454198904,
2060
+ // 100
2061
+ -0.82986570023714,
2062
+ // 150
2063
+ -0.77925524218887,
2064
+ // 200
2065
+ -0.73231968077941,
2066
+ // 250
2067
+ -0.69055370894147,
2068
+ // 300
2069
+ -0.65608113228225,
2070
+ // 350
2071
+ -0.6319804472577,
2072
+ // 400
2073
+ -0.62282301825498,
2074
+ // 450
2075
+ -0.63542113918918,
2076
+ // 500
2077
+ -0.65047025648765,
2078
+ // 550
2079
+ -0.64293938410323,
2080
+ // 600
2081
+ -0.63626978185344,
2082
+ // 650
2083
+ -0.63057786221076,
2084
+ // 700
2085
+ -0.62611168444876,
2086
+ // 750
2087
+ -0.62332854799372,
2088
+ // 800
2089
+ -0.62317507759171,
2090
+ // 850
2091
+ -0.62789446720379,
2092
+ // 900
2093
+ -0.64378251527182
2094
+ // 950
2095
+ ];
2096
+ function sampleProfile(values, suffix, monotone) {
2097
+ const last = values.length - 1;
2098
+ const fx = (suffix - STOP_LO) / STOP_STEP;
2099
+ if (fx <= 0) return values[0] + (values[1] - values[0]) * fx;
2100
+ if (fx >= last) return values[last] + (values[last] - values[last - 1]) * (fx - last);
2101
+ const i = Math.floor(fx);
2102
+ const t = fx - i;
2103
+ if (t === 0) return values[i];
2104
+ const seg = values[i + 1] - values[i];
2105
+ const tangent = (k) => {
2106
+ if (k <= 0) return values[1] - values[0];
2107
+ if (k >= last) return values[last] - values[last - 1];
2108
+ return (values[k + 1] - values[k - 1]) / 2;
2109
+ };
2110
+ let m0 = tangent(i);
2111
+ let m1 = tangent(i + 1);
2112
+ if (monotone) {
2113
+ if (seg === 0) {
2114
+ m0 = 0;
2115
+ m1 = 0;
2116
+ } else {
2117
+ const a = m0 / seg;
2118
+ const b = m1 / seg;
2119
+ const s = a * a + b * b;
2120
+ if (s > 9) {
2121
+ const tau = 3 / Math.sqrt(s);
2122
+ m0 = tau * a * seg;
2123
+ m1 = tau * b * seg;
2124
+ }
2125
+ }
2385
2126
  }
2386
- result.reverse();
2387
- return result.join(" ");
2127
+ const t2 = t * t;
2128
+ const t3 = t2 * t;
2129
+ return (2 * t3 - 3 * t2 + 1) * values[i] + (t3 - 2 * t2 + t) * m0 + (-2 * t3 + 3 * t2) * values[i + 1] + (t3 - t2) * m1;
2388
2130
  }
2389
- function evictLRU(cache, maxSize) {
2390
- if (cache.size < maxSize) return;
2391
- const evictCount = maxSize >> 2;
2392
- let count = 0;
2393
- for (const key of cache.keys()) {
2394
- if (count >= evictCount) break;
2395
- cache.delete(key);
2396
- count++;
2397
- }
2131
+ function lightnessFromSuffix(suffix) {
2132
+ return Math.min(0.98, Math.max(0.02, sampleProfile(L_PROFILE, suffix, true)));
2398
2133
  }
2399
- function lruGet(cache, key) {
2400
- const cached = cache.get(key);
2401
- if (cached !== void 0) {
2402
- cache.delete(key);
2403
- cache.set(key, cached);
2134
+ var DEFAULT_COLORS = Object.freeze({
2135
+ theme: {
2136
+ type: "generative",
2137
+ chroma: 0,
2138
+ hue: 0,
2139
+ parabolic: false,
2140
+ chromaBoost: false
2404
2141
  }
2405
- return cached;
2142
+ });
2143
+ var DEFAULT_DARK_CONFIG = {
2144
+ mode: "auto",
2145
+ // 0 → dark keeps the light stop's chroma exactly (same tone in both modes).
2146
+ // Raise it via `@color dark { chroma-boost }` for punchier dark-mode colors.
2147
+ chromaBoost: 0,
2148
+ hueShift: 0
2149
+ };
2150
+ var GAMUT_CLAMP_ITERATIONS = 20;
2151
+ function oklchToOklab(l, c, h) {
2152
+ const hRad = h * Math.PI / 180;
2153
+ return [l, c * Math.cos(hRad), c * Math.sin(hRad)];
2406
2154
  }
2407
- function lruPut(cache, key, value) {
2408
- evictLRU(cache, RI_CACHE_MAX);
2409
- cache.set(key, value);
2155
+ function oklabToLinearSrgb(l, a, b) {
2156
+ const l_ = l + 0.3963377774 * a + 0.2158037573 * b;
2157
+ const m_ = l - 0.1055613458 * a - 0.0638541728 * b;
2158
+ const s_ = l - 0.0894841775 * a - 1.291485548 * b;
2159
+ const lc = l_ * l_ * l_;
2160
+ const mc = m_ * m_ * m_;
2161
+ const sc = s_ * s_ * s_;
2162
+ const r = 4.0767416621 * lc - 3.3077115913 * mc + 0.2309699292 * sc;
2163
+ const g = -1.2684380046 * lc + 2.6097574011 * mc - 0.3413193965 * sc;
2164
+ const bv = -0.0041960863 * lc - 0.7034186147 * mc + 1.707614701 * sc;
2165
+ return [r, g, bv];
2410
2166
  }
2411
- function mergeClasses(classes, resolve, cache) {
2412
- const cacheKey = classes.join("\0");
2413
- const useCache = cacheKey.length <= RI_CACHE_KEY_MAX_LEN;
2414
- if (useCache) {
2415
- const cached = lruGet(cache, cacheKey);
2416
- if (cached !== void 0) return cached;
2417
- }
2418
- const output = mergeUncached(classes, resolve);
2419
- if (useCache) lruPut(cache, cacheKey, output);
2420
- return output;
2167
+ function isInSrgbGamut(l, c, h) {
2168
+ const [la, a, b] = oklchToOklab(l, c, h);
2169
+ const [r, g, bv] = oklabToLinearSrgb(la, a, b);
2170
+ const eps = 1e-3;
2171
+ return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && bv >= -eps && bv <= 1 + eps;
2421
2172
  }
2422
- function mergeFrom(inputs, resolve, cache) {
2423
- let fastPath = true;
2424
- let rawKey = "";
2425
- for (let i = 0; i < inputs.length; i++) {
2426
- const input = inputs[i];
2427
- if (typeof input === "string") {
2428
- if (input !== "") rawKey = rawKey === "" ? input : `${rawKey}\0${input}`;
2429
- } else if (input) {
2430
- fastPath = false;
2431
- break;
2432
- }
2433
- }
2434
- if (fastPath) {
2435
- if (rawKey === "") return "";
2436
- if (rawKey.length <= RI_CACHE_KEY_MAX_LEN) {
2437
- const cached = lruGet(cache, rawKey);
2438
- if (cached !== void 0) return cached;
2439
- const classes2 = flattenInputs(inputs);
2440
- const output = classes2.length === 0 ? "" : classes2.length === 1 ? classes2[0] : mergeUncached(classes2, resolve);
2441
- lruPut(cache, rawKey, output);
2442
- return output;
2173
+ function gamutSafeChroma(l, requestedChroma, h) {
2174
+ if (requestedChroma <= 0) return 0;
2175
+ if (isInSrgbGamut(l, requestedChroma, h)) return requestedChroma;
2176
+ let lo = 0;
2177
+ let hi = requestedChroma;
2178
+ for (let i = 0; i < GAMUT_CLAMP_ITERATIONS; i++) {
2179
+ const mid = (lo + hi) / 2;
2180
+ if (isInSrgbGamut(l, mid, h)) {
2181
+ lo = mid;
2182
+ } else {
2183
+ hi = mid;
2443
2184
  }
2185
+ if (hi - lo < 5e-4) break;
2444
2186
  }
2445
- const classes = flattenInputs(inputs);
2446
- if (classes.length === 0) return "";
2447
- if (classes.length === 1) return classes[0];
2448
- return mergeClasses(classes, resolve, cache);
2187
+ return lo;
2449
2188
  }
2450
- var _isSSR = null;
2451
- function detectSSR() {
2452
- if (_isSSR !== null) return _isSSR;
2453
- _isSSR = typeof window === "undefined" && typeof process !== "undefined" && !!process.versions?.node;
2454
- return _isSSR;
2189
+ var APCA_MAIN_TRC = 2.4;
2190
+ var APCA_NORM_BG = 0.56;
2191
+ var APCA_NORM_TXT = 0.57;
2192
+ var APCA_REV_TXT = 0.62;
2193
+ var APCA_REV_BG = 0.65;
2194
+ var APCA_BLK_THRS = 0.022;
2195
+ var APCA_BLK_CLMP = Math.SQRT2;
2196
+ var APCA_SCALE = 1.14;
2197
+ var APCA_LO_BOW_OFFSET = 0.027;
2198
+ var APCA_LO_WOB_OFFSET = 0.027;
2199
+ var APCA_DELTA_Y_MIN = 5e-4;
2200
+ var APCA_LO_CLIP = 0.1;
2201
+ function linearToSrgb(c) {
2202
+ const x = Math.max(0, Math.min(1, c));
2203
+ return x <= 31308e-7 ? 12.92 * x : 1.055 * x ** (1 / 2.4) - 0.055;
2455
2204
  }
2456
- var WARNING_THROTTLE_MS = 6e4;
2457
- function throttledWarn(message, emit = console.warn) {
2458
- let last = Number.NEGATIVE_INFINITY;
2459
- return (...args) => {
2460
- const now = Date.now();
2461
- if (now - last >= WARNING_THROTTLE_MS) {
2462
- last = now;
2463
- emit(message(...args));
2464
- }
2465
- };
2205
+ function oklchToApcaY(l, c, h) {
2206
+ const [la, a, b] = oklchToOklab(l, c, h);
2207
+ const [lr, lg, lb] = oklabToLinearSrgb(la, a, b);
2208
+ const r = linearToSrgb(lr);
2209
+ const g = linearToSrgb(lg);
2210
+ const bv = linearToSrgb(lb);
2211
+ return 0.2126 * r ** APCA_MAIN_TRC + 0.7152 * g ** APCA_MAIN_TRC + 0.0722 * bv ** APCA_MAIN_TRC;
2466
2212
  }
2467
- var warnSSRUsage = throttledWarn(
2468
- () => !hasFinalizedSnapshot() ? (
2469
- // No compilation has ever finalized — module-level state is uninitialized
2470
- // defaults. In SSR this almost certainly means ri() is being called without
2471
- // a preceding compile pass, which will produce incorrect merge results
2472
- // for custom utilities, text sizes, and font families.
2473
- "[RI-2004] ri() called in an SSR environment before any compilation has finalized. The default ri() export reads module-level state that has not been initialized. Custom utilities, text sizes, and font families will not be recognized. Use createRi(snapshot) for concurrent-safe class merging. See: https://rainbowindex.dev/docs/ssr"
2474
- ) : "[RI-2004] The default ri() export uses module-level state and is not safe for concurrent SSR requests. Use createRi(snapshot) for isolation. See: https://rainbowindex.dev/docs/ssr\nThis warning repeats every 60 s until resolved. In production SSR, switch to createRi(snapshot) to prevent silent data corruption between concurrent requests."
2475
- );
2476
- var warnFlattenDepth = throttledWarn(
2477
- () => `[RI-2011] ri() input nesting exceeds maximum depth of ${MAX_FLATTEN_DEPTH}. Deeply nested inputs are silently dropped. Flatten your class arrays to avoid this limit.`
2478
- );
2479
- var warnTotalClasses = throttledWarn(
2480
- () => `[RI-2012] ri() input exceeds ${MAX_TOTAL_CLASSES} class limit. Excess classes are dropped to prevent memory exhaustion.`
2481
- );
2482
- var warnClassLength = throttledWarn(
2483
- (cls) => `[RI-2006] Class name exceeds ${MAX_CLASS_NAME_LENGTH} character limit and was dropped: "${cls.slice(0, 40)}\u2026". This is a safety guard against adversarial input in SSR. If this is intentional, shorten the class name.`
2484
- );
2485
- var warnNonStringInput = throttledWarn(
2486
- (input) => `ri() inputs must be strings, arrays, or falsy \u2014 got ${typeof input}; value skipped. Object syntax ({ class: condition }) is not supported; use \`condition && "class"\` instead.`,
2487
- devWarn
2488
- );
2489
- function ri(...inputs) {
2490
- if (detectSSR()) {
2491
- warnSSRUsage();
2213
+ function apcaContrast(yText, yBg) {
2214
+ if (yText < 0 || yBg < 0) return 0;
2215
+ if (Math.max(yText, yBg) < APCA_BLK_THRS) return 0;
2216
+ const txtY = yText < APCA_BLK_THRS ? yText + (APCA_BLK_THRS - yText) ** APCA_BLK_CLMP : yText;
2217
+ const bgY = yBg < APCA_BLK_THRS ? yBg + (APCA_BLK_THRS - yBg) ** APCA_BLK_CLMP : yBg;
2218
+ if (Math.abs(bgY - txtY) < APCA_DELTA_Y_MIN) return 0;
2219
+ if (bgY > txtY) {
2220
+ const sapc2 = (bgY ** APCA_NORM_BG - txtY ** APCA_NORM_TXT) * APCA_SCALE;
2221
+ return sapc2 < APCA_LO_CLIP ? 0 : (sapc2 - APCA_LO_BOW_OFFSET) * 100;
2222
+ }
2223
+ const sapc = (bgY ** APCA_REV_BG - txtY ** APCA_REV_TXT) * APCA_SCALE;
2224
+ return sapc > -APCA_LO_CLIP ? 0 : (sapc + APCA_LO_WOB_OFFSET) * 100;
2225
+ }
2226
+ var _stopMemo = /* @__PURE__ */ new WeakMap();
2227
+ function generateStop(def, suffix) {
2228
+ let bySuffix = _stopMemo.get(def);
2229
+ if (!bySuffix) {
2230
+ bySuffix = /* @__PURE__ */ new Map();
2231
+ _stopMemo.set(def, bySuffix);
2232
+ }
2233
+ const cached = bySuffix.get(suffix);
2234
+ if (cached) return cached;
2235
+ const stop = computeStop(def, suffix);
2236
+ bySuffix.set(suffix, stop);
2237
+ return stop;
2238
+ }
2239
+ function computeStop(def, suffix) {
2240
+ const roundedL = Math.round(lightnessFromSuffix(suffix) * 1e3) / 1e3;
2241
+ const h = def.chroma > 0 ? def.hue + sampleProfile(H_DRIFT, suffix, false) : def.hue;
2242
+ const roundedH = Math.round(h * 1e3) / 1e3;
2243
+ const shapedChroma = def.parabolic === false ? def.chroma : def.chroma * sampleProfile(C_REF, suffix, false) / REF_CHROMA;
2244
+ const safeC = gamutSafeChroma(roundedL, shapedChroma, roundedH);
2245
+ const roundedC = Math.floor(safeC * 1e4) / 1e4;
2246
+ return {
2247
+ stop: suffix,
2248
+ l: roundedL,
2249
+ c: roundedC,
2250
+ h: roundedH
2251
+ };
2252
+ }
2253
+ function computeDarkStop(def, suffix, darkConfig, darkOverride) {
2254
+ const lightStop = generateStop(def, suffix);
2255
+ const darkL = generateStop(def, 1e3 - suffix).l;
2256
+ let extraChroma = 0;
2257
+ let extraHue = 0;
2258
+ if (darkOverride?.strategy === "shift") {
2259
+ extraChroma = darkOverride.chromaDelta;
2260
+ extraHue = darkOverride.hueDelta;
2492
2261
  }
2493
- return mergeFrom(inputs, resolveProps, defaultRiCache);
2262
+ const darkH = lightStop.h + darkConfig.hueShift + extraHue;
2263
+ const globalChromaBoost = def.chromaBoost !== false ? darkConfig.chromaBoost : 0;
2264
+ const requestedChroma = lightStop.c + globalChromaBoost + extraChroma;
2265
+ const darkC = Math.floor(gamutSafeChroma(darkL, requestedChroma, darkH) * 1e4) / 1e4;
2266
+ return { stop: suffix, l: darkL, c: darkC, h: darkH };
2494
2267
  }
2495
- function createRi(snapshot) {
2496
- const resolve = resolverFor(snapshot);
2497
- const cache = /* @__PURE__ */ new Map();
2498
- return function boundRi(...inputs) {
2499
- return mergeFrom(inputs, resolve, cache);
2500
- };
2268
+ function formatOklch(l, c, h) {
2269
+ return `oklch(${l} ${c} ${h})`;
2501
2270
  }
2502
- var MAX_FLATTEN_DEPTH = 10;
2503
- var MAX_CLASS_NAME_LENGTH = 500;
2504
- var MAX_TOTAL_CLASSES = 1e4;
2505
- function flattenInputs(inputs) {
2506
- const result = [];
2507
- const stack = [[inputs, 0, 0]];
2508
- while (stack.length > 0) {
2509
- const top = stack[stack.length - 1];
2510
- const [arr, , depth] = top;
2511
- if (top[1] >= arr.length) {
2512
- stack.pop();
2513
- continue;
2271
+ function generateColorVariables(name, def, suffixes, darkConfig = DEFAULT_DARK_CONFIG, darkOverride) {
2272
+ const vars = [];
2273
+ const strategy = darkOverride?.strategy ?? "mirror";
2274
+ for (const suffix of suffixes) {
2275
+ const lightStop = generateStop(def, suffix);
2276
+ const lightValue = formatOklch(lightStop.l, lightStop.c, lightStop.h);
2277
+ if (darkConfig.mode === "off" || strategy === "fixed") {
2278
+ if (darkConfig.mode === "off") {
2279
+ vars.push(`--color-${name}-${suffix}: ${lightValue};`);
2280
+ } else {
2281
+ vars.push(`--color-${name}-${suffix}: light-dark(${lightValue}, ${lightValue});`);
2282
+ }
2283
+ } else {
2284
+ const darkStop = computeDarkStop(def, suffix, darkConfig, darkOverride);
2285
+ const darkValue = formatOklch(darkStop.l, darkStop.c, darkStop.h);
2286
+ vars.push(`--color-${name}-${suffix}: light-dark(${lightValue}, ${darkValue});`);
2514
2287
  }
2515
- const input = arr[top[1]++];
2516
- if (!input) continue;
2517
- if (Array.isArray(input)) {
2518
- if (depth + 1 > MAX_FLATTEN_DEPTH) {
2519
- warnFlattenDepth();
2520
- continue;
2288
+ }
2289
+ return vars;
2290
+ }
2291
+ var SEMANTIC_COLORS = Object.freeze([
2292
+ "--color-paper: light-dark(oklch(1 0 0), oklch(0 0 0));",
2293
+ "--color-ink: light-dark(oklch(0 0 0), oklch(1 0 0));"
2294
+ ]);
2295
+ var DEFAULT_PALETTE_CONTRAST_LC = 60;
2296
+ function checkPaletteContrast(colors, usedColorStops, options = {}) {
2297
+ const minLc = options.minLc ?? DEFAULT_PALETTE_CONTRAST_LC;
2298
+ const warnings = [];
2299
+ const paperY = oklchToApcaY(1, 0, 0);
2300
+ const inkY = oklchToApcaY(0, 0, 0);
2301
+ for (const [name, def] of Object.entries(colors)) {
2302
+ if (def.type !== "generative") continue;
2303
+ const stops = usedColorStops.get(name);
2304
+ if (!stops || stops.size === 0) continue;
2305
+ const sorted = [...stops].sort((a, b) => a - b);
2306
+ for (const suffix of sorted) {
2307
+ const stop = generateStop(def, suffix);
2308
+ const fgY = oklchToApcaY(stop.l, stop.c, stop.h);
2309
+ const lcPaper = Math.abs(apcaContrast(fgY, paperY));
2310
+ const lcInk = Math.abs(apcaContrast(fgY, inkY));
2311
+ const best = Math.max(lcPaper, lcInk);
2312
+ if (best < minLc) {
2313
+ warnings.push(
2314
+ `[RI-1106] @color "${name}-${suffix}" has low APCA contrast (best |Lc| ${best.toFixed(0)} < ${minLc}; paper ${lcPaper.toFixed(0)}, ink ${lcInk.toFixed(0)}). Stop is unsuitable as text on either --color-paper or --color-ink \u2014 use a darker (lower suffix) or lighter (higher suffix) variant for text roles.`
2315
+ );
2521
2316
  }
2522
- stack.push([input, 0, depth + 1]);
2523
- } else if (typeof input !== "string") {
2524
- if (IS_DEV) {
2525
- warnNonStringInput(input);
2317
+ }
2318
+ }
2319
+ return warnings;
2320
+ }
2321
+ function generateAllColorVariables(colors = DEFAULT_COLORS, darkConfig = DEFAULT_DARK_CONFIG, usedColorStops) {
2322
+ const allVars = [];
2323
+ allVars.push(...SEMANTIC_COLORS);
2324
+ for (const [name, def] of Object.entries(colors)) {
2325
+ switch (def.type) {
2326
+ case "generative": {
2327
+ const suffixes = usedColorStops?.get(name);
2328
+ if (!suffixes || suffixes.size === 0) break;
2329
+ const sorted = [...suffixes].sort((a, b) => a - b);
2330
+ allVars.push(...generateColorVariables(name, def, sorted, darkConfig, def.dark));
2331
+ break;
2526
2332
  }
2527
- } else {
2528
- const trimmed = input.trim();
2529
- if (trimmed) {
2530
- for (const cls of splitBracketAware(trimmed)) {
2531
- if (result.length >= MAX_TOTAL_CLASSES) {
2532
- warnTotalClasses();
2533
- return result;
2534
- }
2535
- if (cls.length <= MAX_CLASS_NAME_LENGTH) {
2536
- result.push(cls);
2537
- } else {
2538
- warnClassLength(cls);
2333
+ case "explicit": {
2334
+ allVars.push(`--color-${name}: ${def.value};`);
2335
+ break;
2336
+ }
2337
+ case "pair": {
2338
+ if (darkConfig.mode === "off") {
2339
+ allVars.push(`--color-${name}: ${def.light};`);
2340
+ } else {
2341
+ allVars.push(`--color-${name}: light-dark(${def.light}, ${def.dark});`);
2342
+ }
2343
+ break;
2344
+ }
2345
+ case "alias": {
2346
+ const source = colors[def.source];
2347
+ if (source && source.type === "generative") {
2348
+ const suffixes = usedColorStops?.get(name);
2349
+ if (!suffixes || suffixes.size === 0) break;
2350
+ const sorted = [...suffixes].sort((a, b) => a - b);
2351
+ for (const suffix of sorted) {
2352
+ allVars.push(`--color-${name}-${suffix}: var(--color-${def.source}-${suffix});`);
2539
2353
  }
2354
+ } else if (source) {
2355
+ allVars.push(`--color-${name}: var(--color-${def.source});`);
2540
2356
  }
2357
+ break;
2541
2358
  }
2542
2359
  }
2543
2360
  }
2544
- return result;
2361
+ return allVars;
2545
2362
  }
2546
- function splitBracketAware(input) {
2547
- if (!input.includes("[") && !input.includes("(")) {
2548
- const trimmed = input.trim();
2549
- return trimmed === "" ? [] : trimmed.split(WS_SPLIT_RE);
2550
- }
2551
- const result = [];
2552
- let tokenStart = -1;
2553
- let depth = 0;
2554
- for (let i = 0; i < input.length; i++) {
2555
- const ch = input[i];
2556
- if (ch === "\\" && i + 1 < input.length) {
2557
- if (tokenStart === -1) tokenStart = i;
2558
- i++;
2559
- continue;
2560
- }
2561
- if (ch === "[" || ch === "(") {
2562
- depth++;
2563
- if (tokenStart === -1) tokenStart = i;
2564
- } else if (ch === "]" || ch === ")") {
2565
- if (depth > 0) depth--;
2566
- if (tokenStart === -1) tokenStart = i;
2567
- } else if (depth === 0 && (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f")) {
2568
- if (tokenStart !== -1) {
2569
- result.push(input.slice(tokenStart, i));
2570
- tokenStart = -1;
2571
- }
2572
- } else {
2573
- if (tokenStart === -1) tokenStart = i;
2363
+ function generateThemeOverrides(colors, usedThemeSuffixes) {
2364
+ if (!Object.hasOwn(colors, "theme")) return [];
2365
+ if (!usedThemeSuffixes || usedThemeSuffixes.size === 0) return [];
2366
+ const sorted = [...usedThemeSuffixes].sort((a, b) => a - b);
2367
+ const blocks = [];
2368
+ for (const [name, def] of Object.entries(colors)) {
2369
+ if (name === "theme") continue;
2370
+ if (def.type !== "generative") continue;
2371
+ if (!def.inline) continue;
2372
+ const vars = [];
2373
+ for (const suffix of sorted) {
2374
+ vars.push(` --color-theme-${suffix}: var(--color-${name}-${suffix});`);
2574
2375
  }
2376
+ const safeName = name.replace(/[\\"'\]]/g, "");
2377
+ if (!safeName) continue;
2378
+ blocks.push(`[data-theme="${safeName}"] {
2379
+ ${vars.join("\n")}
2380
+ }`);
2575
2381
  }
2576
- if (tokenStart !== -1) result.push(input.slice(tokenStart));
2577
- return result;
2382
+ return blocks;
2578
2383
  }
2579
2384
 
2385
+ // src/theme/index.ts
2386
+ var CORNER_SHAPE_KEYWORDS = [
2387
+ "round",
2388
+ "scoop",
2389
+ "bevel",
2390
+ "notch",
2391
+ "square",
2392
+ "squircle"
2393
+ ];
2394
+ var DEFAULT_CORNER_SCALE = {
2395
+ round: 1,
2396
+ square: 1,
2397
+ notch: 1,
2398
+ bevel: 0.8,
2399
+ scoop: 1.2,
2400
+ squircle: 1.6
2401
+ };
2402
+ var DEFAULT_SUPERELLIPSE_SCALE = 1.6;
2403
+ var defaultTheme = Object.freeze({
2404
+ spacing: {
2405
+ base: "0.25rem"
2406
+ },
2407
+ colors: DEFAULT_COLORS,
2408
+ // Every named scale below ships empty: the tokens come from @text,
2409
+ // @breakpoint, @shadow, @weight, @ease, @animate and @blur. @fluid carries
2410
+ // no viewport range until one is declared, so fluid utilities stay inert.
2411
+ text: {},
2412
+ breakpoints: {},
2413
+ shadows: {},
2414
+ weights: {},
2415
+ easing: {},
2416
+ fluid: {},
2417
+ animations: {},
2418
+ blur: {}
2419
+ });
2420
+
2580
2421
  export {
2581
- isValidColorSuffix,
2582
- DEFAULT_COLORS,
2583
- DEFAULT_DARK_CONFIG,
2584
- oklchToOklab,
2585
- oklabToLinearSrgb,
2586
- linearToSrgb,
2587
- generateStop,
2588
- computeDarkStop,
2589
- formatOklch,
2590
- checkPaletteContrast,
2591
- generateAllColorVariables,
2592
- generateThemeOverrides,
2593
- DEFAULT_TEXT,
2594
- DEFAULT_BREAKPOINTS,
2595
- DEFAULT_ROUNDED_ROOF,
2596
- DEFAULT_ROUNDED,
2597
- CORNER_SHAPE_KEYWORDS,
2598
- DEFAULT_CORNER_SCALE,
2599
- DEFAULT_SUPERELLIPSE_SCALE,
2600
- DEFAULT_SHADOWS,
2601
- DEFAULT_WEIGHTS,
2602
- DEFAULT_EASING,
2603
- DEFAULT_BLUR,
2604
- DEFAULT_ANIMATIONS,
2605
- DEFAULT_FLUID,
2606
- DEFAULT_TRACKING,
2607
- DEFAULT_LEADING,
2608
- defaultTheme,
2422
+ IS_DEV,
2423
+ devWarn,
2609
2424
  PADDING_MAP,
2610
2425
  MARGIN_MAP,
2611
2426
  GAP_MAP,
@@ -2621,9 +2436,6 @@ export {
2621
2436
  BUILTIN_STATIC_KEYS,
2622
2437
  PREFIX_PROP_KEYS,
2623
2438
  buildFirstSegmentMap,
2624
- IS_DEV,
2625
- devWarn,
2626
- DEFAULT_TEXT_SIZES,
2627
2439
  resolverFor,
2628
2440
  createCompilationContext,
2629
2441
  registerCustomUtility,
@@ -2634,5 +2446,21 @@ export {
2634
2446
  finalizeCompilationContext,
2635
2447
  mergeUncached,
2636
2448
  ri,
2637
- createRi
2449
+ createRi,
2450
+ isValidColorSuffix,
2451
+ DEFAULT_COLORS,
2452
+ DEFAULT_DARK_CONFIG,
2453
+ oklchToOklab,
2454
+ oklabToLinearSrgb,
2455
+ linearToSrgb,
2456
+ generateStop,
2457
+ computeDarkStop,
2458
+ formatOklch,
2459
+ checkPaletteContrast,
2460
+ generateAllColorVariables,
2461
+ generateThemeOverrides,
2462
+ CORNER_SHAPE_KEYWORDS,
2463
+ DEFAULT_CORNER_SCALE,
2464
+ DEFAULT_SUPERELLIPSE_SCALE,
2465
+ defaultTheme
2638
2466
  };