rainbowindex 0.5.1 → 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,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;
@@ -763,10 +155,11 @@ var BUILTIN_STATIC_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
763
155
  // outline shorthand + offset (claiming all sub-properties); outline-none just sets style.
764
156
  outline: ["outline-width"],
765
157
  "outline-hidden": ["outline-style", "outline-width", "outline-color", "outline-offset"],
766
- // Shadow / ring (static bare + reset forms; valued forms via PREFIX_PROPS).
158
+ // Shadow / ring (static reset + bare ring forms; valued forms via PREFIX_PROPS).
767
159
  // Each composable family claims box-shadow (shared) + its own slot var, so
768
160
  // shadow/inset-shadow/ring/inset-ring coexist while same-family repeats dedupe.
769
- 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.
770
163
  "shadow-none": ["box-shadow", "--ri-shadow"],
771
164
  ring: ["box-shadow", "--ri-ring-shadow"],
772
165
  "inset-ring": ["box-shadow", "--ri-inset-ring-shadow"],
@@ -1311,23 +704,7 @@ addAll(
1311
704
  ],
1312
705
  ["~divide:border-style"]
1313
706
  );
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
- );
707
+ addAll(["animate-in", "animate-out", "animate-none"], ["animation"]);
1331
708
  addAll(["animate-infinite", "animate-once", "animate-twice"], ["animation-iteration-count"]);
