rainbowindex 0.5.1 → 0.7.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +876 -0
  2. package/LICENSE +16 -17
  3. package/NOTICE.md +106 -0
  4. package/README.md +225 -69
  5. package/dist/browser.d.ts +4 -2
  6. package/dist/browser.mjs +12 -6
  7. package/dist/chunk-2T7V5XLK.mjs +912 -0
  8. package/dist/chunk-6OORICWF.mjs +16 -0
  9. package/dist/{chunk-PDORZSQX.mjs → chunk-FJOZJIKB.mjs} +7814 -5174
  10. package/dist/chunk-L56IRO7A.mjs +491 -0
  11. package/dist/chunk-PZDVDEZJ.mjs +196 -0
  12. package/dist/{chunk-4CTJLMYM.mjs → chunk-RC6DDE4L.mjs} +37 -23
  13. package/dist/chunk-TQJYVQPE.mjs +217 -0
  14. package/dist/chunk-W756NVYI.mjs +33 -0
  15. package/dist/chunk-WBESS2ZD.mjs +598 -0
  16. package/dist/{chunk-3HRMFZGE.mjs → chunk-X66Z2YHT.mjs} +2 -1
  17. package/dist/{chunk-4UKFK2GE.mjs → chunk-XQGSG2HK.mjs} +213 -756
  18. package/dist/cli.mjs +1101 -125
  19. package/dist/{context-B9yhJxd5.d.ts → context-DcBtnnan.d.ts} +55 -108
  20. package/dist/editor.d.ts +82 -421
  21. package/dist/editor.mjs +68 -363
  22. package/dist/eslint.d.ts +16 -0
  23. package/dist/eslint.mjs +32 -0
  24. package/dist/{index-Dx-NpFFx.d.ts → imports-C9esHd5Q.d.ts} +98 -81
  25. package/dist/index-CNqdL5U0.d.ts +56 -0
  26. package/dist/index-Czx-EUwh.d.ts +138 -0
  27. package/dist/index-DW8YSxTz.d.ts +104 -0
  28. package/dist/index.d.ts +49 -5
  29. package/dist/index.mjs +40 -13
  30. package/dist/oxlint.d.ts +21 -3
  31. package/dist/oxlint.mjs +19 -1
  32. package/dist/recipe.d.ts +111 -0
  33. package/dist/recipe.mjs +71 -0
  34. package/dist/safelist-CH3_PywB.d.ts +43 -0
  35. package/dist/session-CMaskdB7.d.ts +543 -0
  36. package/dist/tailwind.css +644 -0
  37. package/dist/theme-CIZiGlce.d.ts +115 -0
  38. package/dist/vite.d.ts +10 -1
  39. package/dist/vite.mjs +273 -113
  40. package/package.json +27 -5
  41. package/dist/chunk-F4VCBISU.mjs +0 -1866
  42. package/dist/chunk-RU4756NG.mjs +0 -243
  43. package/dist/safelist-DAkKuxCk.d.ts +0 -96