1332
709
  addAll(
1333
710
  ["animate-fill-none", "animate-fill-forwards", "animate-fill-both", "animate-fill-backwards"],
@@ -1530,6 +907,9 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1530
907
  ...MARGIN_MAP,
1531
908
  ...GAP_MAP,
1532
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"],
1533
913
  "space-x": ["~space:margin-inline-start", "~space:margin-inline-end"],
1534
914
  "space-y": ["~space:margin-block-start", "~space:margin-block-end"],
1535
915
  ...INSET_MAP,
@@ -1677,10 +1057,13 @@ var PREFIX_PROPS = Object.assign(/* @__PURE__ */ Object.create(null), {
1677
1057
  "from-position": ["--ri-gradient-from-position"],
1678
1058
  "via-position": ["--ri-gradient-via-position"],
1679
1059
  "to-position": ["--ri-gradient-to-position"],
1680
- // 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.
1681
1063
  "animate-duration": ["animation-duration"],
1682
1064
  "animate-delay": ["animation-delay"],
1683
1065
  "animate-ease": ["animation-timing-function"],
1066
+ animate: ["animation"],
1684
1067
  "break-before": ["break-before"],
1685
1068
  "break-after": ["break-after"],
1686
1069
  "break-inside": ["break-inside"],
@@ -2031,17 +1414,6 @@ function scanBracketAware(input, onChar, options) {
2031
1414
  }
2032
1415
 
2033
1416
  // 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
1417
  var DEFAULT_FONT_FAMILIES = ["sans", "serif", "mono"];
2046
1418
  function stripTextModifier(value) {
2047
1419
  if (value.indexOf("/") === -1) return value;
@@ -2217,7 +1589,7 @@ function resolvePropsWith(utility, customStaticProps, customFunctionalProps, tex
2217
1589
  // src/merge/context.ts
2218
1590
  var _customStaticProps = {};
2219
1591
  var _customFunctionalProps = [];
2220
- var _textSizes = new Set(DEFAULT_TEXT_SIZES);
1592
+ var _textSizes = /* @__PURE__ */ new Set();
2221
1593
  var _fontFamilies = new Set(DEFAULT_FONT_FAMILIES);
2222
1594
  var _colorNames = /* @__PURE__ */ new Set();
2223
1595
  var _latestSnapshot = null;
@@ -2289,7 +1661,7 @@ function createCompilationContext() {
2289
1661
  return {
2290
1662
  customStaticProps: {},
2291
1663
  customFunctionalProps: {},
2292
- textSizes: new Set(DEFAULT_TEXT_SIZES),
1664
+ textSizes: /* @__PURE__ */ new Set(),
2293
1665
  fontFamilies: new Set(DEFAULT_FONT_FAMILIES),
2294
1666
  colorNames: /* @__PURE__ */ new Set()
2295
1667
  };
@@ -2359,268 +1731,696 @@ function findVariantSplit(cls) {
2359
1731
  );
2360
1732
  return lastColon;
2361
1733
  }
2362
- function canonicalVariantPrefix(variantPrefix) {
2363
- if (variantPrefix.indexOf(":") === variantPrefix.length - 1) return variantPrefix;
2364
- const segments = [];
2365
- let start = 0;
2366
- scanBracketAware(variantPrefix, (ch, i, depth) => {
2367
- if (ch === ":" && depth === 0) {
2368
- segments.push(variantPrefix.slice(start, i));
2369
- start = i + 1;
1734
+ function canonicalVariantPrefix(variantPrefix) {
1735
+ if (variantPrefix.indexOf(":") === variantPrefix.length - 1) return variantPrefix;
1736
+ const segments = [];
1737
+ let start = 0;
1738
+ scanBracketAware(variantPrefix, (ch, i, depth) => {
1739
+ if (ch === ":" && depth === 0) {
1740
+ segments.push(variantPrefix.slice(start, i));
1741
+ start = i + 1;
1742
+ }
1743
+ });
1744
+ if (segments.length < 2) return variantPrefix;
1745
+ segments.sort();
1746
+ return `${segments.join(":")}:`;
1747
+ }
1748
+ function mergeUncached(classes, resolve, trace) {
1749
+ const claimed = /* @__PURE__ */ new Set();
1750
+ const result = [];
1751
+ for (let i = classes.length - 1; i >= 0; i--) {
1752
+ const cls = classes[i];
1753
+ const splitIdx = findVariantSplit(cls);
1754
+ const utility = splitIdx === -1 ? cls : cls.slice(splitIdx + 1);
1755
+ const important = utility.charCodeAt(utility.length - 1) === 33;
1756
+ const cleanUtility = important ? utility.slice(0, -1) : utility;
1757
+ const props = resolve(cleanUtility);
1758
+ if (!props) {
1759
+ result.push(cls);
1760
+ continue;
1761
+ }
1762
+ let ns = splitIdx === -1 ? "" : canonicalVariantPrefix(cls.slice(0, splitIdx + 1));
1763
+ if (important) ns = `!${ns}`;
1764
+ let dominated = true;
1765
+ for (const prop of props) {
1766
+ if (!claimed.has(ns + prop)) {
1767
+ dominated = false;
1768
+ break;
1769
+ }
1770
+ }
1771
+ if (dominated) {
1772
+ if (trace) {
1773
+ const by = /* @__PURE__ */ new Set();
1774
+ for (const prop of props) {
1775
+ const claimer = trace.claimers.get(ns + prop);
1776
+ if (claimer !== void 0) by.add(claimer);
1777
+ }
1778
+ trace.dropped.push({ index: i, overriddenBy: [...by].sort((a, b) => a - b) });
1779
+ }
1780
+ continue;
1781
+ }
1782
+ for (const prop of props) {
1783
+ const key = ns + prop;
1784
+ if (trace && !claimed.has(key)) trace.claimers.set(key, i);
1785
+ claimed.add(key);
1786
+ const longhands = OVERRIDES[prop];
1787
+ if (longhands !== void 0) {
1788
+ for (const lh of longhands) {
1789
+ const longhandKey = ns + lh;
1790
+ if (trace && !claimed.has(longhandKey)) trace.claimers.set(longhandKey, i);
1791
+ claimed.add(longhandKey);
1792
+ }
1793
+ }
1794
+ }
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));
2370
1853
  }
2371
- });
2372
- if (segments.length < 2) return variantPrefix;
2373
- segments.sort();
2374
- return `${segments.join(":")}:`;
1854
+ };
2375
1855
  }
2376
- function mergeUncached(classes, resolve, trace) {
2377
- const claimed = /* @__PURE__ */ new Set();
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) {
2378
1895
  const result = [];
2379
- for (let i = classes.length - 1; i >= 0; i--) {
2380
- const cls = classes[i];
2381
- const splitIdx = findVariantSplit(cls);
2382
- const utility = splitIdx === -1 ? cls : cls.slice(splitIdx + 1);
2383
- const important = utility.charCodeAt(utility.length - 1) === 33;
2384
- const cleanUtility = important ? utility.slice(0, -1) : utility;
2385
- const props = resolve(cleanUtility);
2386
- if (!props) {
2387
- result.push(cls);
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();
2388
1902
  continue;
2389
1903
  }
2390
- let ns = splitIdx === -1 ? "" : canonicalVariantPrefix(cls.slice(0, splitIdx + 1));
2391
- if (important) ns = `!${ns}`;
2392
- let dominated = true;
2393
- for (const prop of props) {
2394
- if (!claimed.has(ns + prop)) {
2395
- dominated = false;
2396
- break;
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;
2397
1910
  }
2398
- }
2399
- if (dominated) {
2400
- if (trace) {
2401
- const by = /* @__PURE__ */ new Set();
2402
- for (const prop of props) {
2403
- const claimer = trace.claimers.get(ns + prop);
2404
- if (claimer !== void 0) by.add(claimer);
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
+ }
2405
1929
  }
2406
- trace.dropped.push({ index: i, overriddenBy: [...by].sort((a, b) => a - b) });
2407
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++;
2408
1948
  continue;
2409
1949
  }
2410
- for (const prop of props) {
2411
- const key = ns + prop;
2412
- if (trace && !claimed.has(key)) trace.claimers.set(key, i);
2413
- claimed.add(key);
2414
- const longhands = OVERRIDES[prop];
2415
- if (longhands !== void 0) {
2416
- for (const lh of longhands) {
2417
- const longhandKey = ns + lh;
2418
- if (trace && !claimed.has(longhandKey)) trace.claimers.set(longhandKey, i);
2419
- claimed.add(longhandKey);
2420
- }
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;
2421
2124
  }
2422
2125
  }
2423
- result.push(cls);
2424
2126
  }
2425
- result.reverse();
2426
- 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;
2427
2130
  }
2428
- function mergeClasses(classes, resolve, cache) {
2429
- const cacheKey = classes.join("\0");
2430
- const useCache = cacheKey.length <= RI_CACHE_KEY_MAX_LEN;
2431
- if (useCache) {
2432
- const cached = cache.get(cacheKey);
2433
- if (cached !== void 0) return cached;
2434
- }
2435
- const output = mergeUncached(classes, resolve);
2436
- if (useCache) cache.put(cacheKey, output);
2437
- return output;
2131
+ function lightnessFromSuffix(suffix) {
2132
+ return Math.min(0.98, Math.max(0.02, sampleProfile(L_PROFILE, suffix, true)));
2438
2133
  }
2439
- function mergeFrom(inputs, resolve, cache) {
2440
- let fastPath = true;
2441
- let rawKey = "";
2442
- for (let i = 0; i < inputs.length; i++) {
2443
- const input = inputs[i];
2444
- if (typeof input === "string") {
2445
- if (input !== "") rawKey = rawKey === "" ? input : `${rawKey}\0${input}`;
2446
- } else if (input) {
2447
- fastPath = false;
2448
- break;
2449
- }
2134
+ var DEFAULT_COLORS = Object.freeze({
2135
+ theme: {
2136
+ type: "generative",
2137
+ chroma: 0,
2138
+ hue: 0,
2139
+ parabolic: false,
2140
+ chromaBoost: false
2450
2141
  }
2451
- if (fastPath) {
2452
- if (rawKey === "") return "";
2453
- if (rawKey.length <= RI_CACHE_KEY_MAX_LEN) {
2454
- const cached = cache.get(rawKey);
2455
- if (cached !== void 0) return cached;
2456
- const classes2 = flattenInputs(inputs);
2457
- const output = classes2.length === 0 ? "" : classes2.length === 1 ? classes2[0] : mergeUncached(classes2, resolve);
2458
- cache.put(rawKey, output);
2459
- return output;
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)];
2154
+ }
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];
2166
+ }
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;
2172
+ }
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;
2460
2184
  }
2185
+ if (hi - lo < 5e-4) break;
2461
2186
  }
2462
- const classes = flattenInputs(inputs);
2463
- if (classes.length === 0) return "";
2464
- if (classes.length === 1) return classes[0];
2465
- return mergeClasses(classes, resolve, cache);
2187
+ return lo;
2466
2188
  }
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;
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;
2472
2204
  }
2473
- var WARNING_THROTTLE_MS = 6e4;
2474
- function throttledWarn(message, emit = console.warn) {
2475
- let last = Number.NEGATIVE_INFINITY;
2476
- return (...args) => {
2477
- const now = Date.now();
2478
- if (now - last >= WARNING_THROTTLE_MS) {
2479
- last = now;
2480
- emit(message(...args));
2481
- }
2482
- };
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;
2483
2212
  }
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
- );
2493
- var warnFlattenDepth = throttledWarn(
2494
- () => `[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
- );
2496
- var warnTotalClasses = throttledWarn(
2497
- () => `[RI-2012] ri() input exceeds ${MAX_TOTAL_CLASSES} class limit. Excess classes are dropped to prevent memory exhaustion.`
2498
- );
2499
- var warnClassLength = throttledWarn(
2500
- (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.`
2501
- );
2502
- var warnNonStringInput = throttledWarn(
2503
- (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.`,
2504
- devWarn
2505
- );
2506
- function ri(...inputs) {
2507
- if (detectSSR()) {
2508
- 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;
2509
2261
  }
2510
- 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 };
2511
2267
  }
2512
- function createRi(snapshot) {
2513
- const resolve = resolverFor(snapshot);
2514
- const cache = new RiCache(RI_CACHE_MAX);
2515
- return function boundRi(...inputs) {
2516
- return mergeFrom(inputs, resolve, cache);
2517
- };
2268
+ function formatOklch(l, c, h) {
2269
+ return `oklch(${l} ${c} ${h})`;
2518
2270
  }
2519
- var MAX_FLATTEN_DEPTH = 10;
2520
- var MAX_CLASS_NAME_LENGTH = 500;
2521
- var MAX_TOTAL_CLASSES = 1e4;
2522
- function flattenInputs(inputs) {
2523
- const result = [];
2524
- const stack = [[inputs, 0, 0]];
2525
- while (stack.length > 0) {
2526
- const top = stack[stack.length - 1];
2527
- const [arr, , depth] = top;
2528
- if (top[1] >= arr.length) {
2529
- stack.pop();
2530
- 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});`);
2531
2287
  }
2532
- const input = arr[top[1]++];
2533
- if (!input) continue;
2534
- if (Array.isArray(input)) {
2535
- if (depth + 1 > MAX_FLATTEN_DEPTH) {
2536
- warnFlattenDepth();
2537
- 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
+ );
2538
2316
  }
2539
- stack.push([input, 0, depth + 1]);
2540
- } else if (typeof input !== "string") {
2541
- if (IS_DEV) {
2542
- 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;
2543
2332
  }
2544
- } else {
2545
- const trimmed = input.trim();
2546
- if (trimmed) {
2547
- for (const cls of splitBracketAware(trimmed)) {
2548
- if (result.length >= MAX_TOTAL_CLASSES) {
2549
- warnTotalClasses();
2550
- return result;
2551
- }
2552
- if (cls.length <= MAX_CLASS_NAME_LENGTH) {
2553
- result.push(cls);
2554
- } else {
2555
- 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});`);
2556
2353
  }
2354
+ } else if (source) {
2355
+ allVars.push(`--color-${name}: var(--color-${def.source});`);
2557
2356
  }
2357
+ break;
2558
2358
  }
2559
2359
  }
2560
2360
  }
2561
- return result;
2361
+ return allVars;
2562
2362
  }
2563
- function splitBracketAware(input) {
2564
- if (!input.includes("[") && !input.includes("(")) {
2565
- const trimmed = input.trim();
2566
- return trimmed === "" ? [] : trimmed.split(WS_SPLIT_RE);
2567
- }
2568
- const result = [];
2569
- let tokenStart = -1;
2570
- let depth = 0;
2571
- for (let i = 0; i < input.length; i++) {
2572
- const ch = input[i];
2573
- if (ch === "\\" && i + 1 < input.length) {
2574
- if (tokenStart === -1) tokenStart = i;
2575
- i++;
2576
- continue;
2577
- }
2578
- if (ch === "[" || ch === "(") {
2579
- depth++;
2580
- if (tokenStart === -1) tokenStart = i;
2581
- } else if (ch === "]" || ch === ")") {
2582
- if (depth > 0) depth--;
2583
- if (tokenStart === -1) tokenStart = i;
2584
- } else if (depth === 0 && (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f")) {
2585
- if (tokenStart !== -1) {
2586
- result.push(input.slice(tokenStart, i));
2587
- tokenStart = -1;
2588
- }
2589
- } else {
2590
- 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});`);
2591
2375
  }