@@ -1,611 +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 CORNER_SHAPE_KEYWORDS = [
441
- "round",
442
- "scoop",
443
- "bevel",
444
- "notch",
445
- "square",
446
- "squircle"
447
- ];
448
- var DEFAULT_CORNER_SCALE = {
449
- round: 1,
450
- square: 1,
451
- notch: 1,
452
- bevel: 0.8,
453
- scoop: 1.2,
454
- squircle: 1.6
455
- };
456
- var DEFAULT_SUPERELLIPSE_SCALE = 1.6;
457
- var DEFAULT_SHADOWS = {
458
- // Building blocks: colors
459
- line: "light-dark(oklch(0 0 0 / 0.06), oklch(1 0 0 / 0.02))",
460
- drop: "light-dark(oklch(0 0 0 / 0.06), oklch(0 0 0 / 0.18))",
461
- "hi-1": "light-dark(transparent, oklch(1 0 0 / 0.01))",
462
- "hi-2": "light-dark(transparent, oklch(1 0 0 / 0.02))",
463
- "hi-3": "light-dark(transparent, oklch(1 0 0 / 0.04))",
464
- "hi-4": "light-dark(transparent, oklch(1 0 0 / 0.06))",
465
- "dark-line": "light-dark(transparent, oklch(0 0 0 / 0.14))",
466
- // Building blocks: composed shadow layers
467
- ring: "0 0 0 1px var(--shadow-line)",
468
- "layer-1": "0 1px 1px -0.5px var(--shadow-drop)",
469
- "layer-2": "0 3px 3px -1.5px var(--shadow-drop)",
470
- "layer-3": "0 6px 6px -3px var(--shadow-drop)",
471
- "layer-4": "0 12px 12px -6px var(--shadow-drop)",
472
- "layer-5": "0 24px 24px -12px var(--shadow-drop)",
473
- "layer-6": "0 48px 48px -24px var(--shadow-drop)",
474
- "layer-7": "0 96px 96px -48px var(--shadow-drop)",
475
- // Class-facing
476
- px: "var(--shadow-ring)",
477
- "2xs": "inset 0 1px 0 0 var(--shadow-hi-1), var(--shadow-ring), var(--shadow-layer-1)",
478
- 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)",
479
- 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)",
480
- 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)",
481
- 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)",
482
- 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)",
483
- "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)",
484
- none: "0 0 #0000"
485
- };
486
- var DEFAULT_WEIGHTS = {
487
- thin: 100,
488
- extralight: 200,
489
- light: 300,
490
- normal: 400,
491
- medium: 500,
492
- semibold: 600,
493
- bold: 700,
494
- extrabold: 800,
495
- black: 900
496
- };
497
- var DEFAULT_EASING = {
498
- in: "cubic-bezier(0.4, 0, 1, 1)",
499
- out: "cubic-bezier(0, 0, 0.2, 1)",
500
- "in-out": "cubic-bezier(0.4, 0, 0.2, 1)",
501
- linear: "linear"
502
- };
503
- var DEFAULT_BLUR = {
504
- xs: "2px",
505
- sm: "4px",
506
- DEFAULT: "8px",
507
- md: "12px",
508
- lg: "16px",
509
- xl: "24px",
510
- "2xl": "40px",
511
- "3xl": "64px",
512
- none: "0"
513
- };
514
- var DEFAULT_ANIMATIONS = {
515
- spin: {
516
- shorthand: "spin 1s linear infinite",
517
- keyframes: `from { transform: rotate(0deg); }
518
- to { transform: rotate(360deg); }`
519
- },
520
- pulse: {
521
- shorthand: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
522
- keyframes: `0%, 100% { opacity: 1; }
523
- 50% { opacity: 0.5; }`
524
- },
525
- bounce: {
526
- shorthand: "bounce 1s infinite",
527
- keyframes: `0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
528
- 50% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }`
529
- },
530
- ping: {
531
- shorthand: "ping 1s cubic-bezier(0, 0, 0.2, 1) infinite",
532
- keyframes: "75%, 100% { transform: scale(2); opacity: 0; }"
533
- },
534
- // Disclosure animations for the @rainbowindex/ui Accordion + Collapsible
535
- // primitives. Both expand from 0 → the natural content size published by
536
- // the package's `useCollapseSize` hook as `--ri-collapsible-content-height`
537
- // (a single shared variable — the primitives do not emit separate accordion
538
- // vs collapsible vars). The `auto` fallback keeps the keyframe valid on the
539
- // first frame before the hook measures.
540
- "accordion-down": {
541
- shorthand: "accordion-down 0.2s ease-out",
542
- keyframes: `from { height: 0; }
543
- to { height: var(--ri-collapsible-content-height, auto); }`
544
- },
545
- "accordion-up": {
546
- shorthand: "accordion-up 0.2s ease-out",
547
- keyframes: `from { height: var(--ri-collapsible-content-height, auto); }
548
- to { height: 0; }`
549
- },
550
- "collapsible-down": {
551
- shorthand: "collapsible-down 0.2s ease-out",
552
- keyframes: `from { height: 0; }
553
- to { height: var(--ri-collapsible-content-height, auto); }`
554
- },
555
- "collapsible-up": {
556
- shorthand: "collapsible-up 0.2s ease-out",
557
- keyframes: `from { height: var(--ri-collapsible-content-height, auto); }
558
- to { height: 0; }`
559
- },
560
- "caret-blink": {
561
- shorthand: "caret-blink 1.25s ease-out infinite",
562
- keyframes: `0%, 70%, 100% { opacity: 1; }
563
- 20%, 50% { opacity: 0; }`
564
- }
565
- };
566
- var DEFAULT_FLUID = {
567
- min: "20rem",
568
- max: "80rem"
569
- };
570
- var DEFAULT_TRACKING = {
571
- tighter: "-0.05em",
572
- tight: "-0.025em",
573
- normal: "0em",
574
- wide: "0.025em",
575
- wider: "0.05em",
576
- widest: "0.1em"
577
- };
578
- var DEFAULT_LEADING = {
579
- "3": "0.75rem",
580
- "4": "1rem",
581
- "5": "1.25rem",
582
- "6": "1.5rem",
583
- "7": "1.75rem",
584
- "8": "2rem",
585
- "9": "2.25rem",
586
- "10": "2.5rem",
587
- none: "1",
588
- tight: "1.25",
589
- snug: "1.375",
590
- normal: "1.5",
591
- relaxed: "1.625",
592
- loose: "2"
593
- };
594
- var defaultTheme = Object.freeze({
595
- spacing: {
596
- base: "0.25rem"
597
- },
598
- colors: DEFAULT_COLORS,
599
- text: DEFAULT_TEXT,
600
- breakpoints: DEFAULT_BREAKPOINTS,
601
- shadows: DEFAULT_SHADOWS,
602
- weights: DEFAULT_WEIGHTS,
603
- easing: DEFAULT_EASING,
604
- fluid: DEFAULT_FLUID,
605
- animations: DEFAULT_ANIMATIONS,
606
- blur: DEFAULT_BLUR
607
- });
608
-
609
1
  // src/runtime.ts
610
2
  var IS_PROD = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
611
3
  var IS_DEV = !IS_PROD;
@@ -613,7 +5,7 @@ function devWarn(message) {
613
5
  if (!IS_PROD) console.warn(message);
614
6
  }
615
7
 
616
- // src/merge/props.ts
8
+ // src/utilities/property-maps.ts
617
9
  var PADDING_MAP = Object.freeze({
618
10
  p: ["padding"],
619
11
  px: ["padding-inline"],
@@ -698,6 +90,34 @@ var BORDER_DIR_PROPS = Object.freeze({
698
90
  "border-x": ["border-inline-width"],
699
91
  "border-y": ["border-block-width"]
700
92
  });
93
+ var FILTER_SLOTS = Object.freeze([
94
+ "--ri-blur",
95
+ "--ri-brightness",
96
+ "--ri-contrast",
97
+ "--ri-grayscale",
98
+ "--ri-hue-rotate",
99
+ "--ri-invert",
100
+ "--ri-saturate",
101
+ "--ri-sepia",
102
+ "--ri-drop-shadow"
103
+ ]);
104
+ var BACKDROP_FILTER_SLOTS = Object.freeze([
105
+ "--ri-backdrop-blur",
106
+ "--ri-backdrop-brightness",
107
+ "--ri-backdrop-contrast",
108
+ "--ri-backdrop-grayscale",
109
+ "--ri-backdrop-hue-rotate",
110
+ "--ri-backdrop-invert",
111
+ "--ri-backdrop-saturate",
112
+ "--ri-backdrop-sepia",
113
+ "--ri-backdrop-opacity"
114
+ ]);
115
+ var FILTER_COMPOSED = FILTER_SLOTS.map((slot) => `var(${slot}, )`).join(" ");
116
+ var BACKDROP_FILTER_COMPOSED = BACKDROP_FILTER_SLOTS.map((slot) => `var(${slot}, )`).join(
117
+ " "
118
+ );
119
+
120
+ // src/merge/static-props.ts
701
121
  var BUILTIN_STATIC_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
702
122
  // Grow/shrink
703
123
  grow: ["flex-grow"],
@@ -749,6 +169,9 @@ var BUILTIN_STATIC_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
749
169
  "transform-3d": ["transform-style"],
750
170
  // Translate/rotate/scale none
751
171
  "translate-none": ["translate"],
172
+ // `translate-3d` only opts into the Z axis; it claims the same property, so
173
+ // a later `translate-none` still wins and two of them dedupe.
174
+ "translate-3d": ["translate"],
752
175
  "rotate-none": ["rotate"],
753
176
  "scale-none": ["scale"],
754
177
  // Perspective
@@ -763,10 +186,11 @@ var BUILTIN_STATIC_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
763
186
  // outline shorthand + offset (claiming all sub-properties); outline-none just sets style.
764
187
  outline: ["outline-width"],
765
188
  "outline-hidden": ["outline-style", "outline-width", "outline-color", "outline-offset"],
766
- // Shadow / ring (static bare + reset forms; valued forms via PREFIX_PROPS).
189
+ // Shadow / ring (static reset + bare ring forms; valued forms via PREFIX_PROPS).
767
190
  // Each composable family claims box-shadow (shared) + its own slot var, so
768
191
  // shadow/inset-shadow/ring/inset-ring coexist while same-family repeats dedupe.
769
- shadow: ["box-shadow", "--ri-shadow"],
192
+ // Bare `shadow` is not static: with no default shadow scale it only resolves
193
+ // when the theme defines a `DEFAULT` (or `md`) token via @shadow.
770
194
  "shadow-none": ["box-shadow", "--ri-shadow"],
771
195
  ring: ["box-shadow", "--ri-ring-shadow"],
772
196
  "inset-ring": ["box-shadow", "--ri-inset-ring-shadow"],
@@ -794,14 +218,22 @@ var BUILTIN_STATIC_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
794
218
  // Divide-between reverse flags (scoped so they compose with divide-x/divide-y)
795
219
  "divide-x-reverse": ["~divide:--ri-divide-x-reverse"],
796
220
  "divide-y-reverse": ["~divide:--ri-divide-y-reverse"],
797
- // Backdrop filter (static) — backdrop-blur-none resets all backdrop filters
798
- "backdrop-blur-none": ["backdrop-filter"],
221
+ // Backdrop filter (static). `backdrop-blur-none` clears the blur slot and
222
+ // nothing else, so it claims what every other backdrop-blur spelling claims
223
+ // — that is what lets it dedupe against `backdrop-blur-lg` while leaving a
224
+ // `backdrop-invert` beside it alone.
225
+ "backdrop-blur-none": ["--ri-backdrop-blur", "backdrop-filter"],
226
+ // Bare `backdrop-filter` enables the chain without contributing to it, so it
227
+ // claims the shorthand alone and composes with every function to its left.
228
+ "backdrop-filter": ["backdrop-filter"],
799
229
  "backdrop-grayscale": ["--ri-backdrop-grayscale", "backdrop-filter"],
800
230
  "backdrop-invert": ["--ri-backdrop-invert", "backdrop-filter"],
801
231
  "backdrop-sepia": ["--ri-backdrop-sepia", "backdrop-filter"],
802
232
  // Filter (static) — filter-none resets all filters
803
- "filter-none": ["filter"],
804
- "backdrop-filter-none": ["backdrop-filter"],
233
+ "filter-none": ["filter", ...FILTER_SLOTS],
234
+ "backdrop-filter-none": ["backdrop-filter", ...BACKDROP_FILTER_SLOTS],
235
+ // Bare `filter`, as above: enables, does not reset.
236
+ filter: ["filter"],
805
237
  grayscale: ["--ri-grayscale", "filter"],
806
238
  invert: ["--ri-invert", "filter"],
807
239
  sepia: ["--ri-sepia", "filter"],
@@ -1311,23 +743,7 @@ addAll(
1311
743
  ],
1312
744
  ["~divide:border-style"]
1313
745
  );
1314
- addAll(
1315
- [
1316
- "animate-spin",
1317
- "animate-pulse",
1318
- "animate-bounce",
1319
- "animate-ping",
1320
- "animate-in",
1321
- "animate-out",
1322
- "animate-none",
1323
- "animate-accordion-down",
1324
- "animate-accordion-up",
1325
- "animate-collapsible-down",
1326
- "animate-collapsible-up",
1327
- "animate-caret-blink"
1328
- ],
1329
- ["animation"]
1330
- );
746
+ addAll(["animate-in", "animate-out", "animate-none"], ["animation"]);
1331
747
  addAll(["animate-infinite", "animate-once", "animate-twice"], ["animation-iteration-count"]);
1332
748
  addAll(
1333
749
  ["animate-fill-none", "animate-fill-forwards", "animate-fill-both", "animate-fill-backwards"],
@@ -1513,6 +929,9 @@ addAll(
1513
929
  ],
1514
930
  ["mix-blend-mode"]
1515
931
  );
932
+ Object.freeze(BUILTIN_STATIC_PROPS);
933
+
934
+ // src/merge/prefix-props.ts
1516
935
  var FLUID_EXCLUDED_PREFIXES = /* @__PURE__ */ new Set(["inset-s", "inset-e", "inset-bs", "inset-be"]);
1517
936
  var FLUID_SPACING_PROPS = (() => {
1518
937
  const rows = {};
@@ -1530,6 +949,9 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1530
949
  ...MARGIN_MAP,
1531
950
  ...GAP_MAP,
1532
951
  ...FLUID_SPACING_PROPS,
952
+ // fluid-<name> scope classes all claim the same scope-var pair, so a later
953
+ // range wins over an earlier one.
954
+ fluid: ["--fluid-scope-min", "--fluid-scope-max"],
1533
955
  "space-x": ["~space:margin-inline-start", "~space:margin-inline-end"],
1534
956
  "space-y": ["~space:margin-block-start", "~space:margin-block-end"],
1535
957
  ...INSET_MAP,
@@ -1606,6 +1028,14 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1606
1028
  "inset-shadow": ["box-shadow", "--ri-inset-shadow"],
1607
1029
  ring: ["box-shadow", "--ri-ring-shadow"],
1608
1030
  "inset-ring": ["box-shadow", "--ri-inset-ring-shadow"],
1031
+ // `ring-offset-*` claims its own slots and NOT `box-shadow`, which is the
1032
+ // whole reason it composes: the offset layer joins a chain some other class
1033
+ // already emits the shorthand for. A claim on `box-shadow` here would let
1034
+ // `ring-offset-2` dominate the `ring-2` doing the visible work — which is
1035
+ // what happened before this row existed, when the family fell into the
1036
+ // `ring` prefix and `ri("ring-2 ring-offset-2 ring-offset-white")` returned
1037
+ // just `"ring-offset-white"`.
1038
+ "ring-offset": ["--ri-ring-offset-width", "--ri-ring-offset-shadow"],
1609
1039
  // text-shadow is its own property (not part of the box-shadow composition)
1610
1040
  "text-shadow": ["text-shadow"],
1611
1041
  opacity: ["opacity"],
@@ -1624,7 +1054,9 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1624
1054
  "skew-y": ["transform", "--ri-skew-y"],
1625
1055
  transform: ["transform"],
1626
1056
  zoom: ["zoom"],
1627
- filter: ["filter"],
1057
+ // `filter-[v]` replaces the chain outright, so it claims every slot. The
1058
+ // bare `filter` static above is matched first and claims only the shorthand.
1059
+ filter: ["filter", ...FILTER_SLOTS],
1628
1060
  brightness: ["--ri-brightness", "filter"],
1629
1061
  contrast: ["--ri-contrast", "filter"],
1630
1062
  saturate: ["--ri-saturate", "filter"],
@@ -1677,10 +1109,13 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1677
1109
  "from-position": ["--ri-gradient-from-position"],
1678
1110
  "via-position": ["--ri-gradient-via-position"],
1679
1111
  "to-position": ["--ri-gradient-to-position"],
1680
- // Animations
1112
+ // Animations. `animate` is last of the four by length, so the three
1113
+ // specific prefixes match first and `animate-{name}` catches every
1114
+ // @animate token and arbitrary shorthand.
1681
1115
  "animate-duration": ["animation-duration"],
1682
1116
  "animate-delay": ["animation-delay"],
1683
1117
  "animate-ease": ["animation-timing-function"],
1118
+ animate: ["animation"],
1684
1119
  "break-before": ["break-before"],
1685
1120
  "break-after": ["break-after"],
1686
1121
  "break-inside": ["break-inside"],
@@ -1705,7 +1140,7 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1705
1140
  ...SCROLL_MARGIN_MAP,
1706
1141
  ...SCROLL_PADDING_MAP,
1707
1142
  // Backdrop filter — each function gets a unique CSS variable so they coexist
1708
- "backdrop-filter": ["backdrop-filter"],
1143
+ "backdrop-filter": ["backdrop-filter", ...BACKDROP_FILTER_SLOTS],
1709
1144
  "backdrop-blur": ["--ri-backdrop-blur", "backdrop-filter"],
1710
1145
  "backdrop-brightness": ["--ri-backdrop-brightness", "backdrop-filter"],
1711
1146
  "backdrop-contrast": ["--ri-backdrop-contrast", "backdrop-filter"],
@@ -1754,6 +1189,11 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1754
1189
  decoration: ["text-decoration-thickness"],
1755
1190
  accent: ["accent-color"],
1756
1191
  caret: ["caret-color"],
1192
+ // Scoped, like `divide`: the declaration is `color`, but it lands inside
1193
+ // `&::placeholder`, so it never conflicts with `text-*`. Without the
1194
+ // `~placeholder:` scope both would claim `color` and the merger would drop
1195
+ // one of `text-red-500 placeholder-gray-400`.
1196
+ placeholder: ["~placeholder:color"],
1757
1197
  // Composable scrollbar-color (shared scrollbar-color + per-part slot var).
1758
1198
  "scrollbar-thumb": ["scrollbar-color", "--ri-scrollbar-thumb"],
1759
1199
  "scrollbar-track": ["scrollbar-color", "--ri-scrollbar-track"],
@@ -1773,6 +1213,9 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1773
1213
  "position-area": ["position-area"],
1774
1214
  "anchor-scope": ["anchor-scope"]
1775
1215
  });
1216
+ Object.freeze(PREFIX_PROPS);
1217
+
1218
+ // src/merge/props.ts
1776
1219
  var DIRECT_OVERRIDES = Object.assign(/* @__PURE__ */ Object.create(null), {
1777
1220
  padding: ["padding-inline", "padding-block"],
1778
1221
  "padding-inline": ["padding-inline-start", "padding-inline-end"],
@@ -1855,30 +1298,15 @@ var DIRECT_OVERRIDES = Object.assign(/* @__PURE__ */ Object.create(null), {
1855
1298
  ],
1856
1299
  "place-items": ["align-items", "justify-items"],
1857
1300
  "place-content": ["align-content", "justify-content"],
1858
- "place-self": ["align-self", "justify-self"],
1859
- // filter-none / backdrop-blur-none reset all individual filter functions
1860
- filter: [
1861
- "--ri-blur",
1862
- "--ri-brightness",
1863
- "--ri-contrast",
1864
- "--ri-saturate",
1865
- "--ri-hue-rotate",
1866
- "--ri-drop-shadow",
1867
- "--ri-grayscale",
1868
- "--ri-invert",
1869
- "--ri-sepia"
1870
- ],
1871
- "backdrop-filter": [
1872
- "--ri-backdrop-blur",
1873
- "--ri-backdrop-brightness",
1874
- "--ri-backdrop-contrast",
1875
- "--ri-backdrop-saturate",
1876
- "--ri-backdrop-grayscale",
1877
- "--ri-backdrop-invert",
1878
- "--ri-backdrop-sepia",
1879
- "--ri-backdrop-opacity",
1880
- "--ri-backdrop-hue-rotate"
1881
- ]
1301
+ "place-self": ["align-self", "justify-self"]
1302
+ // NOTE: `filter` and `backdrop-filter` deliberately have NO row here.
1303
+ // This table is keyed on the CSS property, and every composable member of
1304
+ // those families emits the shared shorthand as its second declaration — so
1305
+ // a row here made `grayscale` claim `--ri-blur`, and `ri("blur-sm
1306
+ // grayscale")` returned `"grayscale"`. The whole family annihilated itself,
1307
+ // which is the opposite of the composition the slot variables exist for.
1308
+ // The spellings that really do replace the chain claim the slots directly
1309
+ // instead; see FILTER_SLOTS.
1882
1310
  });
1883
1311
  var OVERRIDES = (() => {
1884
1312
  const closed = /* @__PURE__ */ Object.create(null);
@@ -1896,9 +1324,31 @@ var OVERRIDES = (() => {
1896
1324
  }
1897
1325
  return closed;
1898
1326
  })();
1899
- Object.freeze(BUILTIN_STATIC_PROPS);
1900
- Object.freeze(PREFIX_PROPS);
1901
1327
  Object.freeze(OVERRIDES);
1328
+ var BUILTIN_STATIC_KEYS = new Set(Object.keys(BUILTIN_STATIC_PROPS));
1329
+ var PREFIX_PROP_KEYS = new Set(Object.keys(PREFIX_PROPS));
1330
+ var SORTED_PREFIXES = Object.keys(PREFIX_PROPS).sort((a, b) => b.length - a.length);
1331
+ function buildFirstSegmentMap(prefixes) {
1332
+ const map = /* @__PURE__ */ new Map();
1333
+ for (const prefix of prefixes) {
1334
+ const dashIdx = prefix.indexOf("-");
1335
+ const firstSeg = dashIdx === -1 ? prefix : prefix.slice(0, dashIdx);
1336
+ const existing = map.get(firstSeg);
1337
+ if (existing) {
1338
+ existing.push(prefix);
1339
+ } else {
1340
+ map.set(firstSeg, [prefix]);
1341
+ }
1342
+ }
1343
+ const frozen = /* @__PURE__ */ new Map();
1344
+ for (const [key, arr] of map) {
1345
+ frozen.set(key, Object.freeze(arr));
1346
+ }
1347
+ return frozen;
1348
+ }
1349
+ var PREFIX_FIRST_SEGMENT_MAP = buildFirstSegmentMap(SORTED_PREFIXES);
1350
+
1351
+ // src/merge/value-kinds.ts
1902
1352
  var RE_COLOR_SHADE = /^[a-z]+(?:-[a-z]+)*-\d{2,3}$/;
1903
1353
  var COLOR_FUNCTION_ALTERNATION = "oklch|oklab|rgb|rgba|hsl|hsla|hwb|lab|lch|color|light-dark";
1904
1354
  var RE_ARBITRARY_COLOR = new RegExp(`[#]|(?:${COLOR_FUNCTION_ALTERNATION})\\s*\\(`);
@@ -1974,28 +1424,6 @@ function isFontFamilyValue(value) {
1974
1424
  }
1975
1425
  return false;
1976
1426
  }
1977
- var BUILTIN_STATIC_KEYS = new Set(Object.keys(BUILTIN_STATIC_PROPS));
1978
- var PREFIX_PROP_KEYS = new Set(Object.keys(PREFIX_PROPS));
1979
- var SORTED_PREFIXES = Object.keys(PREFIX_PROPS).sort((a, b) => b.length - a.length);
1980
- function buildFirstSegmentMap(prefixes) {
1981
- const map = /* @__PURE__ */ new Map();
1982
- for (const prefix of prefixes) {
1983
- const dashIdx = prefix.indexOf("-");
1984
- const firstSeg = dashIdx === -1 ? prefix : prefix.slice(0, dashIdx);
1985
- const existing = map.get(firstSeg);
1986
- if (existing) {
1987
- existing.push(prefix);
1988
- } else {
1989
- map.set(firstSeg, [prefix]);
1990
- }
1991
- }
1992
- const frozen = /* @__PURE__ */ new Map();
1993
- for (const [key, arr] of map) {
1994
- frozen.set(key, Object.freeze(arr));
1995
- }
1996
- return frozen;
1997
- }
1998
- var PREFIX_FIRST_SEGMENT_MAP = buildFirstSegmentMap(SORTED_PREFIXES);
1999
1427
 
2000
1428
  // src/brackets.ts
2001
1429
  function scanBracketAware(input, onChar, options) {
@@ -2031,17 +1459,6 @@ function scanBracketAware(input, onChar, options) {
2031
1459
  }
2032
1460
 
2033
1461
  // src/merge/resolve.ts
2034
- var DEFAULT_TEXT_SIZES = [
2035
- "xs",
2036
- "sm",
2037
- "base",
2038
- "lg",
2039
- "xl",
2040
- "2xl",
2041
- "3xl",
2042
- "4xl",
2043
- "5xl"
2044
- ];
2045
1462
  var DEFAULT_FONT_FAMILIES = ["sans", "serif", "mono"];
2046
1463
  function stripTextModifier(value) {
2047
1464
  if (value.indexOf("/") === -1) return value;
@@ -2099,10 +1516,18 @@ var DECORATION_COLOR_PROPS = Object.freeze(["text-decoration-color"]);
2099
1516
  var RE_SIGNED_INT = /^-?\d+$/;
2100
1517
  var RE_UNSIGNED_INT = /^\d+$/;
2101
1518
  var RE_DECIMAL = /^\d+(?:[._]\d+)?$/;
2102
- function colorOrDefault(prefix, colorProps) {
1519
+ function colorOrDefault(prefix, colorProps, hasInitial = false) {
2103
1520
  const defaultProps = PREFIX_PROPS[prefix];
2104
- return (value, _textSizes2, _fontFamilies2, colorNames) => isColorValue(value, void 0, colorNames) ? colorProps : defaultProps;
1521
+ return (value, _textSizes2, _fontFamilies2, colorNames) => hasInitial && value === "initial" || isColorValue(value, void 0, colorNames) ? colorProps : defaultProps;
2105
1522
  }
1523
+ var RING_INSET_PROPS = Object.freeze(["--ri-ring-inset"]);
1524
+ var RING_COLOR_PROPS = Object.freeze(["--ri-ring-color"]);
1525
+ var RING_DEFAULT_PROPS = PREFIX_PROPS.ring;
1526
+ var RING_DUAL_MODE = (value, _textSizes2, _fontFamilies2, colorNames) => {
1527
+ if (isColorValue(value, void 0, colorNames)) return RING_COLOR_PROPS;
1528
+ if (value === "inset") return RING_INSET_PROPS;
1529
+ return RING_DEFAULT_PROPS;
1530
+ };
2106
1531
  var DUAL_MODE_PREFIXES = {
2107
1532
  ...MASK_STOP_DUAL_MODES,
2108
1533
  // mask-radial-[<size>] sets the size var; mask-radial-[<value>] is a full image.
@@ -2147,11 +1572,12 @@ var DUAL_MODE_PREFIXES = {
2147
1572
  // so a color-first check would misclassify it) and background-color
2148
1573
  // for everything else — never the full `background` shorthand.
2149
1574
  bg: (value) => isImageValue(value) ? BG_IMAGE_PROPS : PREFIX_PROPS.bg,
2150
- shadow: colorOrDefault("shadow", Object.freeze(["--ri-shadow-color"])),
2151
- "inset-shadow": colorOrDefault("inset-shadow", Object.freeze(["--ri-inset-shadow-color"])),
2152
- ring: colorOrDefault("ring", Object.freeze(["--ri-ring-color"])),
1575
+ shadow: colorOrDefault("shadow", Object.freeze(["--ri-shadow-color"]), true),
1576
+ "ring-offset": colorOrDefault("ring-offset", Object.freeze(["--ri-ring-offset-color"])),
1577
+ "inset-shadow": colorOrDefault("inset-shadow", Object.freeze(["--ri-inset-shadow-color"]), true),
1578
+ ring: RING_DUAL_MODE,
2153
1579
  "inset-ring": colorOrDefault("inset-ring", Object.freeze(["--ri-inset-ring-color"])),
2154
- "text-shadow": colorOrDefault("text-shadow", Object.freeze(["--ri-text-shadow-color"])),
1580
+ "text-shadow": colorOrDefault("text-shadow", Object.freeze(["--ri-text-shadow-color"]), true),
2155
1581
  "drop-shadow": colorOrDefault("drop-shadow", Object.freeze(["--ri-drop-shadow-color"])),
2156
1582
  from: (value) => isGradientPositionValue(value) ? PREFIX_PROPS["from-position"] : PREFIX_PROPS.from,
2157
1583
  via: (value) => isGradientPositionValue(value) ? PREFIX_PROPS["via-position"] : PREFIX_PROPS.via,
@@ -2217,7 +1643,7 @@ function resolvePropsWith(utility, customStaticProps, customFunctionalProps, tex
2217
1643
  // src/merge/context.ts
2218
1644
  var _customStaticProps = {};
2219
1645
  var _customFunctionalProps = [];
2220
- var _textSizes = new Set(DEFAULT_TEXT_SIZES);
1646
+ var _textSizes = /* @__PURE__ */ new Set();
2221
1647
  var _fontFamilies = new Set(DEFAULT_FONT_FAMILIES);
2222
1648
  var _colorNames = /* @__PURE__ */ new Set();
2223
1649
  var _latestSnapshot = null;
@@ -2289,7 +1715,7 @@ function createCompilationContext() {
2289
1715
  return {
2290
1716
  customStaticProps: {},
2291
1717
  customFunctionalProps: {},
2292
- textSizes: new Set(DEFAULT_TEXT_SIZES),
1718
+ textSizes: /* @__PURE__ */ new Set(),
2293
1719
  fontFamilies: new Set(DEFAULT_FONT_FAMILIES),
2294
1720
  colorNames: /* @__PURE__ */ new Set()
2295
1721
  };
@@ -2329,8 +1755,7 @@ function snapshotCompilationContext(ctx) {
2329
1755
  colorNames: new Set(ctx.colorNames)
2330
1756
  };
2331
1757
  }
2332
- function finalizeCompilationContext(ctx) {
2333
- const snapshot = snapshotCompilationContext(ctx);
1758
+ function publishSnapshot(snapshot) {
2334
1759
  _customStaticProps = snapshot.customStaticProps;
2335
1760
  _customFunctionalProps = snapshot.customFunctionalProps;
2336
1761
  _textSizes = snapshot.textSizes;
@@ -2338,8 +1763,52 @@ function finalizeCompilationContext(ctx) {
2338
1763
  _colorNames = snapshot.colorNames;
2339
1764
  defaultRiCache.clear();
2340
1765
  _latestSnapshot = snapshot;
1766
+ }
1767
+ function finalizeCompilationContext(ctx) {
1768
+ const snapshot = snapshotCompilationContext(ctx);
1769
+ publishSnapshot(snapshot);
2341
1770
  return snapshot;
2342
1771
  }
1772
+ var SNAPSHOT_FORMAT = 1;
1773
+ function serializeSnapshot(snapshot) {
1774
+ return {
1775
+ format: SNAPSHOT_FORMAT,
1776
+ customStaticProps: Object.fromEntries(
1777
+ Object.entries(snapshot.customStaticProps).map(([k, v]) => [k, [...v]]).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)
1778
+ ),
1779
+ // Already tuples, and already ordered longest-root-first — an order the
1780
+ // resolver depends on, so it is preserved rather than sorted.
1781
+ customFunctionalProps: snapshot.customFunctionalProps.map(
1782
+ ([root, props]) => [root, [...props]]
1783
+ ),
1784
+ textSizes: [...snapshot.textSizes].sort(),
1785
+ fontFamilies: [...snapshot.fontFamilies].sort(),
1786
+ colorNames: [...snapshot.colorNames].sort()
1787
+ };
1788
+ }
1789
+ function hydrateSnapshot(data) {
1790
+ if (data?.format !== SNAPSHOT_FORMAT) {
1791
+ devWarn(
1792
+ `[RI-2007] The generated theme snapshot was written for format ${String(data?.format)}, but this version of rainbowindex reads format ${SNAPSHOT_FORMAT}. Its theme was ignored, so \`ri()\` will guess at project-defined sizes and colours. Re-run \`rainbowindex generate-snapshot\`, or update the plugin that generates it.`
1793
+ );
1794
+ return {
1795
+ customStaticProps: {},
1796
+ customFunctionalProps: [],
1797
+ textSizes: /* @__PURE__ */ new Set(),
1798
+ // The three built-in slots are always live: an absent list means "no
1799
+ // snapshot said otherwise", not "no font slots exist".
1800
+ fontFamilies: new Set(DEFAULT_FONT_FAMILIES),
1801
+ colorNames: /* @__PURE__ */ new Set()
1802
+ };
1803
+ }
1804
+ return {
1805
+ customStaticProps: data.customStaticProps,
1806
+ customFunctionalProps: data.customFunctionalProps,
1807
+ textSizes: new Set(data.textSizes),
1808
+ fontFamilies: new Set(data.fontFamilies),
1809
+ colorNames: new Set(data.colorNames)
1810
+ };
1811
+ }
2343
1812
 
2344
1813
  // src/merge/index.ts
2345
1814
  var RI_CACHE_KEY_MAX_LEN = 2048;
@@ -2436,7 +1905,7 @@ function mergeClasses(classes, resolve, cache) {
2436
1905
  if (useCache) cache.put(cacheKey, output);
2437
1906
  return output;
2438
1907
  }
2439
- function mergeFrom(inputs, resolve, cache) {
1908
+ function mergeFrom(inputs, resolve, cache, warnUnthemed = false) {
2440
1909
  let fastPath = true;
2441
1910
  let rawKey = "";
2442
1911
  for (let i = 0; i < inputs.length; i++) {
@@ -2454,6 +1923,7 @@ function mergeFrom(inputs, resolve, cache) {
2454
1923
  const cached = cache.get(rawKey);
2455
1924
  if (cached !== void 0) return cached;
2456
1925
  const classes2 = flattenInputs(inputs);
1926
+ if (warnUnthemed && classes2.length > 1) warnUnpublishedTheme(classes2, resolve);
2457
1927
  const output = classes2.length === 0 ? "" : classes2.length === 1 ? classes2[0] : mergeUncached(classes2, resolve);
2458
1928
  cache.put(rawKey, output);
2459
1929
  return output;
@@ -2462,14 +1932,9 @@ function mergeFrom(inputs, resolve, cache) {
2462
1932
  const classes = flattenInputs(inputs);
2463
1933
  if (classes.length === 0) return "";
2464
1934
  if (classes.length === 1) return classes[0];
1935
+ if (warnUnthemed) warnUnpublishedTheme(classes, resolve);
2465
1936
  return mergeClasses(classes, resolve, cache);
2466
1937
  }
2467
- var _isSSR = null;
2468
- function detectSSR() {
2469
- if (_isSSR !== null) return _isSSR;
2470
- _isSSR = typeof window === "undefined" && typeof process !== "undefined" && !!process.versions?.node;
2471
- return _isSSR;
2472
- }
2473
1938
  var WARNING_THROTTLE_MS = 6e4;
2474
1939
  function throttledWarn(message, emit = console.warn) {
2475
1940
  let last = Number.NEGATIVE_INFINITY;
@@ -2481,15 +1946,32 @@ function throttledWarn(message, emit = console.warn) {
2481
1946
  }
2482
1947
  };
2483
1948
  }
2484
- var warnSSRUsage = throttledWarn(
2485
- () => !hasFinalizedSnapshot() ? (
2486
- // No compilation has ever finalized — module-level state is uninitialized
2487
- // defaults. In SSR this almost certainly means ri() is being called without
2488
- // a preceding compile pass, which will produce incorrect merge results
2489
- // for custom utilities, text sizes, and font families.
2490
- "[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"
2491
- ) : "[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."
2492
- );
1949
+ var _warnedNoSnapshot = false;
1950
+ function shouldCheckSnapshot() {
1951
+ return !_warnedNoSnapshot && !hasFinalizedSnapshot();
1952
+ }
1953
+ function isThemeDependent(cls, resolve) {
1954
+ const splitIdx = findVariantSplit(cls);
1955
+ let utility = splitIdx === -1 ? cls : cls.slice(splitIdx + 1);
1956
+ if (utility.charCodeAt(utility.length - 1) === 33) utility = utility.slice(0, -1);
1957
+ if (utility.charCodeAt(0) === 45) utility = utility.slice(1);
1958
+ return utility.startsWith("text-") || utility.startsWith("font-") || resolve(utility) === null;
1959
+ }
1960
+ function warnUnpublishedTheme(classes, resolve) {
1961
+ if (_warnedNoSnapshot || hasFinalizedSnapshot()) return;
1962
+ let offender;
1963
+ for (const cls of classes) {
1964
+ if (isThemeDependent(cls, resolve)) {
1965
+ offender = cls;
1966
+ break;
1967
+ }
1968
+ }
1969
+ if (offender === void 0) return;
1970
+ _warnedNoSnapshot = true;
1971
+ console.warn(
1972
+ `[RI-2004] ri() merged "${offender}" with no theme published, so its meaning was guessed. Since 0.6.0 text sizes, weights, font slots, and color names are all project-defined, and without them a size can be read as a color and dropped. With Vite this is automatic \u2014 check the plugin is installed. Otherwise run \`rainbowindex generate-snapshot\` and import the generated module once at startup, or pass a snapshot to createRi(snapshot). This warns once per process. See: https://rainbowindex.dev/docs/ssr`
1973
+ );
1974
+ }
2493
1975
  var warnFlattenDepth = throttledWarn(
2494
1976
  () => `[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.`
2495
1977
  );
@@ -2504,10 +1986,7 @@ var warnNonStringInput = throttledWarn(
2504
1986
  devWarn
2505
1987
  );
2506
1988
  function ri(...inputs) {
2507
- if (detectSSR()) {
2508
- warnSSRUsage();
2509
- }
2510
- return mergeFrom(inputs, resolveProps, defaultRiCache);
1989
+ return mergeFrom(inputs, resolveProps, defaultRiCache, shouldCheckSnapshot());
2511
1990
  }
2512
1991
  function createRi(snapshot) {
2513
1992
  const resolve = resolverFor(snapshot);
@@ -2595,32 +2074,8 @@ function splitBracketAware(input) {
2595
2074
  }
2596
2075
 
2597
2076
  export {
2598
- isValidColorSuffix,
2599
- DEFAULT_COLORS,
2600
- DEFAULT_DARK_CONFIG,
2601
- oklchToOklab,
2602
- oklabToLinearSrgb,
2603
- linearToSrgb,
2604
- generateStop,
2605
- computeDarkStop,
2606
- formatOklch,
2607
- checkPaletteContrast,
2608
- generateAllColorVariables,
2609
- generateThemeOverrides,
2610
- DEFAULT_TEXT,
2611
- DEFAULT_BREAKPOINTS,
2612
- CORNER_SHAPE_KEYWORDS,
2613
- DEFAULT_CORNER_SCALE,
2614
- DEFAULT_SUPERELLIPSE_SCALE,
2615
- DEFAULT_SHADOWS,
2616
- DEFAULT_WEIGHTS,
2617
- DEFAULT_EASING,
2618
- DEFAULT_BLUR,
2619
- DEFAULT_ANIMATIONS,
2620
- DEFAULT_FLUID,
2621
- DEFAULT_TRACKING,
2622
- DEFAULT_LEADING,
2623
- defaultTheme,
2077
+ IS_DEV,
2078
+ devWarn,
2624
2079
  PADDING_MAP,
2625
2080
  MARGIN_MAP,
2626
2081
  GAP_MAP,
@@ -2628,17 +2083,16 @@ export {
2628
2083
  SCROLL_MARGIN_MAP,
2629
2084
  SCROLL_PADDING_MAP,
2630
2085
  BORDER_DIR_PROPS,
2086
+ FILTER_COMPOSED,
2087
+ BACKDROP_FILTER_COMPOSED,
2088
+ BUILTIN_STATIC_KEYS,
2089
+ PREFIX_PROP_KEYS,
2090
+ buildFirstSegmentMap,
2631
2091
  COLOR_FUNCTION_ALTERNATION,
2632
2092
  SPECIAL_COLORS,
2633
2093
  isMaskRadialSizeValue,
2634
2094
  RE_IMAGE_VALUE,
2635
2095
  isFontFamilyValue,
2636
- BUILTIN_STATIC_KEYS,
2637
- PREFIX_PROP_KEYS,
2638
- buildFirstSegmentMap,
2639
- IS_DEV,
2640
- devWarn,
2641
- DEFAULT_TEXT_SIZES,
2642
2096
  resolverFor,
2643
2097
  createCompilationContext,
2644
2098
  registerCustomUtility,
@@ -2646,7 +2100,10 @@ export {
2646
2100
  registerCustomFontFamilies,
2647
2101
  registerColorNames,
2648
2102
  snapshotCompilationContext,
2103
+ publishSnapshot,
2649
2104
  finalizeCompilationContext,
2105
+ serializeSnapshot,
2106
+ hydrateSnapshot,
2650
2107
  mergeUncached,
2651
2108
  ri,
2652
2109
  createRi