2376
+ const safeName = name.replace(/[\\"'\]]/g, "");
2377
+ if (!safeName) continue;
2378
+ blocks.push(`[data-theme="${safeName}"] {
2379
+ ${vars.join("\n")}
2380
+ }`);
2592
2381
  }
2593
- if (tokenStart !== -1) result.push(input.slice(tokenStart));
2594
- return result;
2382
+ return blocks;
2595
2383
  }
2596
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
+
2597
2421
  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,
2422
+ IS_DEV,
2423
+ devWarn,
2624
2424
  PADDING_MAP,
2625
2425
  MARGIN_MAP,
2626
2426
  GAP_MAP,
@@ -2636,9 +2436,6 @@ export {
2636
2436
  BUILTIN_STATIC_KEYS,
2637
2437
  PREFIX_PROP_KEYS,
2638
2438
  buildFirstSegmentMap,
2639
- IS_DEV,
2640
- devWarn,
2641
- DEFAULT_TEXT_SIZES,
2642
2439
  resolverFor,
2643
2440
  createCompilationContext,
2644
2441
  registerCustomUtility,
@@ -2649,5 +2446,21 @@ export {
2649
2446
  finalizeCompilationContext,
2650
2447
  mergeUncached,
2651
2448
  ri,
2652
- 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
2653
2466
  };