@barocss/kit 0.10.3 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -271,249 +271,571 @@ function clearContextCaches(ctx) {
271
271
  state.failures.clear();
272
272
  }
273
273
  //#endregion
274
- //#region src/core/registry.ts
275
- var utilityRegistry = [];
276
- function registerUtility(util, ctx) {
277
- const state = ctx && getContextState(ctx);
278
- if (ctx && !state) throw new Error("Utility registration requires a context from createContext");
279
- (state?.utilities || utilityRegistry).push(util);
280
- if (ctx) clearContextCaches(ctx);
281
- else {
282
- parseResultCache.clear();
283
- utilityCache.clear();
284
- }
285
- }
286
- function getUtility(ctx) {
287
- return ctx && getContextState(ctx)?.utilities || utilityRegistry;
288
- }
289
- var modifierRegistry = [];
274
+ //#region src/core/utils.ts
290
275
  /**
291
- * staticModifier: A helper that registers a modifier name and an array of CSS selectors directly to the registry
292
- *
293
- * @example
294
- * ```
295
- * staticModifier('disabled', ['&:disabled'], { source: 'pseudo' });
296
- * ```
297
- *
298
- * @param name The name of the modifier
299
- * @param selectors The selectors of the modifier
300
- * @param options The options of the modifier
301
- *
302
- * @returns {void}
276
+ * Parses a fraction string (e.g., '1/2') and returns a percentage string (e.g., '50%'), or null if not a valid fraction.
303
277
  */
304
- function staticModifier(name, selectors, options = {}, ctx) {
305
- registerModifier({
306
- name,
307
- match: (mod) => mod === name,
308
- modifySelector: ({ ..._rest }) => {
309
- return selectors.map((sel) => ({
310
- selector: sel,
311
- source: options.source
312
- }));
313
- },
314
- ...options
315
- }, ctx);
316
- }
317
- function functionalModifier(match, modifySelector, wrap, options = {}, ctx) {
318
- registerModifier({
319
- match,
320
- modifySelector,
321
- wrap,
322
- ...options
323
- }, ctx);
324
- }
325
- function registerModifier(modifier, ctx) {
326
- const state = ctx && getContextState(ctx);
327
- if (ctx && !state) throw new Error("Modifier registration requires a context from createContext");
328
- (state?.modifiers || modifierRegistry).push(modifier);
329
- if (ctx) clearContextCaches(ctx);
330
- }
331
- function getModifier(ctx) {
332
- return ctx && getContextState(ctx)?.modifiers || modifierRegistry;
333
- }
334
- var ESCAPE_REGEX = /[^A-Za-z0-9_-]/gu;
335
- var UNSAFE_RAW = /^[\s\p{C}\p{Z}]$/u;
336
- function escapeClassName(className) {
337
- if (className === "-") return "\\-";
338
- const lead = /^-?[0-9]/.exec(className);
339
- if (lead) {
340
- const i = lead[0].length - 1;
341
- return className.slice(0, i) + "\\" + className.charCodeAt(i).toString(16) + " " + escapeRest(className.slice(i + 1));
278
+ function parseFraction(input) {
279
+ if (input.includes("/")) {
280
+ const [num, denom] = input.split("/").map(Number);
281
+ if (!isNaN(num) && !isNaN(denom) && denom !== 0) return `${num / denom * 100}%`;
342
282
  }
343
- return escapeRest(className);
344
- }
345
- function escapeRest(className) {
346
- return className.replace(ESCAPE_REGEX, (c) => {
347
- if (c === " ") return "\\x20 ";
348
- if (c === ".") return "\\.";
349
- if (c === "/") return "\\/";
350
- if (c === ":") return "\\:";
351
- if (c === "[") return "\\[";
352
- if (c === "]") return "\\]";
353
- if (c === "(") return "\\(";
354
- if (c === ")") return "\\)";
355
- if (c === "%") return "\\%";
356
- if (c === "#") return "\\#";
357
- if (c === ",") return "\\,";
358
- if (c === "=") return "\\=";
359
- if (c === "&") return "\\&";
360
- if (c === "~") return "\\~";
361
- if (c === "*") return "\\*";
362
- if (c === "$") return "\\$";
363
- if (c === "^") return "\\^";
364
- if (c === "+") return "\\+";
365
- if (c === "?") return "\\?";
366
- if (c === "!") return "\\!";
367
- if (c === "@") return "\\@";
368
- if (c === "'") return "\\'";
369
- if (c === "\"") return "\\\"";
370
- if (c === "`") return "\\`";
371
- if (c === ";") return "\\;";
372
- if (c === "<") return "\\<";
373
- if (c === ">") return "\\>";
374
- if (c === "{") return "\\{";
375
- if (c === "}") return "\\}";
376
- if (c === "|") return "\\|";
377
- if (c === "\\") return "\\\\";
378
- if (UNSAFE_RAW.test(c)) return "\\" + c.codePointAt(0).toString(16) + " ";
379
- return "\\" + c;
380
- });
283
+ return null;
381
284
  }
382
285
  /**
383
- * staticUtility: A helper that registers a utility name and an array of CSS declaration pairs directly to the registry
384
- *
385
- * @example
386
- * ```
387
- * staticUtility('block', [['display', 'block']]);
388
- * staticUtility('hidden', [['display', 'none']]);
389
- * staticUtility('space-x-px', [
390
- * [
391
- * '& > :not([hidden]) ~ :not([hidden])', // selector
392
- * [
393
- * ['margin-inline-start', '1px'], // [prop, value]
394
- * ['margin-inline-end', '1px'], // [prop, value]
395
- * ],
396
- * ],
397
- * ]);
398
- * ```
399
- *
400
- * @param name The name of the utility
401
- * @param decls The declarations of the utility
402
- * @param opts The options of the utility
286
+ * Returns the input if it is a valid non-negative integer string, else null.
403
287
  *
404
- * @returns {void}
288
+ * @example
289
+ * parseNumber("10") // "10"
290
+ * parseNumber("-10") // "-10"
291
+ * parseNumber("10.5") // "10.5"
405
292
  */
406
- function staticUtility(name, decls, opts, ctx) {
407
- registerUtility({
408
- name,
409
- match: (className) => {
410
- return className === name;
411
- },
412
- handler: (value) => {
413
- return decls.flatMap((params) => {
414
- if (params.type) return [params];
415
- if (typeof params === "function") return [params(value)];
416
- const [a, b] = params;
417
- if (typeof b === "string") return [decl(a, b)];
418
- else if (Array.isArray(b)) return [rule(a, b.map(([prop, value]) => decl(prop, value)))];
419
- return [];
420
- });
421
- },
422
- description: opts?.description,
423
- category: opts?.category,
424
- priority: opts?.priority
425
- }, ctx);
293
+ function parseNumber(input) {
294
+ return /^-?\d+(?:\.\d+)?$/.test(input) ? input : null;
426
295
  }
427
296
  /**
428
- * functionalUtility: A helper that registers dynamic utilities (theme, arbitrary, custom, negative, fraction, etc.) directly
429
- *
430
- * Example:
431
- * functionalUtility({
432
- * name: 'z',
433
- * supportsNegative: true,
434
- * themeKeys: ['--z-index'],
435
- * handleBareValue: ({ value }) => isPositiveInteger(value) ? value : null,
436
- * handle: (value) => [decl('z-index', value)],
437
- * description: 'z-index utility',
438
- * category: 'layout',
439
- * });
297
+ * Returns the input if it is a valid length string, else null.
440
298
  */
299
+ function parseLength(input) {
300
+ return /^\d+(px|em|rem|vh|vw|vmin|vmax|%|in|cm|mm|pt|pc|ex|ch|fr)$/.test(input) ? input : null;
301
+ }
441
302
  /**
442
- * #300: a theme key that no built-in utility names (`theme.extend.borderRadius.card`) still resolves, as in
443
- * Tailwind 4 where `--radius-card` gives `rounded-card`. Only word keys that start with a letter (numbers stay
444
- * bare values), never `DEFAULT`; unknown keys return null so the utility emits nothing (#213).
303
+ * Unified parser for fraction or number, with options for percent or repeat syntax.
304
+ * - percent: if true, returns percentage for fraction (e.g., '1/2' -> '50%')
305
+ * - repeat: if true, returns repeat() for number (e.g., '3' -> 'repeat(3, minmax(0, 1fr))')
445
306
  */
446
- function themeKeyEntry(ctx, namespace, key) {
447
- if (key === "DEFAULT" || !/^[a-zA-Z][\w-]*$/.test(key) || typeof ctx?.theme !== "function") return void 0;
448
- const table = ctx.theme(namespace);
449
- if (!table || typeof table !== "object" || !Object.prototype.hasOwnProperty.call(table, key)) return void 0;
450
- return table[key] ?? void 0;
451
- }
452
- /** #300: the literal value of `theme.<namespace>.<key>` when it is a string, else null. */
453
- function themeKeyValue(ctx, namespace, key) {
454
- const v = themeKeyEntry(ctx, namespace, key);
455
- return typeof v === "string" ? v : null;
456
- }
457
- /** #300: `var(--<varPrefix>-<key>)` when `theme.<namespace>.<key>` exists (the :root var BaroCSS emits for it), else null. */
458
- function themeKeyVar(ctx, namespace, key, varPrefix) {
459
- return themeKeyEntry(ctx, namespace, key) === void 0 ? null : `var(--${varPrefix}-${key})`;
460
- }
461
- /** #261: `var(--spacing-<key>)` for a named (non-numeric) `theme.spacing` key, else null. */
462
- function spacingKeyValue(ctx, key, negative) {
463
- if (key === "px" || !/^[a-zA-Z][\w-]*$/.test(key) || ctx.theme("spacing", key) == null) return null;
464
- const ref = `var(--spacing-${key})`;
465
- return negative ? `calc(${ref} * -1)` : ref;
307
+ function parseFractionOrNumber(value, opts = {}) {
308
+ if (/^\d+$/.test(value)) {
309
+ if (opts.repeat) return `repeat(${value}, minmax(0, 1fr))`;
310
+ return value;
311
+ }
312
+ if (value.includes("/")) {
313
+ const [numerator, denominator] = value.split("/").map(Number);
314
+ if (!isNaN(numerator) && !isNaN(denominator) && denominator !== 0) {
315
+ const result = numerator / denominator;
316
+ if (opts.percent) return `${result * 100}%`;
317
+ return result.toString();
318
+ }
319
+ }
320
+ return null;
466
321
  }
467
- function functionalUtility(opts, ctx) {
468
- registerUtility({
469
- name: opts.name,
470
- match: (className) => className.startsWith(opts.name + "-"),
471
- handler: (value, ctx, token, _options) => {
472
- let finalValue = value;
473
- const parsedUtility = token;
474
- const extra = { opacity: token.opacity };
475
- if (opts.supportsOpacity && !token.arbitrary && !token.customProperty && value.includes("/")) {
476
- const list = value.split("/");
477
- if (list.length >= 2) {
478
- extra.opacity = list.pop();
479
- finalValue = list.join("/");
480
- }
481
- }
482
- if (opts.supportsArbitrary && parsedUtility.arbitrary) {
483
- const processedValue = normalizeMathSpacing(expandThemeFunctions(finalValue.replace(/_/g, " ")));
484
- if (opts.handle) {
485
- const result = opts.handle(processedValue, ctx, token, extra);
486
- if (result) return result;
487
- }
488
- if (opts.prop) return [decl(opts.prop, processedValue)];
489
- return [];
490
- }
491
- if (opts.supportsCustomProperty && parsedUtility.customProperty) {
492
- if (opts.handleCustomProperty) return opts.handleCustomProperty(finalValue, ctx, token, extra);
493
- const customValue = `var(${finalValue})`;
494
- if (opts.handle) {
495
- const result = opts.handle(customValue, ctx, token, extra);
496
- if (result) return result;
497
- }
498
- if (opts.prop) return [decl(opts.prop, customValue)];
499
- return [];
500
- }
501
- let themeValue;
502
- if (opts.themeKey && ctx.theme) themeValue = themeScalar(ctx.theme(opts.themeKey, finalValue));
503
- let namespace = themeValue !== void 0 ? opts.themeKey : void 0;
504
- if (!themeValue && opts.themeKeys && ctx.theme) for (const key of opts.themeKeys) {
505
- themeValue = themeScalar(ctx.theme(key, finalValue));
506
- if (themeValue !== void 0) {
507
- namespace = key;
508
- break;
509
- }
510
- }
511
- if (themeValue !== void 0) {
512
- extra.themeNamespace = namespace;
513
- extra.themeKey = finalValue;
514
- if (namespace === "colors" || !(opts.themeKeys ?? [opts.themeKey]).includes("colors")) extra.realThemeValue = finalValue;
515
- finalValue = themeValue;
516
- if (opts.prop) return [decl(opts.prop, finalValue)];
322
+ var CSS_COLOR_NAMES = /* @__PURE__ */ new Set([
323
+ "aliceblue",
324
+ "antiquewhite",
325
+ "aqua",
326
+ "aquamarine",
327
+ "azure",
328
+ "beige",
329
+ "bisque",
330
+ "black",
331
+ "blanchedalmond",
332
+ "blue",
333
+ "blueviolet",
334
+ "brown",
335
+ "burlywood",
336
+ "cadetblue",
337
+ "chartreuse",
338
+ "chocolate",
339
+ "coral",
340
+ "cornflowerblue",
341
+ "cornsilk",
342
+ "crimson",
343
+ "cyan",
344
+ "darkblue",
345
+ "darkcyan",
346
+ "darkgoldenrod",
347
+ "darkgray",
348
+ "darkgreen",
349
+ "darkgrey",
350
+ "darkkhaki",
351
+ "darkmagenta",
352
+ "darkolivegreen",
353
+ "darkorange",
354
+ "darkorchid",
355
+ "darkred",
356
+ "darksalmon",
357
+ "darkseagreen",
358
+ "darkslateblue",
359
+ "darkslategray",
360
+ "darkslategrey",
361
+ "darkturquoise",
362
+ "darkviolet",
363
+ "deeppink",
364
+ "deepskyblue",
365
+ "dimgray",
366
+ "dimgrey",
367
+ "dodgerblue",
368
+ "firebrick",
369
+ "floralwhite",
370
+ "forestgreen",
371
+ "fuchsia",
372
+ "gainsboro",
373
+ "ghostwhite",
374
+ "gold",
375
+ "goldenrod",
376
+ "gray",
377
+ "grey",
378
+ "green",
379
+ "greenyellow",
380
+ "honeydew",
381
+ "hotpink",
382
+ "indianred",
383
+ "indigo",
384
+ "ivory",
385
+ "khaki",
386
+ "lavender",
387
+ "lavenderblush",
388
+ "lawngreen",
389
+ "lemonchiffon",
390
+ "lightblue",
391
+ "lightcoral",
392
+ "lightcyan",
393
+ "lightgoldenrodyellow",
394
+ "lightgray",
395
+ "lightgreen",
396
+ "lightgrey",
397
+ "lightpink",
398
+ "lightsalmon",
399
+ "lightseagreen",
400
+ "lightskyblue",
401
+ "lightslategray",
402
+ "lightslategrey",
403
+ "lightsteelblue",
404
+ "lightyellow",
405
+ "lime",
406
+ "limegreen",
407
+ "linen",
408
+ "magenta",
409
+ "maroon",
410
+ "mediumaquamarine",
411
+ "mediumblue",
412
+ "mediumorchid",
413
+ "mediumpurple",
414
+ "mediumseagreen",
415
+ "mediumslateblue",
416
+ "mediumspringgreen",
417
+ "mediumturquoise",
418
+ "mediumvioletred",
419
+ "midnightblue",
420
+ "mintcream",
421
+ "mistyrose",
422
+ "moccasin",
423
+ "navajowhite",
424
+ "navy",
425
+ "oldlace",
426
+ "olive",
427
+ "olivedrab",
428
+ "orange",
429
+ "orangered",
430
+ "orchid",
431
+ "palegoldenrod",
432
+ "palegreen",
433
+ "paleturquoise",
434
+ "palevioletred",
435
+ "papayawhip",
436
+ "peachpuff",
437
+ "peru",
438
+ "pink",
439
+ "plum",
440
+ "powderblue",
441
+ "purple",
442
+ "red",
443
+ "rosybrown",
444
+ "royalblue",
445
+ "saddlebrown",
446
+ "salmon",
447
+ "sandybrown",
448
+ "seagreen",
449
+ "seashell",
450
+ "sienna",
451
+ "silver",
452
+ "skyblue",
453
+ "slateblue",
454
+ "slategray",
455
+ "slategrey",
456
+ "snow",
457
+ "springgreen",
458
+ "steelblue",
459
+ "tan",
460
+ "teal",
461
+ "thistle",
462
+ "tomato",
463
+ "turquoise",
464
+ "violet",
465
+ "wheat",
466
+ "white",
467
+ "whitesmoke",
468
+ "yellow",
469
+ "yellowgreen"
470
+ ]);
471
+ /**
472
+ * Returns the input if it is a valid color string, else null.
473
+ *
474
+ * #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(r, g, b), rgb(r, g, b, a), hsl(h, s, l), hsl(h, s, l, a), hwb(h, w, b), hwb(h, w, b, a), lab(l, a, b), lab(l, a, b, a), lch(l, c, h), lch(l, c, h, a), oklab(l, a, b), oklab(l, a, b, a), oklch(l, c, h), oklch(l, c, h, a), color-mix(in oklab, var(--color-blue-500) 60%, transparent)
475
+ */
476
+ function parseColor(input) {
477
+ if (CSS_COLOR_NAMES.has(input.toLowerCase())) return input;
478
+ if (input.startsWith("color:var(")) return input.slice(6);
479
+ if (input.startsWith("color:")) return parseColor(input.slice(6));
480
+ if (input.startsWith("#") && input.length === 4) return `#${input.slice(1)}`;
481
+ if (input.startsWith("#") && input.length === 5) return `#${input.slice(1)}`;
482
+ if (input.startsWith("#") && input.length === 7) return `#${input.slice(1)}`;
483
+ if (input.startsWith("#") && input.length === 9) return `#${input.slice(1)}`;
484
+ if (input.startsWith("rgb(")) return input.slice(4, -1);
485
+ if (input.startsWith("rgba(")) return input.slice(5, -1);
486
+ if (input.startsWith("hsl(")) return input.slice(4, -1);
487
+ if (input.startsWith("hsla(")) return input.slice(5, -1);
488
+ if (input.startsWith("hwb(")) return input.slice(4, -1);
489
+ if (input.startsWith("lab(")) return input.slice(4, -1);
490
+ if (input.startsWith("lch(")) return input.slice(4, -1);
491
+ if (input.startsWith("oklab(")) return input.slice(5, -1);
492
+ if (input.startsWith("oklch(")) return input.slice(6, -1);
493
+ if (input.startsWith("color-mix(")) return input.slice(9, -1);
494
+ return null;
495
+ }
496
+ var COLOR_KEYWORDS = /* @__PURE__ */ new Set([
497
+ "inherit",
498
+ "currentcolor",
499
+ "transparent"
500
+ ]);
501
+ /**
502
+ * Declarations for a theme colour (#228), as Tailwind v4 emits them: `var(--color-<key>)` so runtime theme
503
+ * overrides apply; with an opacity modifier, a literal srgb color-mix fallback plus an oklab color-mix of the var.
504
+ */
505
+ function themeColorDecls(prop, value, extra) {
506
+ const key = String(extra.realThemeValue);
507
+ const ref = COLOR_KEYWORDS.has(value.toLowerCase()) || value.startsWith("var(") || !/^[\w-]+$/.test(key) ? value : `var(--color-${key})`;
508
+ if (!extra.opacity) return [decl(prop, ref)];
509
+ const alpha = normalizeAlpha(String(extra.opacity));
510
+ if (!alpha) return [];
511
+ const supports = (amount) => atRule("supports", "(color:color-mix(in lab, red, red))", [decl(prop, `color-mix(in oklab, ${ref} ${amount}, transparent)`)]);
512
+ if (alpha.isVar) return [decl(prop, value), supports(alpha.amount)];
513
+ return [decl(prop, `color-mix(in srgb, ${value} ${alpha.amount}, transparent)`), supports(alpha.amount)];
514
+ }
515
+ /**
516
+ * Opacity modifier → color-mix amount, as Tailwind v4 does: `50` → `50%`, `[37%]` → `37%`, `[0.5]` / `[.8]` → `50%` /
517
+ * `80%` (a bracketed number ≤ 1 is a fraction), `[var(--a)]` / `(--a)` → `var(--a)`.
518
+ */
519
+ function normalizeAlpha(raw) {
520
+ const v = raw.trim();
521
+ const cp = /^\((--[\w-]+)\)$/.exec(v) ?? /^\[var\((--[\w-]+)\)\]$/.exec(v);
522
+ if (cp) return {
523
+ amount: `var(${cp[1]})`,
524
+ isVar: true
525
+ };
526
+ const m = /^(\[)?(\d+(?:\.\d+)?|\.\d+)(%)?(\])?$/.exec(v);
527
+ if (!m || !!m[1] !== !!m[4] || m[3] && !m[1]) return null;
528
+ const n = Number(m[2]);
529
+ return {
530
+ amount: `${+(m[3] ? n : m[1] && n <= 1 ? n * 100 : n).toFixed(4)}%`,
531
+ isVar: false
532
+ };
533
+ }
534
+ var MIX_SUPPORTS = "(color:color-mix(in lab, red, red))";
535
+ var COLOR_PROP = /(^|-)color$|^(fill|stroke)$|^--baro-gradient-(from|via|to)$/;
536
+ /**
537
+ * #393: an arbitrary or custom-property colour with an opacity modifier, as Tailwind 4.3.3 emits it: a literal
538
+ * colour with a literal alpha mixes directly (`color-mix(in oklab, #f00 50%, transparent)`); a var colour or a var
539
+ * alpha keeps the plain colour and mixes only under `@supports`. Returns null for an alpha it can't express.
540
+ */
541
+ function colorAlphaDecls(prop, color, opacity) {
542
+ const alpha = normalizeAlpha(opacity);
543
+ if (!alpha) return null;
544
+ const mix = `color-mix(in oklab, ${color} ${alpha.amount}, transparent)`;
545
+ if (alpha.isVar || color.startsWith("var(")) return [decl(prop, color), atRule("supports", MIX_SUPPORTS, [decl(prop, mix)])];
546
+ return [decl(prop, mix)];
547
+ }
548
+ /**
549
+ * #393: applies an opacity modifier to every declaration of `nodes` whose value is one of `colors` (the colour an
550
+ * arbitrary / custom-property utility emitted without the modifier). Returns null when none matched or the alpha
551
+ * is invalid, so the caller emits nothing rather than dropping the modifier or writing a malformed value.
552
+ */
553
+ function applyColorAlpha(nodes, colors, opacity) {
554
+ let matched = false;
555
+ let invalid = false;
556
+ const walk = (list) => list.flatMap((n) => {
557
+ if (n.type === "decl" && typeof n.value === "string" && colors.includes(n.value)) {
558
+ matched = true;
559
+ const out = COLOR_PROP.test(n.prop) ? colorAlphaDecls(n.prop, n.value, opacity) : null;
560
+ if (!out) invalid = true;
561
+ return out ?? [];
562
+ }
563
+ if (n.type === "at-rule" || n.type === "rule" || n.type === "style-rule" || n.type === "at-root") return [{
564
+ ...n,
565
+ nodes: walk(n.nodes)
566
+ }];
567
+ if (n.type === "wrap") return [{
568
+ ...n,
569
+ items: walk(n.items)
570
+ }];
571
+ return [n];
572
+ });
573
+ const out = walk(nodes);
574
+ return matched && !invalid ? out : null;
575
+ }
576
+ //#endregion
577
+ //#region src/core/registry.ts
578
+ /** #393: a handler result meaning "this class is invalid": the engine stops trying other registrations. */
579
+ var REJECT_CLASS = Object.freeze([]);
580
+ var utilityRegistry = [];
581
+ function registerUtility(util, ctx) {
582
+ const state = ctx && getContextState(ctx);
583
+ if (ctx && !state) throw new Error("Utility registration requires a context from createContext");
584
+ (state?.utilities || utilityRegistry).push(util);
585
+ if (ctx) clearContextCaches(ctx);
586
+ else {
587
+ parseResultCache.clear();
588
+ utilityCache.clear();
589
+ }
590
+ }
591
+ function getUtility(ctx) {
592
+ return ctx && getContextState(ctx)?.utilities || utilityRegistry;
593
+ }
594
+ var modifierRegistry = [];
595
+ /**
596
+ * staticModifier: A helper that registers a modifier name and an array of CSS selectors directly to the registry
597
+ *
598
+ * @example
599
+ * ```
600
+ * staticModifier('disabled', ['&:disabled'], { source: 'pseudo' });
601
+ * ```
602
+ *
603
+ * @param name The name of the modifier
604
+ * @param selectors The selectors of the modifier
605
+ * @param options The options of the modifier
606
+ *
607
+ * @returns {void}
608
+ */
609
+ function staticModifier(name, selectors, options = {}, ctx) {
610
+ registerModifier({
611
+ name,
612
+ match: (mod) => mod === name,
613
+ modifySelector: ({ ..._rest }) => {
614
+ return selectors.map((sel) => ({
615
+ selector: sel,
616
+ source: options.source
617
+ }));
618
+ },
619
+ ...options
620
+ }, ctx);
621
+ }
622
+ function functionalModifier(match, modifySelector, wrap, options = {}, ctx) {
623
+ registerModifier({
624
+ match,
625
+ modifySelector,
626
+ wrap,
627
+ ...options
628
+ }, ctx);
629
+ }
630
+ function registerModifier(modifier, ctx) {
631
+ const state = ctx && getContextState(ctx);
632
+ if (ctx && !state) throw new Error("Modifier registration requires a context from createContext");
633
+ (state?.modifiers || modifierRegistry).push(modifier);
634
+ if (ctx) clearContextCaches(ctx);
635
+ }
636
+ function getModifier(ctx) {
637
+ return ctx && getContextState(ctx)?.modifiers || modifierRegistry;
638
+ }
639
+ var ESCAPE_REGEX = /[^A-Za-z0-9_-]/gu;
640
+ var UNSAFE_RAW = /^[\s\p{C}\p{Z}]$/u;
641
+ function escapeClassName(className) {
642
+ if (className === "-") return "\\-";
643
+ const lead = /^-?[0-9]/.exec(className);
644
+ if (lead) {
645
+ const i = lead[0].length - 1;
646
+ return className.slice(0, i) + "\\" + className.charCodeAt(i).toString(16) + " " + escapeRest(className.slice(i + 1));
647
+ }
648
+ return escapeRest(className);
649
+ }
650
+ function escapeRest(className) {
651
+ return className.replace(ESCAPE_REGEX, (c) => {
652
+ if (c === " ") return "\\x20 ";
653
+ if (c === ".") return "\\.";
654
+ if (c === "/") return "\\/";
655
+ if (c === ":") return "\\:";
656
+ if (c === "[") return "\\[";
657
+ if (c === "]") return "\\]";
658
+ if (c === "(") return "\\(";
659
+ if (c === ")") return "\\)";
660
+ if (c === "%") return "\\%";
661
+ if (c === "#") return "\\#";
662
+ if (c === ",") return "\\,";
663
+ if (c === "=") return "\\=";
664
+ if (c === "&") return "\\&";
665
+ if (c === "~") return "\\~";
666
+ if (c === "*") return "\\*";
667
+ if (c === "$") return "\\$";
668
+ if (c === "^") return "\\^";
669
+ if (c === "+") return "\\+";
670
+ if (c === "?") return "\\?";
671
+ if (c === "!") return "\\!";
672
+ if (c === "@") return "\\@";
673
+ if (c === "'") return "\\'";
674
+ if (c === "\"") return "\\\"";
675
+ if (c === "`") return "\\`";
676
+ if (c === ";") return "\\;";
677
+ if (c === "<") return "\\<";
678
+ if (c === ">") return "\\>";
679
+ if (c === "{") return "\\{";
680
+ if (c === "}") return "\\}";
681
+ if (c === "|") return "\\|";
682
+ if (c === "\\") return "\\\\";
683
+ if (UNSAFE_RAW.test(c)) return "\\" + c.codePointAt(0).toString(16) + " ";
684
+ return "\\" + c;
685
+ });
686
+ }
687
+ /**
688
+ * staticUtility: A helper that registers a utility name and an array of CSS declaration pairs directly to the registry
689
+ *
690
+ * @example
691
+ * ```
692
+ * staticUtility('block', [['display', 'block']]);
693
+ * staticUtility('hidden', [['display', 'none']]);
694
+ * staticUtility('space-x-px', [
695
+ * [
696
+ * '& > :not([hidden]) ~ :not([hidden])', // selector
697
+ * [
698
+ * ['margin-inline-start', '1px'], // [prop, value]
699
+ * ['margin-inline-end', '1px'], // [prop, value]
700
+ * ],
701
+ * ],
702
+ * ]);
703
+ * ```
704
+ *
705
+ * @param name The name of the utility
706
+ * @param decls The declarations of the utility
707
+ * @param opts The options of the utility
708
+ *
709
+ * @returns {void}
710
+ */
711
+ function staticUtility(name, decls, opts, ctx) {
712
+ registerUtility({
713
+ name,
714
+ match: (className) => {
715
+ return className === name;
716
+ },
717
+ handler: (value) => {
718
+ return decls.flatMap((params) => {
719
+ if (params.type) return [params];
720
+ if (typeof params === "function") return [params(value)];
721
+ const [a, b] = params;
722
+ if (typeof b === "string") return [decl(a, b)];
723
+ else if (Array.isArray(b)) return [rule(a, b.map(([prop, value]) => decl(prop, value)))];
724
+ return [];
725
+ });
726
+ },
727
+ description: opts?.description,
728
+ category: opts?.category,
729
+ priority: opts?.priority
730
+ }, ctx);
731
+ }
732
+ /**
733
+ * functionalUtility: A helper that registers dynamic utilities (theme, arbitrary, custom, negative, fraction, etc.) directly
734
+ *
735
+ * Example:
736
+ * functionalUtility({
737
+ * name: 'z',
738
+ * supportsNegative: true,
739
+ * themeKeys: ['--z-index'],
740
+ * handleBareValue: ({ value }) => isPositiveInteger(value) ? value : null,
741
+ * handle: (value) => [decl('z-index', value)],
742
+ * description: 'z-index utility',
743
+ * category: 'layout',
744
+ * });
745
+ */
746
+ /**
747
+ * #300: a theme key that no built-in utility names (`theme.extend.borderRadius.card`) still resolves, as in
748
+ * Tailwind 4 where `--radius-card` gives `rounded-card`. Only word keys that start with a letter (numbers stay
749
+ * bare values), never `DEFAULT`; unknown keys return null so the utility emits nothing (#213).
750
+ */
751
+ function themeKeyEntry(ctx, namespace, key) {
752
+ if (key === "DEFAULT" || !/^[a-zA-Z][\w-]*$/.test(key) || typeof ctx?.theme !== "function") return void 0;
753
+ const table = ctx.theme(namespace);
754
+ if (!table || typeof table !== "object" || !Object.prototype.hasOwnProperty.call(table, key)) return void 0;
755
+ return table[key] ?? void 0;
756
+ }
757
+ /** #300: the literal value of `theme.<namespace>.<key>` when it is a string, else null. */
758
+ function themeKeyValue(ctx, namespace, key) {
759
+ const v = themeKeyEntry(ctx, namespace, key);
760
+ return typeof v === "string" ? v : null;
761
+ }
762
+ /** #300: `var(--<varPrefix>-<key>)` when `theme.<namespace>.<key>` exists (the :root var BaroCSS emits for it), else null. */
763
+ function themeKeyVar(ctx, namespace, key, varPrefix) {
764
+ return themeKeyEntry(ctx, namespace, key) === void 0 ? null : `var(--${varPrefix}-${key})`;
765
+ }
766
+ /** #261: `var(--spacing-<key>)` for a named (non-numeric) `theme.spacing` key, else null. */
767
+ function spacingKeyValue(ctx, key, negative) {
768
+ if (key === "px" || !/^[a-zA-Z][\w-]*$/.test(key) || ctx.theme("spacing", key) == null) return null;
769
+ const ref = `var(--spacing-${key})`;
770
+ return negative ? `calc(${ref} * -1)` : ref;
771
+ }
772
+ function functionalUtility(opts, ctx) {
773
+ registerUtility({
774
+ name: opts.name,
775
+ match: (className) => className.startsWith(opts.name + "-"),
776
+ handler: (value, ctx, token, _options) => {
777
+ let finalValue = value;
778
+ const parsedUtility = token;
779
+ const extra = { opacity: token.opacity };
780
+ if (opts.supportsOpacity && !token.arbitrary && !token.customProperty && value.includes("/")) {
781
+ const list = value.split("/");
782
+ if (list.length >= 2) {
783
+ extra.opacity = list.pop();
784
+ finalValue = list.join("/");
785
+ }
786
+ }
787
+ const splitModifier = !token.arbitrary && !token.customProperty && value.includes("/");
788
+ if (opts.supportsOpacity && (extra.opacity || splitModifier) && !normalizeAlpha(String(extra.opacity ?? ""))) {
789
+ const v = parsedUtility.arbitrary ? finalValue.replace(/_/g, " ") : finalValue;
790
+ return (parsedUtility.customProperty ? !/^[\w-]+:/.test(v) || v.startsWith("color:") : parsedUtility.arbitrary ? !!parseColor(v) || /^var\(--/.test(v) || v.startsWith("color:") : false) ? REJECT_CLASS : [];
791
+ }
792
+ const direct = (x) => {
793
+ if (opts.supportsArbitrary && parsedUtility.arbitrary) {
794
+ const processedValue = normalizeMathSpacing(expandThemeFunctions(finalValue.replace(/_/g, " ")));
795
+ if (opts.handle) {
796
+ const result = opts.handle(processedValue, ctx, token, x);
797
+ if (result) return result;
798
+ }
799
+ if (opts.prop) return [decl(opts.prop, processedValue)];
800
+ return [];
801
+ }
802
+ if (opts.supportsCustomProperty && parsedUtility.customProperty) {
803
+ if (opts.handleCustomProperty) return opts.handleCustomProperty(finalValue, ctx, token, x) ?? null;
804
+ const customValue = `var(${finalValue})`;
805
+ if (opts.handle) {
806
+ const result = opts.handle(customValue, ctx, token, x);
807
+ if (result) return result;
808
+ }
809
+ if (opts.prop) return [decl(opts.prop, customValue)];
810
+ return [];
811
+ }
812
+ return null;
813
+ };
814
+ if (opts.supportsArbitrary && parsedUtility.arbitrary || opts.supportsCustomProperty && parsedUtility.customProperty) {
815
+ const result = direct(extra);
816
+ if (opts.supportsOpacity && !opts.ownsOpacity && extra.opacity && result?.length) {
817
+ const raw = parsedUtility.arbitrary ? normalizeMathSpacing(expandThemeFunctions(finalValue.replace(/_/g, " "))) : `var(${finalValue})`;
818
+ const hint = parsedUtility.arbitrary ? /^color:(.+)$/.exec(raw)?.[1] : /^color:(--.+)$/.exec(finalValue)?.[1];
819
+ return applyColorAlpha(result, [raw, ...hint ? [hint, `var(${hint})`] : []], String(extra.opacity)) ?? [];
820
+ }
821
+ return result;
822
+ }
823
+ let themeValue;
824
+ if (opts.themeKey && ctx.theme) themeValue = themeScalar(ctx.theme(opts.themeKey, finalValue));
825
+ let namespace = themeValue !== void 0 ? opts.themeKey : void 0;
826
+ if (!themeValue && opts.themeKeys && ctx.theme) for (const key of opts.themeKeys) {
827
+ themeValue = themeScalar(ctx.theme(key, finalValue));
828
+ if (themeValue !== void 0) {
829
+ namespace = key;
830
+ break;
831
+ }
832
+ }
833
+ if (themeValue !== void 0) {
834
+ extra.themeNamespace = namespace;
835
+ extra.themeKey = finalValue;
836
+ if (namespace === "colors" || !(opts.themeKeys ?? [opts.themeKey]).includes("colors")) extra.realThemeValue = finalValue;
837
+ finalValue = themeValue;
838
+ if (opts.prop) return [decl(opts.prop, finalValue)];
517
839
  if (opts.handle) {
518
840
  const result = opts.handle(finalValue, ctx, token, extra);
519
841
  if (result) return result;
@@ -914,6 +1236,48 @@ function isBalancedPrelude(text) {
914
1236
  return stack.length === 0 && !quote;
915
1237
  }
916
1238
  /**
1239
+ * #396: returns `selector` with every quoted string and every `[...]` group (brackets included, nested groups and
1240
+ * escapes inside them too) replaced by a NUL placeholder per character. Backslash escapes outside those regions
1241
+ * are kept verbatim; parentheses are kept. The placeholder is not an identifier character, so it ends a token.
1242
+ */
1243
+ function maskStringsAndBrackets(selector) {
1244
+ let out = "";
1245
+ let quote = "";
1246
+ let bracket = 0;
1247
+ for (let i = 0; i < selector.length; i++) {
1248
+ const c = selector[i];
1249
+ const masked = quote !== "" || bracket > 0;
1250
+ if (c === "\\") {
1251
+ const pair = selector.slice(i, i + 2);
1252
+ out += masked ? "\0".repeat(pair.length) : pair;
1253
+ i++;
1254
+ continue;
1255
+ }
1256
+ if (quote) {
1257
+ if (c === quote) quote = "";
1258
+ out += "\0";
1259
+ continue;
1260
+ }
1261
+ if (c === "\"" || c === "'") {
1262
+ quote = c;
1263
+ out += "\0";
1264
+ continue;
1265
+ }
1266
+ if (c === "[") {
1267
+ bracket++;
1268
+ out += "\0";
1269
+ continue;
1270
+ }
1271
+ if (c === "]" && bracket > 0) {
1272
+ bracket--;
1273
+ out += "\0";
1274
+ continue;
1275
+ }
1276
+ out += masked ? "\0" : c;
1277
+ }
1278
+ return out;
1279
+ }
1280
+ /**
917
1281
  * #392: true when every top-level comma part of an emitted selector names `escapedClass` (a class selector
918
1282
  * already escaped with escapeClassName, including its leading dot) as a whole class token, or, when
919
1283
  * `allowNesting` is set, uses the nesting selector `&`. Escapes, quoted strings and bracket groups are skipped
@@ -943,7 +1307,8 @@ function isScopedSelector(selector, escapedClass, allowNesting = false) {
943
1307
  }
944
1308
  }
945
1309
  parts.push(selector.slice(start));
946
- return parts.every((part) => {
1310
+ return parts.every((raw) => {
1311
+ const part = maskStringsAndBrackets(raw);
947
1312
  if (allowNesting && part.includes("&")) return true;
948
1313
  for (let at = part.indexOf(escapedClass); at !== -1; at = part.indexOf(escapedClass, at + 1)) {
949
1314
  if (at > 0 && part[at - 1] === "\\") continue;
@@ -1045,6 +1410,13 @@ function parseModifier(value) {
1045
1410
  function nameSort(a, b) {
1046
1411
  return b.name.length - a.name.length;
1047
1412
  }
1413
+ /** #393: index of the `close` that balances the value opened just before `s` (depth starts at 1), or -1. */
1414
+ function matchingClose(s, open, close) {
1415
+ let depth = 1;
1416
+ for (let i = 0; i < s.length; i++) if (s[i] === open) depth++;
1417
+ else if (s[i] === close && --depth === 0) return i;
1418
+ return -1;
1419
+ }
1048
1420
  /**
1049
1421
  * Parse utility token
1050
1422
  */
@@ -1073,15 +1445,27 @@ function parseUtility(value, ctx) {
1073
1445
  if (value.startsWith("-")) negative = true;
1074
1446
  if (value.includes("-[")) {
1075
1447
  [prefix, utilityValue] = value.split("-[");
1076
- const closeIdx = utilityValue.lastIndexOf("]");
1448
+ const closeIdx = matchingClose(utilityValue, "[", "]");
1077
1449
  if (closeIdx !== -1 && closeIdx < utilityValue.length - 1 && utilityValue[closeIdx + 1] === "/") {
1078
1450
  opacity = utilityValue.slice(closeIdx + 2);
1451
+ if (!opacity) return {
1452
+ prefix: "",
1453
+ value: ""
1454
+ };
1079
1455
  utilityValue = utilityValue.slice(0, closeIdx);
1080
1456
  } else utilityValue = utilityValue.replace(/]$/, "");
1081
1457
  arbitrary = true;
1082
1458
  } else if (value.includes("-(")) {
1083
1459
  [prefix, utilityValue] = value.split("-(");
1084
- utilityValue = utilityValue.replace(/\)$/, "");
1460
+ const closeIdx = matchingClose(utilityValue, "(", ")");
1461
+ if (closeIdx !== -1 && closeIdx < utilityValue.length - 1 && utilityValue[closeIdx + 1] === "/") {
1462
+ opacity = utilityValue.slice(closeIdx + 2);
1463
+ if (!opacity) return {
1464
+ prefix: "",
1465
+ value: ""
1466
+ };
1467
+ utilityValue = utilityValue.slice(0, closeIdx);
1468
+ } else utilityValue = utilityValue.replace(/\)$/, "");
1085
1469
  customProperty = true;
1086
1470
  } else {
1087
1471
  const sortedUtilities = [...getUtility(ctx)].sort(nameSort);
@@ -1133,6 +1517,13 @@ var uniqueDescriptors = (node) => {
1133
1517
  const seen = /* @__PURE__ */ new Set();
1134
1518
  return node.nodes.filter((c) => c.type !== "decl" || !seen.has(c.prop) && !!seen.add(c.prop));
1135
1519
  };
1520
+ var NO_IMPORTANT_AT = /* @__PURE__ */ new Set([
1521
+ "property",
1522
+ "font-face",
1523
+ "keyframes",
1524
+ "-webkit-keyframes",
1525
+ "counter-style"
1526
+ ]);
1136
1527
  var isSafeDecl = (prop, value) => isStructureSafeValue(String(prop)) && isStructureSafeValue(String(value ?? "")) && !hasHtmlEndTagOpener(String(prop)) && !hasHtmlEndTagOpener(String(value ?? ""));
1137
1528
  var importantPrefix = "!important";
1138
1529
  /**
@@ -1203,10 +1594,15 @@ function astToCss(ast, baseSelector, opts, _indent = "") {
1203
1594
  if (!isSafePrelude(node.selector) || !inScope(node.selector)) return "";
1204
1595
  if (minify) return `${indent}${node.selector} {${astToCss(node.nodes, baseSelector, nestedOpts, nextIndent)}}`;
1205
1596
  else return `${indent}${node.selector} {\n${astToCss(node.nodes, baseSelector, nestedOpts, nextIndent)}${indent}}`;
1206
- case "at-rule":
1597
+ case "at-rule": {
1207
1598
  if (!isSafePrelude(node.name) || !isSafePrelude(node.params)) return "";
1208
- if (minify) return `${indent}@${node.name} ${node.params}{${astToCss(node.nodes, baseSelector, opts, nextIndent)}}`;
1209
- else return `${indent}@${node.name} ${node.params} {\n${astToCss(node.nodes, baseSelector, opts, nextIndent)}${indent}}`;
1599
+ const atOpts = opts?.important && NO_IMPORTANT_AT.has(node.name) ? {
1600
+ ...opts,
1601
+ important: false
1602
+ } : opts;
1603
+ if (minify) return `${indent}@${node.name} ${node.params}{${astToCss(node.nodes, baseSelector, atOpts, nextIndent)}}`;
1604
+ else return `${indent}@${node.name} ${node.params} {\n${astToCss(node.nodes, baseSelector, atOpts, nextIndent)}${indent}}`;
1605
+ }
1210
1606
  case "comment": return minify ? "" : `${indent}/* ${node.text} */`;
1211
1607
  case "raw": return `${indent}${node.value}`;
1212
1608
  default:
@@ -1565,6 +1961,552 @@ function applyVarPrefix(ast, ctx) {
1565
1961
  return walk(ast);
1566
1962
  }
1567
1963
  //#endregion
1964
+ //#region src/core/tw-property-order.ts
1965
+ var TW_PROPERTY_ORDER = [
1966
+ "container-type",
1967
+ "pointer-events",
1968
+ "visibility",
1969
+ "position",
1970
+ "inset",
1971
+ "inset-inline",
1972
+ "inset-block",
1973
+ "inset-inline-start",
1974
+ "inset-inline-end",
1975
+ "inset-block-start",
1976
+ "inset-block-end",
1977
+ "top",
1978
+ "right",
1979
+ "bottom",
1980
+ "left",
1981
+ "isolation",
1982
+ "z-index",
1983
+ "order",
1984
+ "grid-column",
1985
+ "grid-column-start",
1986
+ "grid-column-end",
1987
+ "grid-row",
1988
+ "grid-row-start",
1989
+ "grid-row-end",
1990
+ "float",
1991
+ "clear",
1992
+ "--tw-container-component",
1993
+ "margin",
1994
+ "margin-inline",
1995
+ "margin-block",
1996
+ "margin-inline-start",
1997
+ "margin-inline-end",
1998
+ "margin-block-start",
1999
+ "margin-block-end",
2000
+ "margin-top",
2001
+ "margin-right",
2002
+ "margin-bottom",
2003
+ "margin-left",
2004
+ "box-sizing",
2005
+ "display",
2006
+ "field-sizing",
2007
+ "aspect-ratio",
2008
+ "height",
2009
+ "max-height",
2010
+ "min-height",
2011
+ "width",
2012
+ "max-width",
2013
+ "min-width",
2014
+ "flex",
2015
+ "flex-shrink",
2016
+ "flex-grow",
2017
+ "flex-basis",
2018
+ "table-layout",
2019
+ "caption-side",
2020
+ "border-collapse",
2021
+ "border-spacing",
2022
+ "transform-origin",
2023
+ "translate",
2024
+ "--tw-translate-x",
2025
+ "--tw-translate-y",
2026
+ "--tw-translate-z",
2027
+ "scale",
2028
+ "--tw-scale-x",
2029
+ "--tw-scale-y",
2030
+ "--tw-scale-z",
2031
+ "rotate",
2032
+ "--tw-rotate-x",
2033
+ "--tw-rotate-y",
2034
+ "--tw-rotate-z",
2035
+ "--tw-skew-x",
2036
+ "--tw-skew-y",
2037
+ "transform",
2038
+ "zoom",
2039
+ "animation",
2040
+ "cursor",
2041
+ "touch-action",
2042
+ "--tw-pan-x",
2043
+ "--tw-pan-y",
2044
+ "--tw-pinch-zoom",
2045
+ "resize",
2046
+ "scroll-snap-type",
2047
+ "--tw-scroll-snap-strictness",
2048
+ "scroll-snap-align",
2049
+ "scroll-snap-stop",
2050
+ "scroll-margin",
2051
+ "scroll-margin-inline",
2052
+ "scroll-margin-block",
2053
+ "scroll-margin-inline-start",
2054
+ "scroll-margin-inline-end",
2055
+ "scroll-margin-block-start",
2056
+ "scroll-margin-block-end",
2057
+ "scroll-margin-top",
2058
+ "scroll-margin-right",
2059
+ "scroll-margin-bottom",
2060
+ "scroll-margin-left",
2061
+ "scroll-padding",
2062
+ "scroll-padding-inline",
2063
+ "scroll-padding-block",
2064
+ "scroll-padding-inline-start",
2065
+ "scroll-padding-inline-end",
2066
+ "scroll-padding-block-start",
2067
+ "scroll-padding-block-end",
2068
+ "scroll-padding-top",
2069
+ "scroll-padding-right",
2070
+ "scroll-padding-bottom",
2071
+ "scroll-padding-left",
2072
+ "scrollbar-width",
2073
+ "scrollbar-color",
2074
+ "scrollbar-gutter",
2075
+ "list-style-position",
2076
+ "list-style-type",
2077
+ "list-style-image",
2078
+ "appearance",
2079
+ "columns",
2080
+ "break-before",
2081
+ "break-inside",
2082
+ "break-after",
2083
+ "grid-auto-columns",
2084
+ "grid-auto-flow",
2085
+ "grid-auto-rows",
2086
+ "grid-template-columns",
2087
+ "grid-template-rows",
2088
+ "flex-direction",
2089
+ "flex-wrap",
2090
+ "place-content",
2091
+ "place-items",
2092
+ "align-content",
2093
+ "align-items",
2094
+ "justify-content",
2095
+ "justify-items",
2096
+ "gap",
2097
+ "column-gap",
2098
+ "row-gap",
2099
+ "--tw-space-x-reverse",
2100
+ "--tw-space-y-reverse",
2101
+ "divide-x-width",
2102
+ "divide-y-width",
2103
+ "--tw-divide-y-reverse",
2104
+ "divide-style",
2105
+ "divide-color",
2106
+ "place-self",
2107
+ "align-self",
2108
+ "justify-self",
2109
+ "overflow",
2110
+ "overflow-x",
2111
+ "overflow-y",
2112
+ "overscroll-behavior",
2113
+ "overscroll-behavior-x",
2114
+ "overscroll-behavior-y",
2115
+ "scroll-behavior",
2116
+ "border-radius",
2117
+ "border-start-radius",
2118
+ "border-end-radius",
2119
+ "border-top-radius",
2120
+ "border-right-radius",
2121
+ "border-bottom-radius",
2122
+ "border-left-radius",
2123
+ "border-start-start-radius",
2124
+ "border-start-end-radius",
2125
+ "border-end-end-radius",
2126
+ "border-end-start-radius",
2127
+ "border-top-left-radius",
2128
+ "border-top-right-radius",
2129
+ "border-bottom-right-radius",
2130
+ "border-bottom-left-radius",
2131
+ "border-width",
2132
+ "border-inline-width",
2133
+ "border-block-width",
2134
+ "border-inline-start-width",
2135
+ "border-inline-end-width",
2136
+ "border-block-start-width",
2137
+ "border-block-end-width",
2138
+ "border-top-width",
2139
+ "border-right-width",
2140
+ "border-bottom-width",
2141
+ "border-left-width",
2142
+ "border-style",
2143
+ "border-inline-style",
2144
+ "border-block-style",
2145
+ "border-inline-start-style",
2146
+ "border-inline-end-style",
2147
+ "border-block-start-style",
2148
+ "border-block-end-style",
2149
+ "border-top-style",
2150
+ "border-right-style",
2151
+ "border-bottom-style",
2152
+ "border-left-style",
2153
+ "border-color",
2154
+ "border-inline-color",
2155
+ "border-block-color",
2156
+ "border-inline-start-color",
2157
+ "border-inline-end-color",
2158
+ "border-block-start-color",
2159
+ "border-block-end-color",
2160
+ "border-top-color",
2161
+ "border-right-color",
2162
+ "border-bottom-color",
2163
+ "border-left-color",
2164
+ "background-color",
2165
+ "background-image",
2166
+ "--tw-gradient-position",
2167
+ "--tw-gradient-stops",
2168
+ "--tw-gradient-via-stops",
2169
+ "--tw-gradient-from",
2170
+ "--tw-gradient-from-position",
2171
+ "--tw-gradient-via",
2172
+ "--tw-gradient-via-position",
2173
+ "--tw-gradient-to",
2174
+ "--tw-gradient-to-position",
2175
+ "mask-image",
2176
+ "--tw-mask-top",
2177
+ "--tw-mask-top-from-color",
2178
+ "--tw-mask-top-from-position",
2179
+ "--tw-mask-top-to-color",
2180
+ "--tw-mask-top-to-position",
2181
+ "--tw-mask-right",
2182
+ "--tw-mask-right-from-color",
2183
+ "--tw-mask-right-from-position",
2184
+ "--tw-mask-right-to-color",
2185
+ "--tw-mask-right-to-position",
2186
+ "--tw-mask-bottom",
2187
+ "--tw-mask-bottom-from-color",
2188
+ "--tw-mask-bottom-from-position",
2189
+ "--tw-mask-bottom-to-color",
2190
+ "--tw-mask-bottom-to-position",
2191
+ "--tw-mask-left",
2192
+ "--tw-mask-left-from-color",
2193
+ "--tw-mask-left-from-position",
2194
+ "--tw-mask-left-to-color",
2195
+ "--tw-mask-left-to-position",
2196
+ "--tw-mask-linear",
2197
+ "--tw-mask-linear-position",
2198
+ "--tw-mask-linear-from-color",
2199
+ "--tw-mask-linear-from-position",
2200
+ "--tw-mask-linear-to-color",
2201
+ "--tw-mask-linear-to-position",
2202
+ "--tw-mask-radial",
2203
+ "--tw-mask-radial-shape",
2204
+ "--tw-mask-radial-size",
2205
+ "--tw-mask-radial-position",
2206
+ "--tw-mask-radial-from-color",
2207
+ "--tw-mask-radial-from-position",
2208
+ "--tw-mask-radial-to-color",
2209
+ "--tw-mask-radial-to-position",
2210
+ "--tw-mask-conic",
2211
+ "--tw-mask-conic-position",
2212
+ "--tw-mask-conic-from-color",
2213
+ "--tw-mask-conic-from-position",
2214
+ "--tw-mask-conic-to-color",
2215
+ "--tw-mask-conic-to-position",
2216
+ "box-decoration-break",
2217
+ "background-size",
2218
+ "background-attachment",
2219
+ "background-clip",
2220
+ "background-position",
2221
+ "background-repeat",
2222
+ "background-origin",
2223
+ "mask-composite",
2224
+ "mask-mode",
2225
+ "mask-type",
2226
+ "mask-size",
2227
+ "mask-clip",
2228
+ "mask-position",
2229
+ "mask-repeat",
2230
+ "mask-origin",
2231
+ "fill",
2232
+ "stroke",
2233
+ "stroke-width",
2234
+ "object-fit",
2235
+ "object-position",
2236
+ "padding",
2237
+ "padding-inline",
2238
+ "padding-block",
2239
+ "padding-inline-start",
2240
+ "padding-inline-end",
2241
+ "padding-block-start",
2242
+ "padding-block-end",
2243
+ "padding-top",
2244
+ "padding-right",
2245
+ "padding-bottom",
2246
+ "padding-left",
2247
+ "text-align",
2248
+ "text-indent",
2249
+ "vertical-align",
2250
+ "font-family",
2251
+ "font-feature-settings",
2252
+ "font-size",
2253
+ "line-height",
2254
+ "font-weight",
2255
+ "letter-spacing",
2256
+ "text-wrap",
2257
+ "overflow-wrap",
2258
+ "word-break",
2259
+ "text-overflow",
2260
+ "hyphens",
2261
+ "white-space",
2262
+ "tab-size",
2263
+ "color",
2264
+ "text-transform",
2265
+ "font-style",
2266
+ "font-stretch",
2267
+ "font-variant-numeric",
2268
+ "text-decoration-line",
2269
+ "text-decoration-color",
2270
+ "text-decoration-style",
2271
+ "text-decoration-thickness",
2272
+ "text-underline-offset",
2273
+ "-webkit-font-smoothing",
2274
+ "placeholder-color",
2275
+ "caret-color",
2276
+ "accent-color",
2277
+ "color-scheme",
2278
+ "opacity",
2279
+ "background-blend-mode",
2280
+ "mix-blend-mode",
2281
+ "box-shadow",
2282
+ "--tw-shadow",
2283
+ "--tw-shadow-color",
2284
+ "--tw-ring-shadow",
2285
+ "--tw-ring-color",
2286
+ "--tw-inset-shadow",
2287
+ "--tw-inset-shadow-color",
2288
+ "--tw-inset-ring-shadow",
2289
+ "--tw-inset-ring-color",
2290
+ "--tw-ring-offset-width",
2291
+ "--tw-ring-offset-color",
2292
+ "outline",
2293
+ "outline-width",
2294
+ "outline-offset",
2295
+ "outline-color",
2296
+ "--tw-blur",
2297
+ "--tw-brightness",
2298
+ "--tw-contrast",
2299
+ "--tw-drop-shadow",
2300
+ "--tw-grayscale",
2301
+ "--tw-hue-rotate",
2302
+ "--tw-invert",
2303
+ "--tw-saturate",
2304
+ "--tw-sepia",
2305
+ "filter",
2306
+ "--tw-backdrop-blur",
2307
+ "--tw-backdrop-brightness",
2308
+ "--tw-backdrop-contrast",
2309
+ "--tw-backdrop-grayscale",
2310
+ "--tw-backdrop-hue-rotate",
2311
+ "--tw-backdrop-invert",
2312
+ "--tw-backdrop-opacity",
2313
+ "--tw-backdrop-saturate",
2314
+ "--tw-backdrop-sepia",
2315
+ "backdrop-filter",
2316
+ "transition-property",
2317
+ "transition-behavior",
2318
+ "transition-delay",
2319
+ "transition-duration",
2320
+ "transition-timing-function",
2321
+ "will-change",
2322
+ "contain",
2323
+ "content",
2324
+ "forced-color-adjust"
2325
+ ];
2326
+ //#endregion
2327
+ //#region src/core/rule-order.ts
2328
+ /**
2329
+ * Tailwind-compatible cascade order for runtime-inserted rules (#254); shared by @barocss/server (#267).
2330
+ *
2331
+ * The runtime discovers classes in DOM order, so without sorting `lg:px-8`
2332
+ * seen before `sm:px-6` would land earlier and lose at >= 1024px. Each rule
2333
+ * gets a sort key derived from its leading `@media` / `@container` preludes:
2334
+ *
2335
+ * 0 base, negated media (`not-md:` → `@media not (…)`, as Tailwind 4.3.3 orders them, #352),
2336
+ * state media (hover), motion/contrast, unknown
2337
+ * 1 max-* breakpoints (larger width first)
2338
+ * 2 min-* breakpoints (smaller width first)
2339
+ * 3 @max-* container queries (larger width first)
2340
+ * 4 @min-* container queries (smaller width first)
2341
+ * 5 orientation, dark (prefers-color-scheme), print, forced-colors
2342
+ *
2343
+ * Nested at-rules (e.g. `sm:dark:`) contribute one key pair per level, so
2344
+ * `sm:` < `sm:dark:` < `md:`.
2345
+ *
2346
+ * #401: within one variant key, rules follow Tailwind 4's property order (the candidate sort of
2347
+ * Tailwind 4.3.3's `compile()`): compare the sorted TW property indices of each rule's declarations up
2348
+ * to the first difference (a rule that runs out of indices sorts last), then more declarations first,
2349
+ * then the class name (Tailwind's numeric-aware compare). Equal keys keep discovery order.
2350
+ */
2351
+ var LEADING_AT = /^\s*@(media|container)\s+([^{]*)\{/;
2352
+ var LATE_MEDIA = /prefers-color-scheme|\bprint\b|forced-colors|orientation/;
2353
+ var MIN_W = /(?:min-width\s*:\s*|width\s*>=?\s*)([\d.]+)(px|rem|em)?/;
2354
+ var MAX_W = /(?:max-width\s*:\s*|width\s*<=?\s*)([\d.]+)(px|rem|em)?/;
2355
+ /** Separates the variant pairs (whose first slot is >= 0) from the property part of a key. */
2356
+ var PROPERTY_PART = -1;
2357
+ /** A rule that has run out of property indices sorts after every real index (TW: `?? Infinity`). */
2358
+ var NO_MORE = Number.MAX_SAFE_INTEGER;
2359
+ var PROPERTY_INDEX = new Map(TW_PROPERTY_ORDER.map((p, i) => [p, i]));
2360
+ var DECL = /(?:^|[{;])\s*(-{0,2}[a-zA-Z][\w-]*)\s*:[^;{}]*(?=[;}])/g;
2361
+ var AT_PRELUDE = /^\s*@[\w-]+[^{]*\{/;
2362
+ var CLASS = /\.((?:\\.|[\w-])+)/;
2363
+ function toPx(n, unit) {
2364
+ const v = parseFloat(n);
2365
+ return unit === "rem" || unit === "em" ? v * 16 : v;
2366
+ }
2367
+ function preludeKey(kind, prelude) {
2368
+ const container = kind === "container";
2369
+ if (!container && /^\s*not\b/i.test(prelude)) return [0, 0];
2370
+ const min = MIN_W.exec(prelude);
2371
+ if (min) return [container ? 4 : 2, toPx(min[1], min[2])];
2372
+ const max = MAX_W.exec(prelude);
2373
+ if (max) return [container ? 3 : 1, -toPx(max[1], max[2])];
2374
+ if (!container && LATE_MEDIA.test(prelude)) return [5, 0];
2375
+ return [0, 0];
2376
+ }
2377
+ /** The rule text after its leading at-rule preludes, up to its first `{` (the selector). */
2378
+ function ruleSelector(rule) {
2379
+ let rest = rule;
2380
+ let m;
2381
+ while (m = AT_PRELUDE.exec(rest)) rest = rest.slice(m[0].length);
2382
+ const end = rest.indexOf("{");
2383
+ return end === -1 ? "" : rest.slice(0, end);
2384
+ }
2385
+ /**
2386
+ * Tailwind's `--tw-sort` overrides (a utility sorts as one pseudo-property), recognised from BaroCSS's
2387
+ * output for the same utilities: space-x/y, divide-*, placeholder colour, gradient stops, container.
2388
+ * (TW's `size-*` override names no listed property, so Tailwind ignores it, and so does this.)
2389
+ */
2390
+ function sortOverride(selector, props, candidate) {
2391
+ const first = props[0];
2392
+ if (first === "--tw-space-x-reverse") return "row-gap";
2393
+ if (first === "--tw-space-y-reverse") return "column-gap";
2394
+ if (first === "--tw-divide-x-reverse") return "divide-x-width";
2395
+ if (first === "--tw-divide-y-reverse") return "divide-y-width";
2396
+ if (selector.includes(":not(:last-child)")) {
2397
+ if (props.includes("border-color")) return "divide-color";
2398
+ if (props.includes("border-style")) return "divide-style";
2399
+ }
2400
+ if (selector.includes("::placeholder") && props.includes("color")) return "placeholder-color";
2401
+ for (const stop of [
2402
+ "from",
2403
+ "via",
2404
+ "to"
2405
+ ]) if (props.includes(`--tw-gradient-${stop}`)) return `--tw-gradient-${stop}`;
2406
+ if (candidate.slice(candidate.lastIndexOf(":") + 1) === "container") return "--tw-container-component";
2407
+ return null;
2408
+ }
2409
+ /**
2410
+ * Tailwind's per-candidate property sort (#401): the sorted, de-duplicated TW property-order indices of
2411
+ * the rule's declarations (at any depth), and the declaration count. `--baro-*` vars count as `--tw-*`.
2412
+ */
2413
+ /** @internal (#401) */ function rulePropertySort(rule, candidate = ruleCandidate(rule)) {
2414
+ const props = [];
2415
+ for (const d of rule.matchAll(DECL)) props.push(d[1].startsWith("--baro-") ? "--tw-" + d[1].slice(7) : d[1]);
2416
+ const override = sortOverride(ruleSelector(rule), props, candidate);
2417
+ const overrideIndex = override === null ? void 0 : PROPERTY_INDEX.get(override);
2418
+ if (overrideIndex !== void 0) return {
2419
+ order: [overrideIndex],
2420
+ count: props.length + 1
2421
+ };
2422
+ const set = /* @__PURE__ */ new Set();
2423
+ for (const p of props) {
2424
+ const i = PROPERTY_INDEX.get(p);
2425
+ if (i !== void 0) set.add(i);
2426
+ }
2427
+ return {
2428
+ order: Array.from(set).sort((a, b) => a - b),
2429
+ count: props.length
2430
+ };
2431
+ }
2432
+ /** The (unescaped) first class in the rule's selector, e.g. `sm:px-2`. */
2433
+ /** @internal (#401) */ function ruleCandidate(rule) {
2434
+ const c = CLASS.exec(ruleSelector(rule));
2435
+ return c ? c[1].replace(/\\(.)/g, "$1") : "";
2436
+ }
2437
+ /** Tailwind's candidate compare: runs of digits compare by value, other chars by code. */
2438
+ /** @internal (#401) */ function compareCandidates(a, b) {
2439
+ const n = Math.min(a.length, b.length);
2440
+ for (let i = 0; i < n; i++) {
2441
+ let x = a.charCodeAt(i);
2442
+ let y = b.charCodeAt(i);
2443
+ if (x >= 48 && x <= 57 && y >= 48 && y <= 57) {
2444
+ let ae = i + 1;
2445
+ let be = i + 1;
2446
+ for (x = a.charCodeAt(ae); x >= 48 && x <= 57;) x = a.charCodeAt(++ae);
2447
+ for (y = b.charCodeAt(be); y >= 48 && y <= 57;) y = b.charCodeAt(++be);
2448
+ const as = a.slice(i, ae);
2449
+ const bs = b.slice(i, be);
2450
+ const diff = Number(as) - Number(bs);
2451
+ if (diff) return diff;
2452
+ if (as < bs) return -1;
2453
+ if (as > bs) return 1;
2454
+ continue;
2455
+ }
2456
+ if (x !== y) return x - y;
2457
+ }
2458
+ return a.length - b.length;
2459
+ }
2460
+ /** The #254 variant part of the key (leading `@media` / `@container` preludes). */
2461
+ /** @internal (#401) */ function ruleVariantKey(rule) {
2462
+ const key = [];
2463
+ let rest = rule;
2464
+ let m;
2465
+ while (m = LEADING_AT.exec(rest)) {
2466
+ const [g, v] = preludeKey(m[1], m[2]);
2467
+ key.push(g, v);
2468
+ rest = rest.slice(m[0].length);
2469
+ }
2470
+ return key;
2471
+ }
2472
+ /**
2473
+ * Full sort key: the #254 variant pairs, then (#401) Tailwind's property sort and the class name.
2474
+ * `candidate` defaults to the rule's first class.
2475
+ */
2476
+ function ruleSortKey(rule, candidate) {
2477
+ const name = candidate ?? ruleCandidate(rule);
2478
+ const { order, count } = rulePropertySort(rule, name);
2479
+ const key = ruleVariantKey(rule);
2480
+ key.push(PROPERTY_PART, ...order, NO_MORE, -count, name);
2481
+ return key;
2482
+ }
2483
+ function compareKeys(a, b) {
2484
+ const n = Math.min(a.length, b.length);
2485
+ for (let i = 0; i < n; i++) {
2486
+ const x = a[i];
2487
+ const y = b[i];
2488
+ if (x === y) continue;
2489
+ if (typeof x === "string" || typeof y === "string") {
2490
+ const d = compareCandidates(String(x), String(y));
2491
+ if (d) return d;
2492
+ continue;
2493
+ }
2494
+ return x - y;
2495
+ }
2496
+ return a.length - b.length;
2497
+ }
2498
+ /** Index after the last key <= `key` (stable upper bound) in sorted `keys`. */
2499
+ function upperBound(keys, key) {
2500
+ let lo = 0;
2501
+ let hi = keys.length;
2502
+ while (lo < hi) {
2503
+ const mid = lo + hi >> 1;
2504
+ if (compareKeys(keys[mid], key) <= 0) lo = mid + 1;
2505
+ else hi = mid;
2506
+ }
2507
+ return lo;
2508
+ }
2509
+ //#endregion
1568
2510
  //#region src/core/engine.ts
1569
2511
  var failureCache = /* @__PURE__ */ new Set();
1570
2512
  /**
@@ -1763,6 +2705,10 @@ function parseClassToAst(fullClassName, ctx) {
1763
2705
  let ast = [];
1764
2706
  for (const utilReg of utilRegs) {
1765
2707
  ast = utilReg.handler(value, ctx, utility, utilReg) || [];
2708
+ if (ast === REJECT_CLASS) {
2709
+ ast = [];
2710
+ break;
2711
+ }
1766
2712
  if (ast.length > 0) break;
1767
2713
  }
1768
2714
  const wrappers = [];
@@ -1887,7 +2833,7 @@ var CLASS_SEPARATOR = /[ \t\n\f\r]+/;
1887
2833
  function generateCss(classList, ctx, opts) {
1888
2834
  const seen = /* @__PURE__ */ new Set();
1889
2835
  const allAtRootNodes = [];
1890
- const results = classList.split(CLASS_SEPARATOR).filter((cls) => {
2836
+ const generated = classList.split(CLASS_SEPARATOR).filter((cls) => {
1891
2837
  if (!cls) return false;
1892
2838
  if (opts?.dedup) {
1893
2839
  if (seen.has(cls)) return false;
@@ -1896,12 +2842,31 @@ function generateCss(classList, ctx, opts) {
1896
2842
  return true;
1897
2843
  }).map((cls) => {
1898
2844
  try {
1899
- return generateOne(cls);
2845
+ return {
2846
+ cls,
2847
+ css: generateOne(cls)
2848
+ };
1900
2849
  } catch (err) {
1901
2850
  debugWarn("[generateCss] class generation failed:", cls, err);
1902
- return "";
2851
+ return {
2852
+ cls,
2853
+ css: ""
2854
+ };
1903
2855
  }
1904
- }).join(opts?.minify ? "" : "\n");
2856
+ });
2857
+ const slots = generated.flatMap((g, i) => g.css ? [i] : []);
2858
+ const sorted = slots.map((i) => ({
2859
+ i,
2860
+ css: generated[i].css,
2861
+ key: ruleSortKey(generated[i].css, generated[i].cls)
2862
+ })).sort((a, b) => compareKeys(a.key, b.key) || a.i - b.i);
2863
+ slots.forEach((slot, j) => {
2864
+ generated[slot] = {
2865
+ cls: generated[slot].cls,
2866
+ css: sorted[j].css
2867
+ };
2868
+ });
2869
+ const results = generated.map((g) => g.css).join(opts?.minify ? "" : "\n");
1905
2870
  function generateOne(cls) {
1906
2871
  const ast = parseClassToAst(cls, ctx);
1907
2872
  const parsedResult = (getContextState(ctx)?.parseResultCache || parseResultCache).get(cls);
@@ -3050,500 +4015,232 @@ function configGetter(config, ...path) {
3050
4015
  else keys = path;
3051
4016
  return keys.reduce((acc, key) => acc ? acc[key] : void 0, config);
3052
4017
  }
3053
- function hasPreset(themeObj, category, preset) {
3054
- return themeObj[category]?.includes?.(preset);
3055
- }
3056
- function resolveTheme(config) {
3057
- let theme = {};
3058
- if (config.presets) {
3059
- for (const preset of config.presets) if (preset.theme) theme = deepMerge(theme, preset.theme);
3060
- }
3061
- if (config.theme) {
3062
- const { extend, ...overrideTheme } = config.theme;
3063
- theme = deepMerge(theme, overrideTheme);
3064
- if (extend) theme = deepMerge(theme, extend);
3065
- }
3066
- return theme;
3067
- }
3068
- function themeToCssVars(theme) {
3069
- return toCssVarsBlock(themeToCssVarsAll(theme));
3070
- }
3071
- function createContext(configObj) {
3072
- if (configObj.debug !== void 0) setDebug(!!configObj.debug);
3073
- const configWithDefaults = {
3074
- presets: [{ theme: require_theme.defaultTheme }, ...configObj.presets || []],
3075
- ...configObj
3076
- };
3077
- const themeObj = resolveTheme(configWithDefaults);
3078
- const ctx = {
3079
- hasPreset: (category, preset) => {
3080
- return hasPreset(themeObj, category, preset);
3081
- },
3082
- theme: (...args) => {
3083
- return themeGetter(themeObj, ...args);
3084
- },
3085
- config: (...args) => {
3086
- return configGetter(configWithDefaults, ...args);
3087
- },
3088
- themeToCssVars: () => themeToCssVars(themeObj),
3089
- extendTheme: (category, values) => {
3090
- if (typeof values === "function") {
3091
- const result = values(ctx.theme);
3092
- if (result && typeof result === "object") {
3093
- const existingValues = themeObj[category] || {};
3094
- themeObj[category] = {
3095
- ...existingValues,
3096
- ...result
3097
- };
3098
- }
3099
- } else if (typeof values === "object" && values !== null && !Array.isArray(values)) {
3100
- const existingValues = themeObj[category] || {};
3101
- themeObj[category] = {
3102
- ...existingValues,
3103
- ...values
3104
- };
3105
- }
3106
- clearContextCaches(ctx);
3107
- },
3108
- getPreflightCSS: (level = true) => {
3109
- return getPreflightCSS(level);
3110
- }
3111
- };
3112
- initializeContextState(ctx, getUtility(), getModifier());
3113
- registerCustomUtilities(ctx, configObj.utilities);
3114
- return ctx;
3115
- }
3116
- //#endregion
3117
- //#region src/core/jsonToAst.ts
3118
- /**
3119
- * Converts a single BaroJsonInput object into an AST tree.
3120
- * Bypasses string parsing and directly invokes utility/modifier handlers.
3121
- *
3122
- * @param input BaroJsonInput object
3123
- * @param ctx Context
3124
- * @returns AstNode[]
3125
- */
3126
- function jsonToAst(input, ctx) {
3127
- if ((input.variants || []).some((v) => typeof v === "string" ? !isSafeVariantToken(v) : !isSafeVariantValue(v.name || "") || !isSafeVariantValue(v.value || "") || hasCommentToken(v.name || "") || hasCommentToken(v.value || ""))) return [];
3128
- let utilReg = getUtility(ctx).find((u) => u.name === input.utility.name);
3129
- if (input.utility.value && !input.utility.arbitrary && !input.utility.customProperty) {
3130
- const fullName = `${input.utility.name}-${input.utility.value}`;
3131
- const exactMatch = getUtility(ctx).find((u) => u.name === fullName);
3132
- if (exactMatch) utilReg = exactMatch;
3133
- }
3134
- if (!utilReg) {
3135
- debugWarn(`[jsonToAst] Unknown utility: "${input.utility.name}"`);
3136
- return [];
3137
- }
3138
- const parsedUtility = {
3139
- prefix: input.utility.name,
3140
- value: input.utility.value,
3141
- arbitrary: input.utility.arbitrary,
3142
- negative: input.utility.negative,
3143
- opacity: input.utility.opacity,
3144
- important: input.utility.important,
3145
- customProperty: input.utility.customProperty,
3146
- category: utilReg.category,
3147
- priority: utilReg.priority
3148
- };
3149
- let value = input.utility.value;
3150
- if (input.utility.negative && value) value = "-" + value;
3151
- let ast = utilReg.handler(value || "", ctx, parsedUtility, utilReg) || [];
3152
- if (input.variants && input.variants.length > 0) {
3153
- const wrappers = [];
3154
- const selector = "&";
3155
- for (let i = input.variants.length - 1; i >= 0; i--) {
3156
- const variantInput = input.variants[i];
3157
- const variantName = typeof variantInput === "string" ? variantInput : variantInput.name;
3158
- const variantValue = typeof variantInput === "string" ? void 0 : variantInput.value;
3159
- const variantArbitrary = typeof variantInput === "string" ? false : variantInput.arbitrary;
3160
- const parsedModifier = {
3161
- type: variantName,
3162
- value: variantValue,
3163
- arbitrary: variantArbitrary
3164
- };
3165
- let matchKey = variantName;
3166
- if (variantArbitrary && variantValue) {
3167
- if (variantName) {
3168
- matchKey = `${variantName}-[${variantValue}]`;
3169
- parsedModifier.type = matchKey;
3170
- } else {
3171
- matchKey = `[${variantValue}]`;
3172
- parsedModifier.type = matchKey;
3173
- }
3174
- } else if (variantValue) {
3175
- matchKey = `${variantName}-[${variantValue}]`;
3176
- parsedModifier.type = matchKey;
3177
- }
3178
- const plugin = getModifier(ctx).find((p) => p.match(matchKey, ctx));
3179
- if (!plugin) {
3180
- debugWarn(`[jsonToAst] Unknown variant: "${matchKey}"`);
3181
- continue;
3182
- }
3183
- if (plugin.astHandler) ast = plugin.astHandler(ast, parsedModifier, ctx, [], i);
3184
- if (plugin.modifySelector) {
3185
- const result = plugin.modifySelector({
3186
- selector,
3187
- fullClassName: "JSON_GENERATED",
3188
- mod: parsedModifier,
3189
- context: ctx,
3190
- variantChain: [],
3191
- index: i
3192
- });
3193
- if (result == null) continue;
3194
- if (plugin.wrap && (result === "&" || typeof result === "object" && !Array.isArray(result) && result.selector === "&" || Array.isArray(result) && result.length === 1 && result[0].selector === "&")) {} else if (typeof result === "string" && result.includes("&")) wrappers.push({
3195
- type: "rule",
3196
- selector: result
3197
- });
3198
- else if (typeof result === "object" && !Array.isArray(result) && result.selector) {
3199
- const r = result;
3200
- const wrappingType = r.wrappingType || "rule";
3201
- wrappers.push({
3202
- type: wrappingType,
3203
- selector: r.selector,
3204
- flatten: r.flatten,
3205
- source: r.source
3206
- });
3207
- } else if (Array.isArray(result)) wrappers.push({
3208
- type: "wrap",
3209
- items: result.map((r) => ({
3210
- type: r.wrappingType || "rule",
3211
- selector: r.selector,
3212
- source: r.source,
3213
- nodes: []
3214
- }))
3215
- });
3216
- }
3217
- if (plugin.wrap) wrappers.push({
3218
- type: "wrap",
3219
- items: plugin.wrap(parsedModifier, ctx)
3220
- });
3221
- }
3222
- for (let i = 0; i < wrappers.length; i++) {
3223
- const wrap = wrappers[i];
3224
- if (wrap.type === "wrap") ast = wrap.items.map((item) => item.type === "rule" || item.type === "style-rule" || item.type === "at-rule" || item.type === "at-root" ? {
3225
- ...item,
3226
- nodes: [...item.nodes || [], ...ast]
3227
- } : item);
3228
- else if (wrap.type === "style-rule") ast = [{
3229
- type: "style-rule",
3230
- selector: wrap.selector,
3231
- source: wrap.source,
3232
- nodes: Array.isArray(ast) ? ast : [ast]
3233
- }];
3234
- else if (wrap.type === "at-rule") ast = [{
3235
- type: "at-rule",
3236
- name: wrap.name || "media",
3237
- params: wrap.params,
3238
- source: wrap.source,
3239
- nodes: Array.isArray(ast) ? ast : [ast]
3240
- }];
3241
- else if (wrap.type === "rule") ast = [{
3242
- type: "rule",
3243
- selector: wrap.selector,
3244
- source: wrap.source,
3245
- nodes: Array.isArray(ast) ? ast : [ast]
3246
- }];
3247
- }
3248
- }
3249
- return applyVarPrefix(ast, ctx);
3250
- }
3251
- /**
3252
- * Generates CSS from a list of BaroJsonInput objects.
3253
- *
3254
- * @param inputs Array of BaroJsonInput
3255
- * @param ctx Context
3256
- * @param opts Options (minify, etc.)
3257
- * @returns CSS string
3258
- */
3259
- function generateCssFromJson(inputs, ctx, opts) {
3260
- const allAtRootNodes = [];
3261
- const cssList = [];
3262
- inputs.forEach((input) => {
3263
- const cleanAst = optimizeAst(jsonToAst(input, ctx));
3264
- cleanAst.forEach((node) => {
3265
- if (node.type === "at-root") allAtRootNodes.push(...node.nodes);
3266
- });
3267
- let reconstructedName = input.utility.name;
3268
- if (input.utility.value) reconstructedName += `-${input.utility.value}`;
3269
- if (input.utility.arbitrary) reconstructedName = `${input.utility.name}-[${input.utility.value}]`;
3270
- if (input.variants) reconstructedName = `${input.variants.map((v) => typeof v === "string" ? v : v.name).join(":")}:${reconstructedName}`;
3271
- const css = astToCss(cleanAst, cleanAst.some((node) => node.type === "style-rule") ? void 0 : `.${reconstructedName.replace(/[^a-zA-Z0-9-_]/g, "\\$&")}`, {
3272
- minify: opts?.minify,
3273
- important: input.utility.important ?? false
3274
- });
3275
- if (css) cssList.push(css);
3276
- });
3277
- const rootCss = rootToCss(allAtRootNodes);
3278
- return `${rootCss ? `:root,:host {${rootCss}}` : ""}${cssList.join(opts?.minify ? "" : "\n")}`;
3279
- }
3280
- //#endregion
3281
- //#region src/core/utils.ts
3282
- /**
3283
- * Parses a fraction string (e.g., '1/2') and returns a percentage string (e.g., '50%'), or null if not a valid fraction.
3284
- */
3285
- function parseFraction(input) {
3286
- if (input.includes("/")) {
3287
- const [num, denom] = input.split("/").map(Number);
3288
- if (!isNaN(num) && !isNaN(denom) && denom !== 0) return `${num / denom * 100}%`;
3289
- }
3290
- return null;
3291
- }
3292
- /**
3293
- * Returns the input if it is a valid non-negative integer string, else null.
3294
- *
3295
- * @example
3296
- * parseNumber("10") // "10"
3297
- * parseNumber("-10") // "-10"
3298
- * parseNumber("10.5") // "10.5"
3299
- */
3300
- function parseNumber(input) {
3301
- return /^-?\d+(?:\.\d+)?$/.test(input) ? input : null;
3302
- }
3303
- /**
3304
- * Returns the input if it is a valid length string, else null.
3305
- */
3306
- function parseLength(input) {
3307
- return /^\d+(px|em|rem|vh|vw|vmin|vmax|%|in|cm|mm|pt|pc|ex|ch|fr)$/.test(input) ? input : null;
3308
- }
3309
- /**
3310
- * Unified parser for fraction or number, with options for percent or repeat syntax.
3311
- * - percent: if true, returns percentage for fraction (e.g., '1/2' -> '50%')
3312
- * - repeat: if true, returns repeat() for number (e.g., '3' -> 'repeat(3, minmax(0, 1fr))')
3313
- */
3314
- function parseFractionOrNumber(value, opts = {}) {
3315
- if (/^\d+$/.test(value)) {
3316
- if (opts.repeat) return `repeat(${value}, minmax(0, 1fr))`;
3317
- return value;
4018
+ function hasPreset(themeObj, category, preset) {
4019
+ return themeObj[category]?.includes?.(preset);
4020
+ }
4021
+ function resolveTheme(config) {
4022
+ let theme = {};
4023
+ if (config.presets) {
4024
+ for (const preset of config.presets) if (preset.theme) theme = deepMerge(theme, preset.theme);
3318
4025
  }
3319
- if (value.includes("/")) {
3320
- const [numerator, denominator] = value.split("/").map(Number);
3321
- if (!isNaN(numerator) && !isNaN(denominator) && denominator !== 0) {
3322
- const result = numerator / denominator;
3323
- if (opts.percent) return `${result * 100}%`;
3324
- return result.toString();
3325
- }
4026
+ if (config.theme) {
4027
+ const { extend, ...overrideTheme } = config.theme;
4028
+ theme = deepMerge(theme, overrideTheme);
4029
+ if (extend) theme = deepMerge(theme, extend);
3326
4030
  }
3327
- return null;
4031
+ return theme;
3328
4032
  }
3329
- var CSS_COLOR_NAMES = /* @__PURE__ */ new Set([
3330
- "aliceblue",
3331
- "antiquewhite",
3332
- "aqua",
3333
- "aquamarine",
3334
- "azure",
3335
- "beige",
3336
- "bisque",
3337
- "black",
3338
- "blanchedalmond",
3339
- "blue",
3340
- "blueviolet",
3341
- "brown",
3342
- "burlywood",
3343
- "cadetblue",
3344
- "chartreuse",
3345
- "chocolate",
3346
- "coral",
3347
- "cornflowerblue",
3348
- "cornsilk",
3349
- "crimson",
3350
- "cyan",
3351
- "darkblue",
3352
- "darkcyan",
3353
- "darkgoldenrod",
3354
- "darkgray",
3355
- "darkgreen",
3356
- "darkgrey",
3357
- "darkkhaki",
3358
- "darkmagenta",
3359
- "darkolivegreen",
3360
- "darkorange",
3361
- "darkorchid",
3362
- "darkred",
3363
- "darksalmon",
3364
- "darkseagreen",
3365
- "darkslateblue",
3366
- "darkslategray",
3367
- "darkslategrey",
3368
- "darkturquoise",
3369
- "darkviolet",
3370
- "deeppink",
3371
- "deepskyblue",
3372
- "dimgray",
3373
- "dimgrey",
3374
- "dodgerblue",
3375
- "firebrick",
3376
- "floralwhite",
3377
- "forestgreen",
3378
- "fuchsia",
3379
- "gainsboro",
3380
- "ghostwhite",
3381
- "gold",
3382
- "goldenrod",
3383
- "gray",
3384
- "grey",
3385
- "green",
3386
- "greenyellow",
3387
- "honeydew",
3388
- "hotpink",
3389
- "indianred",
3390
- "indigo",
3391
- "ivory",
3392
- "khaki",
3393
- "lavender",
3394
- "lavenderblush",
3395
- "lawngreen",
3396
- "lemonchiffon",
3397
- "lightblue",
3398
- "lightcoral",
3399
- "lightcyan",
3400
- "lightgoldenrodyellow",
3401
- "lightgray",
3402
- "lightgreen",
3403
- "lightgrey",
3404
- "lightpink",
3405
- "lightsalmon",
3406
- "lightseagreen",
3407
- "lightskyblue",
3408
- "lightslategray",
3409
- "lightslategrey",
3410
- "lightsteelblue",
3411
- "lightyellow",
3412
- "lime",
3413
- "limegreen",
3414
- "linen",
3415
- "magenta",
3416
- "maroon",
3417
- "mediumaquamarine",
3418
- "mediumblue",
3419
- "mediumorchid",
3420
- "mediumpurple",
3421
- "mediumseagreen",
3422
- "mediumslateblue",
3423
- "mediumspringgreen",
3424
- "mediumturquoise",
3425
- "mediumvioletred",
3426
- "midnightblue",
3427
- "mintcream",
3428
- "mistyrose",
3429
- "moccasin",
3430
- "navajowhite",
3431
- "navy",
3432
- "oldlace",
3433
- "olive",
3434
- "olivedrab",
3435
- "orange",
3436
- "orangered",
3437
- "orchid",
3438
- "palegoldenrod",
3439
- "palegreen",
3440
- "paleturquoise",
3441
- "palevioletred",
3442
- "papayawhip",
3443
- "peachpuff",
3444
- "peru",
3445
- "pink",
3446
- "plum",
3447
- "powderblue",
3448
- "purple",
3449
- "red",
3450
- "rosybrown",
3451
- "royalblue",
3452
- "saddlebrown",
3453
- "salmon",
3454
- "sandybrown",
3455
- "seagreen",
3456
- "seashell",
3457
- "sienna",
3458
- "silver",
3459
- "skyblue",
3460
- "slateblue",
3461
- "slategray",
3462
- "slategrey",
3463
- "snow",
3464
- "springgreen",
3465
- "steelblue",
3466
- "tan",
3467
- "teal",
3468
- "thistle",
3469
- "tomato",
3470
- "turquoise",
3471
- "violet",
3472
- "wheat",
3473
- "white",
3474
- "whitesmoke",
3475
- "yellow",
3476
- "yellowgreen"
3477
- ]);
3478
- /**
3479
- * Returns the input if it is a valid color string, else null.
3480
- *
3481
- * #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(r, g, b), rgb(r, g, b, a), hsl(h, s, l), hsl(h, s, l, a), hwb(h, w, b), hwb(h, w, b, a), lab(l, a, b), lab(l, a, b, a), lch(l, c, h), lch(l, c, h, a), oklab(l, a, b), oklab(l, a, b, a), oklch(l, c, h), oklch(l, c, h, a), color-mix(in oklab, var(--color-blue-500) 60%, transparent)
3482
- */
3483
- function parseColor(input) {
3484
- if (CSS_COLOR_NAMES.has(input.toLowerCase())) return input;
3485
- if (input.startsWith("color:var(")) return input.slice(6);
3486
- if (input.startsWith("color:")) return parseColor(input.slice(6));
3487
- if (input.startsWith("#") && input.length === 4) return `#${input.slice(1)}`;
3488
- if (input.startsWith("#") && input.length === 5) return `#${input.slice(1)}`;
3489
- if (input.startsWith("#") && input.length === 7) return `#${input.slice(1)}`;
3490
- if (input.startsWith("#") && input.length === 9) return `#${input.slice(1)}`;
3491
- if (input.startsWith("rgb(")) return input.slice(4, -1);
3492
- if (input.startsWith("rgba(")) return input.slice(5, -1);
3493
- if (input.startsWith("hsl(")) return input.slice(4, -1);
3494
- if (input.startsWith("hsla(")) return input.slice(5, -1);
3495
- if (input.startsWith("hwb(")) return input.slice(4, -1);
3496
- if (input.startsWith("lab(")) return input.slice(4, -1);
3497
- if (input.startsWith("lch(")) return input.slice(4, -1);
3498
- if (input.startsWith("oklab(")) return input.slice(5, -1);
3499
- if (input.startsWith("oklch(")) return input.slice(6, -1);
3500
- if (input.startsWith("color-mix(")) return input.slice(9, -1);
3501
- return null;
4033
+ function themeToCssVars(theme) {
4034
+ return toCssVarsBlock(themeToCssVarsAll(theme));
3502
4035
  }
3503
- var COLOR_KEYWORDS = /* @__PURE__ */ new Set([
3504
- "inherit",
3505
- "currentcolor",
3506
- "transparent"
3507
- ]);
4036
+ function createContext(configObj) {
4037
+ if (configObj.debug !== void 0) setDebug(!!configObj.debug);
4038
+ const configWithDefaults = {
4039
+ presets: [{ theme: require_theme.defaultTheme }, ...configObj.presets || []],
4040
+ ...configObj
4041
+ };
4042
+ const themeObj = resolveTheme(configWithDefaults);
4043
+ const ctx = {
4044
+ hasPreset: (category, preset) => {
4045
+ return hasPreset(themeObj, category, preset);
4046
+ },
4047
+ theme: (...args) => {
4048
+ return themeGetter(themeObj, ...args);
4049
+ },
4050
+ config: (...args) => {
4051
+ return configGetter(configWithDefaults, ...args);
4052
+ },
4053
+ themeToCssVars: () => themeToCssVars(themeObj),
4054
+ extendTheme: (category, values) => {
4055
+ if (typeof values === "function") {
4056
+ const result = values(ctx.theme);
4057
+ if (result && typeof result === "object") {
4058
+ const existingValues = themeObj[category] || {};
4059
+ themeObj[category] = {
4060
+ ...existingValues,
4061
+ ...result
4062
+ };
4063
+ }
4064
+ } else if (typeof values === "object" && values !== null && !Array.isArray(values)) {
4065
+ const existingValues = themeObj[category] || {};
4066
+ themeObj[category] = {
4067
+ ...existingValues,
4068
+ ...values
4069
+ };
4070
+ }
4071
+ clearContextCaches(ctx);
4072
+ },
4073
+ getPreflightCSS: (level = true) => {
4074
+ return getPreflightCSS(level);
4075
+ }
4076
+ };
4077
+ initializeContextState(ctx, getUtility(), getModifier());
4078
+ registerCustomUtilities(ctx, configObj.utilities);
4079
+ return ctx;
4080
+ }
4081
+ //#endregion
4082
+ //#region src/core/jsonToAst.ts
3508
4083
  /**
3509
- * Declarations for a theme colour (#228), as Tailwind v4 emits them: `var(--color-<key>)` so runtime theme
3510
- * overrides apply; with an opacity modifier, a literal srgb color-mix fallback plus an oklab color-mix of the var.
4084
+ * Converts a single BaroJsonInput object into an AST tree.
4085
+ * Bypasses string parsing and directly invokes utility/modifier handlers.
4086
+ *
4087
+ * @param input BaroJsonInput object
4088
+ * @param ctx Context
4089
+ * @returns AstNode[]
3511
4090
  */
3512
- function themeColorDecls(prop, value, extra) {
3513
- const key = String(extra.realThemeValue);
3514
- const ref = COLOR_KEYWORDS.has(value.toLowerCase()) || value.startsWith("var(") || !/^[\w-]+$/.test(key) ? value : `var(--color-${key})`;
3515
- if (!extra.opacity) return [decl(prop, ref)];
3516
- const alpha = normalizeAlpha(String(extra.opacity));
3517
- const supports = (amount) => atRule("supports", "(color:color-mix(in lab, red, red))", [decl(prop, `color-mix(in oklab, ${ref} ${amount}, transparent)`)]);
3518
- if (alpha.isVar) return [decl(prop, value), supports(alpha.amount)];
3519
- return [decl(prop, `color-mix(in srgb, ${value} ${alpha.amount}, transparent)`), supports(alpha.amount)];
4091
+ function jsonToAst(input, ctx) {
4092
+ if ((input.variants || []).some((v) => typeof v === "string" ? !isSafeVariantToken(v) : !isSafeVariantValue(v.name || "") || !isSafeVariantValue(v.value || "") || hasCommentToken(v.name || "") || hasCommentToken(v.value || ""))) return [];
4093
+ let utilReg = getUtility(ctx).find((u) => u.name === input.utility.name);
4094
+ if (input.utility.value && !input.utility.arbitrary && !input.utility.customProperty) {
4095
+ const fullName = `${input.utility.name}-${input.utility.value}`;
4096
+ const exactMatch = getUtility(ctx).find((u) => u.name === fullName);
4097
+ if (exactMatch) utilReg = exactMatch;
4098
+ }
4099
+ if (!utilReg) {
4100
+ debugWarn(`[jsonToAst] Unknown utility: "${input.utility.name}"`);
4101
+ return [];
4102
+ }
4103
+ const parsedUtility = {
4104
+ prefix: input.utility.name,
4105
+ value: input.utility.value,
4106
+ arbitrary: input.utility.arbitrary,
4107
+ negative: input.utility.negative,
4108
+ opacity: input.utility.opacity,
4109
+ important: input.utility.important,
4110
+ customProperty: input.utility.customProperty,
4111
+ category: utilReg.category,
4112
+ priority: utilReg.priority
4113
+ };
4114
+ let value = input.utility.value;
4115
+ if (input.utility.negative && value) value = "-" + value;
4116
+ let ast = utilReg.handler(value || "", ctx, parsedUtility, utilReg) || [];
4117
+ if (input.variants && input.variants.length > 0) {
4118
+ const wrappers = [];
4119
+ const selector = "&";
4120
+ for (let i = input.variants.length - 1; i >= 0; i--) {
4121
+ const variantInput = input.variants[i];
4122
+ const variantName = typeof variantInput === "string" ? variantInput : variantInput.name;
4123
+ const variantValue = typeof variantInput === "string" ? void 0 : variantInput.value;
4124
+ const variantArbitrary = typeof variantInput === "string" ? false : variantInput.arbitrary;
4125
+ const parsedModifier = {
4126
+ type: variantName,
4127
+ value: variantValue,
4128
+ arbitrary: variantArbitrary
4129
+ };
4130
+ let matchKey = variantName;
4131
+ if (variantArbitrary && variantValue) {
4132
+ if (variantName) {
4133
+ matchKey = `${variantName}-[${variantValue}]`;
4134
+ parsedModifier.type = matchKey;
4135
+ } else {
4136
+ matchKey = `[${variantValue}]`;
4137
+ parsedModifier.type = matchKey;
4138
+ }
4139
+ } else if (variantValue) {
4140
+ matchKey = `${variantName}-[${variantValue}]`;
4141
+ parsedModifier.type = matchKey;
4142
+ }
4143
+ const plugin = getModifier(ctx).find((p) => p.match(matchKey, ctx));
4144
+ if (!plugin) {
4145
+ debugWarn(`[jsonToAst] Unknown variant: "${matchKey}"`);
4146
+ continue;
4147
+ }
4148
+ if (plugin.astHandler) ast = plugin.astHandler(ast, parsedModifier, ctx, [], i);
4149
+ if (plugin.modifySelector) {
4150
+ const result = plugin.modifySelector({
4151
+ selector,
4152
+ fullClassName: "JSON_GENERATED",
4153
+ mod: parsedModifier,
4154
+ context: ctx,
4155
+ variantChain: [],
4156
+ index: i
4157
+ });
4158
+ if (result == null) continue;
4159
+ if (plugin.wrap && (result === "&" || typeof result === "object" && !Array.isArray(result) && result.selector === "&" || Array.isArray(result) && result.length === 1 && result[0].selector === "&")) {} else if (typeof result === "string" && result.includes("&")) wrappers.push({
4160
+ type: "rule",
4161
+ selector: result
4162
+ });
4163
+ else if (typeof result === "object" && !Array.isArray(result) && result.selector) {
4164
+ const r = result;
4165
+ const wrappingType = r.wrappingType || "rule";
4166
+ wrappers.push({
4167
+ type: wrappingType,
4168
+ selector: r.selector,
4169
+ flatten: r.flatten,
4170
+ source: r.source
4171
+ });
4172
+ } else if (Array.isArray(result)) wrappers.push({
4173
+ type: "wrap",
4174
+ items: result.map((r) => ({
4175
+ type: r.wrappingType || "rule",
4176
+ selector: r.selector,
4177
+ source: r.source,
4178
+ nodes: []
4179
+ }))
4180
+ });
4181
+ }
4182
+ if (plugin.wrap) wrappers.push({
4183
+ type: "wrap",
4184
+ items: plugin.wrap(parsedModifier, ctx)
4185
+ });
4186
+ }
4187
+ for (let i = 0; i < wrappers.length; i++) {
4188
+ const wrap = wrappers[i];
4189
+ if (wrap.type === "wrap") ast = wrap.items.map((item) => item.type === "rule" || item.type === "style-rule" || item.type === "at-rule" || item.type === "at-root" ? {
4190
+ ...item,
4191
+ nodes: [...item.nodes || [], ...ast]
4192
+ } : item);
4193
+ else if (wrap.type === "style-rule") ast = [{
4194
+ type: "style-rule",
4195
+ selector: wrap.selector,
4196
+ source: wrap.source,
4197
+ nodes: Array.isArray(ast) ? ast : [ast]
4198
+ }];
4199
+ else if (wrap.type === "at-rule") ast = [{
4200
+ type: "at-rule",
4201
+ name: wrap.name || "media",
4202
+ params: wrap.params,
4203
+ source: wrap.source,
4204
+ nodes: Array.isArray(ast) ? ast : [ast]
4205
+ }];
4206
+ else if (wrap.type === "rule") ast = [{
4207
+ type: "rule",
4208
+ selector: wrap.selector,
4209
+ source: wrap.source,
4210
+ nodes: Array.isArray(ast) ? ast : [ast]
4211
+ }];
4212
+ }
4213
+ }
4214
+ return applyVarPrefix(ast, ctx);
3520
4215
  }
3521
4216
  /**
3522
- * Opacity modifier → color-mix amount, as Tailwind v4 does: `50` → `50%`, `[37%]` → `37%`, `[0.5]` / `[.8]` → `50%` /
3523
- * `80%` (a bracketed number ≤ 1 is a fraction), `[var(--a)]` / `(--a)` → `var(--a)`.
4217
+ * Generates CSS from a list of BaroJsonInput objects.
4218
+ *
4219
+ * @param inputs Array of BaroJsonInput
4220
+ * @param ctx Context
4221
+ * @param opts Options (minify, etc.)
4222
+ * @returns CSS string
3524
4223
  */
3525
- function normalizeAlpha(raw) {
3526
- let v = raw.trim();
3527
- const bracketed = v.startsWith("[") && v.endsWith("]");
3528
- if (bracketed) v = v.slice(1, -1).trim();
3529
- if (v.startsWith("(") && v.endsWith(")")) v = `var(${v.slice(1, -1).trim()})`;
3530
- if (v.startsWith("var(")) return {
3531
- amount: v,
3532
- isVar: true
3533
- };
3534
- if (v.endsWith("%")) return {
3535
- amount: v,
3536
- isVar: false
3537
- };
3538
- const n = Number(v);
3539
- if (v !== "" && Number.isFinite(n)) return {
3540
- amount: `${+(bracketed && n <= 1 ? n * 100 : n).toFixed(4)}%`,
3541
- isVar: false
3542
- };
3543
- return {
3544
- amount: v,
3545
- isVar: false
3546
- };
4224
+ function generateCssFromJson(inputs, ctx, opts) {
4225
+ const allAtRootNodes = [];
4226
+ const cssList = [];
4227
+ inputs.forEach((input) => {
4228
+ const cleanAst = optimizeAst(jsonToAst(input, ctx));
4229
+ cleanAst.forEach((node) => {
4230
+ if (node.type === "at-root") allAtRootNodes.push(...node.nodes);
4231
+ });
4232
+ let reconstructedName = input.utility.name;
4233
+ if (input.utility.value) reconstructedName += `-${input.utility.value}`;
4234
+ if (input.utility.arbitrary) reconstructedName = `${input.utility.name}-[${input.utility.value}]`;
4235
+ if (input.variants) reconstructedName = `${input.variants.map((v) => typeof v === "string" ? v : v.name).join(":")}:${reconstructedName}`;
4236
+ const css = astToCss(cleanAst, cleanAst.some((node) => node.type === "style-rule") ? void 0 : `.${reconstructedName.replace(/[^a-zA-Z0-9-_]/g, "\\$&")}`, {
4237
+ minify: opts?.minify,
4238
+ important: input.utility.important ?? false
4239
+ });
4240
+ if (css) cssList.push(css);
4241
+ });
4242
+ const rootCss = rootToCss(allAtRootNodes);
4243
+ return `${rootCss ? `:root,:host {${rootCss}}` : ""}${cssList.join(opts?.minify ? "" : "\n")}`;
3547
4244
  }
3548
4245
  //#endregion
3549
4246
  //#region src/presets/interactivity.ts
@@ -3557,10 +4254,7 @@ functionalUtility({
3557
4254
  supportsArbitrary: true,
3558
4255
  supportsCustomProperty: true,
3559
4256
  handle: (value, _ctx, _token, extra) => {
3560
- if (extra?.realThemeValue) {
3561
- if (extra.opacity) return [atRule("supports", `(color:color-mix(in lab, red, red))`, [decl("accent-color", `color-mix(in lab, ${value} ${extra.opacity}%, transparent)`)]), decl("accent-color", value)];
3562
- return [decl("accent-color", `var(--color-${extra.realThemeValue})`)];
3563
- }
4257
+ if (extra?.realThemeValue) return themeColorDecls("accent-color", value, extra);
3564
4258
  return [decl("accent-color", value)];
3565
4259
  },
3566
4260
  handleCustomProperty: (value) => [decl("accent-color", `var(${value})`)],
@@ -3579,10 +4273,7 @@ functionalUtility({
3579
4273
  supportsArbitrary: true,
3580
4274
  supportsCustomProperty: true,
3581
4275
  handle: (value, ctx, token, extra) => {
3582
- if (extra?.realThemeValue) {
3583
- if (extra.opacity) return [atRule("supports", `(color:color-mix(in lab, red, red))`, [decl("caret-color", `color-mix(in lab, ${value} ${extra.opacity}%, transparent)`)]), decl("caret-color", value)];
3584
- return [decl("caret-color", `var(--color-${extra.realThemeValue})`)];
3585
- }
4276
+ if (extra?.realThemeValue) return themeColorDecls("caret-color", value, extra);
3586
4277
  return [decl("caret-color", value)];
3587
4278
  },
3588
4279
  handleCustomProperty: (value) => [decl("caret-color", `var(${value})`)],
@@ -3947,22 +4638,11 @@ staticUtility("caption-bottom", [["caption-side", "bottom"]], { category: "table
3947
4638
  //#region src/presets/shadow-color.ts
3948
4639
  /** Opacity modifier to an alpha: `50` → 50%, `[20%]` → 20%, `(--o)` → var(--o); anything else is invalid. */
3949
4640
  function parseAlpha(op) {
3950
- if (!op) return null;
3951
- if (/^\d+(\.\d+)?$/.test(op)) return {
3952
- alpha: `${op}%`,
3953
- isVar: false
3954
- };
3955
- const pct = /^\[(\d+(?:\.\d+)?)%\]$/.exec(op);
3956
- if (pct) return {
3957
- alpha: `${pct[1]}%`,
3958
- isVar: false
3959
- };
3960
- const cp = /^\((--[\w-]+)\)$/.exec(op);
3961
- if (cp) return {
3962
- alpha: `var(${cp[1]})`,
3963
- isVar: true
4641
+ const a = op ? normalizeAlpha(op) : null;
4642
+ return a && {
4643
+ alpha: a.amount,
4644
+ isVar: a.isVar
3964
4645
  };
3965
- return null;
3966
4646
  }
3967
4647
  function splitTop(value, sep) {
3968
4648
  const out = [];
@@ -4471,6 +5151,11 @@ function layerColor(layer, main, opacity, token, realThemeValue) {
4471
5151
  if (main.startsWith("color:")) return shadowColorDecls(layer, `var(${main.slice(6)})`, opacity);
4472
5152
  if (token.arbitrary && parseColor(main)) return shadowColorDecls(layer, main, opacity);
4473
5153
  }
5154
+ function customShadowAlpha(layer, opacity) {
5155
+ if (!opacity) return [];
5156
+ const a = parseAlpha(opacity);
5157
+ return a ? [decl(`--baro-${layer}-alpha`, a.alpha)] : null;
5158
+ }
4474
5159
  for (const layer of ["shadow", "inset-shadow"]) functionalUtility({
4475
5160
  name: layer,
4476
5161
  supportsArbitrary: true,
@@ -4487,11 +5172,17 @@ for (const layer of ["shadow", "inset-shadow"]) functionalUtility({
4487
5172
  if (token.arbitrary) return boxShadowLayer(layer, layer === "inset-shadow" ? insetEach(value) : value, opacity);
4488
5173
  return null;
4489
5174
  },
4490
- handleCustomProperty: (value) => value.startsWith("color:") ? shadowColorDecls(layer, `var(${value.slice(6)})`, void 0) ?? [] : [
4491
- ringShadowProperties(),
4492
- decl(`--baro-${layer}`, layer === "inset-shadow" ? `inset var(${value})` : `var(${value})`),
4493
- decl("box-shadow", SHADOW_COMPOSITE)
4494
- ]
5175
+ ownsOpacity: true,
5176
+ handleCustomProperty: (value, _ctx, _token, extra) => {
5177
+ if (value.startsWith("color:")) return shadowColorDecls(layer, `var(${value.slice(6)})`, extra?.opacity) ?? [];
5178
+ const alpha = customShadowAlpha(layer, extra?.opacity);
5179
+ return alpha ? [
5180
+ ringShadowProperties(),
5181
+ ...alpha,
5182
+ decl(`--baro-${layer}`, layer === "inset-shadow" ? `inset var(${value})` : `var(${value})`),
5183
+ decl("box-shadow", SHADOW_COMPOSITE)
5184
+ ] : [];
5185
+ }
4495
5186
  });
4496
5187
  var textShadowProperties = () => atRoot([property("--baro-text-shadow-color"), property("--baro-text-shadow-alpha", "100%", "<percentage>")]);
4497
5188
  var namedTextShadow = (ctx, name) => {
@@ -4521,7 +5212,19 @@ functionalUtility({
4521
5212
  if (token.arbitrary) return textShadowValue(value, opacity);
4522
5213
  return null;
4523
5214
  },
4524
- handleCustomProperty: (value) => value.startsWith("color:") ? [textShadowProperties(), ...shadowColorDecls("text-shadow", `var(${value.slice(6)})`, void 0) ?? []] : [textShadowProperties(), decl("text-shadow", `var(${value})`)],
5215
+ ownsOpacity: true,
5216
+ handleCustomProperty: (value, _ctx, _token, extra) => {
5217
+ if (value.startsWith("color:")) {
5218
+ const color = shadowColorDecls("text-shadow", `var(${value.slice(6)})`, extra?.opacity);
5219
+ return color ? [textShadowProperties(), ...color] : [];
5220
+ }
5221
+ const alpha = customShadowAlpha("text-shadow", extra?.opacity);
5222
+ return alpha ? [
5223
+ textShadowProperties(),
5224
+ ...alpha,
5225
+ decl("text-shadow", `var(${value})`)
5226
+ ] : [];
5227
+ },
4525
5228
  category: "effects"
4526
5229
  });
4527
5230
  [
@@ -4585,17 +5288,6 @@ functionalUtility({
4585
5288
  ], { category: "effects" });
4586
5289
  });
4587
5290
  staticUtility("ring-inset", [["--baro-ring-inset", "inset"]], { category: "effects" });
4588
- function createRingColorDecls(key, main, opacity, realThemeValue) {
4589
- const colorVar = `var(--color-${realThemeValue})`;
4590
- let colorMix = colorVar;
4591
- let fallback = colorVar;
4592
- if (opacity) {
4593
- colorMix = `color-mix(in oklab, ${colorVar} ${opacity}%, transparent)`;
4594
- if (parseColor(main) && main.startsWith("#")) fallback = `${main}${Math.round(Number(opacity) / 100 * 255).toString(16).padStart(2, "0")}`;
4595
- else fallback = colorMix;
4596
- }
4597
- return [atRule("supports", "(color:color-mix(in lab, red, red))", [decl(key, colorMix)]), decl(key, fallback)];
4598
- }
4599
5291
  functionalUtility({
4600
5292
  name: "ring",
4601
5293
  supportsArbitrary: true,
@@ -4609,28 +5301,9 @@ functionalUtility({
4609
5301
  decl("--baro-ring-shadow", ringShadowValue(value)),
4610
5302
  decl("box-shadow", SHADOW_COMPOSITE)
4611
5303
  ];
4612
- const opacity = extra?.opacity;
4613
- const realThemeValue = extra?.realThemeValue;
4614
- if (realThemeValue) return createRingColorDecls("--baro-ring-color", main, opacity, realThemeValue);
4615
- if (main.startsWith("color:")) {
4616
- const cp = main.replace("color:", "");
4617
- let colorMix = `var(${cp})`;
4618
- let fallback = colorMix;
4619
- if (opacity) {
4620
- colorMix = `color-mix(in oklab, var(${cp}) ${opacity}%, transparent)`;
4621
- fallback = colorMix;
4622
- }
4623
- return [atRule("supports", "(color:color-mix(in lab, red, red))", [decl("--baro-ring-color", colorMix)]), decl("--baro-ring-color", fallback)];
4624
- }
5304
+ if (extra?.realThemeValue) return themeColorDecls("--baro-ring-color", main, extra);
5305
+ if (main.startsWith("color:")) return [decl("--baro-ring-color", `var(${main.slice(6)})`)];
4625
5306
  if (token.arbitrary) {
4626
- let colorMix = main;
4627
- let fallback = main;
4628
- if (opacity) {
4629
- colorMix = `color-mix(in oklab, ${main} ${opacity}%, transparent)`;
4630
- if (parseColor(main) && main.startsWith("#")) fallback = `${main}${Math.round(Number(opacity) / 100 * 255).toString(16).padStart(2, "0")}`;
4631
- else fallback = colorMix;
4632
- return [atRule("supports", "(color:color-mix(in lab, red, red))", [decl("--baro-ring-color", colorMix)]), decl("--baro-ring-color", fallback)];
4633
- }
4634
5307
  if (!parseColor(main) && /^(-?(\d+\.?\d*|\.\d+)(px|rem|em|%|vw|vh|vmin|vmax|ch|ex|pt|cm|mm|in|pc)|0|(length:.+)|calc\(.+\))$/i.test(main)) {
4635
5308
  const width = main.startsWith("length:") ? main.slice(7) : main;
4636
5309
  return [
@@ -4657,30 +5330,9 @@ functionalUtility({
4657
5330
  themeKeys: ["colors", "shadows"],
4658
5331
  handle: (value, ctx, token, extra) => {
4659
5332
  const main = value;
4660
- const opacity = extra?.opacity;
4661
- const realThemeValue = extra?.realThemeValue;
4662
- if (realThemeValue) return createRingColorDecls("--baro-inset-ring-color", main, opacity, realThemeValue);
4663
- if (main.startsWith("color:")) {
4664
- const cp = main.replace("color:", "");
4665
- let colorMix = `var(${cp})`;
4666
- let fallback = colorMix;
4667
- if (opacity) {
4668
- colorMix = `color-mix(in oklab, var(${cp}) ${opacity}%, transparent)`;
4669
- fallback = colorMix;
4670
- }
4671
- return [atRule("supports", "(color:color-mix(in lab, red, red))", [decl("--baro-inset-ring-color", colorMix)]), decl("--baro-inset-ring-color", fallback)];
4672
- }
4673
- if (token.arbitrary) {
4674
- let colorMix = main;
4675
- let fallback = main;
4676
- if (opacity) {
4677
- colorMix = `color-mix(in oklab, ${main} ${opacity}%, transparent)`;
4678
- if (parseColor(main) && main.startsWith("#")) fallback = `${main}${Math.round(Number(opacity) / 100 * 255).toString(16).padStart(2, "0")}`;
4679
- else fallback = colorMix;
4680
- return [atRule("supports", "(color:color-mix(in lab, red, red))", [decl("--baro-inset-ring-color", colorMix)]), decl("--baro-inset-ring-color", fallback)];
4681
- }
4682
- return [decl("box-shadow", `inset ${main}`)];
4683
- }
5333
+ if (extra?.realThemeValue) return themeColorDecls("--baro-inset-ring-color", main, extra);
5334
+ if (main.startsWith("color:")) return [decl("--baro-inset-ring-color", `var(${main.slice(6)})`)];
5335
+ if (token.arbitrary) return [parseColor(main) || /^var\(--[^)]+\)$/.test(main) ? decl("--baro-inset-ring-color", main) : decl("box-shadow", `inset ${main}`)];
4684
5336
  if (main === "inherit" || main === "current" || main === "transparent") return [decl("--baro-inset-ring-color", main === "current" ? "currentColor" : main)];
4685
5337
  return null;
4686
5338
  },
@@ -6505,7 +7157,7 @@ functionalUtility({
6505
7157
  supportsOpacity: true,
6506
7158
  handle: (value, _ctx, _token, extra) => {
6507
7159
  if (extra?.realThemeValue) return [rule("&::placeholder", themeColorDecls("color", value, extra))];
6508
- if (parseColor(value)) return placeholderColor(value);
7160
+ if (parseColor(value) || /^var\(--[\w-]+\)$/.test(value)) return placeholderColor(value);
6509
7161
  return null;
6510
7162
  },
6511
7163
  handleCustomProperty: (value) => placeholderColor(`var(${value})`),
@@ -6711,7 +7363,7 @@ var stopsDecls = (stop, color) => {
6711
7363
  if (parseColor(value)) return stopsDecls(stop, value);
6712
7364
  return null;
6713
7365
  },
6714
- handleCustomProperty: (value) => [decl(`--baro-gradient-${stop}`, `var(${value})`)],
7366
+ handleCustomProperty: (value) => value.startsWith("color:") ? stopsDecls(stop, `var(${value.slice(6)})`) : [decl(`--baro-gradient-${stop}`, `var(${value})`)],
6715
7367
  description: `${stop} gradient stop utility (color, percent, custom property, arbitrary supported)`,
6716
7368
  category: "background"
6717
7369
  });
@@ -6893,11 +7545,11 @@ var withBorderStyle = (props, width) => [
6893
7545
  ...propList.map((prop) => [prop.replace("width", "style"), "var(--baro-border-style)"]),
6894
7546
  ...propList.map((prop) => [prop, width])
6895
7547
  ];
6896
- staticUtility(`${name}-0`, styled("0px"));
6897
- staticUtility(`${name}-2`, styled("2px"));
6898
- staticUtility(`${name}-4`, styled("4px"));
6899
- staticUtility(`${name}-8`, styled("8px"));
6900
- staticUtility(`${name}`, styled("1px"));
7548
+ staticUtility(`${name}-0`, styled("0px"), { category: "borders" });
7549
+ staticUtility(`${name}-2`, styled("2px"), { category: "borders" });
7550
+ staticUtility(`${name}-4`, styled("4px"), { category: "borders" });
7551
+ staticUtility(`${name}-8`, styled("8px"), { category: "borders" });
7552
+ staticUtility(`${name}`, styled("1px"), { category: "borders" });
6901
7553
  functionalUtility({
6902
7554
  name,
6903
7555
  themeKeys: ["colors", "borderWidth"],
@@ -7067,7 +7719,7 @@ functionalUtility({
7067
7719
  return null;
7068
7720
  },
7069
7721
  handleCustomProperty: (value) => {
7070
- if (value.startsWith("color:")) return [decl("outline-color", value.replace("color:", ""))];
7722
+ if (value.startsWith("color:")) return [decl("outline-color", `var(${value.slice(6)})`)];
7071
7723
  if (value.startsWith("length:")) return withOutlineStyle(`var(${value.replace("length:", "")})`);
7072
7724
  return [decl("outline-color", `var(${value})`)];
7073
7725
  },
@@ -7099,7 +7751,7 @@ functionalUtility({
7099
7751
  handle: (value, _ctx, token, extra) => {
7100
7752
  if (token.prefix !== "divide") return null;
7101
7753
  if (extra?.realThemeValue) return [rule(":where(& > :not(:last-child))", themeColorDecls("border-color", value, extra))];
7102
- if (parseColor(value)) return divideColor(value);
7754
+ if (parseColor(value) || /^var\(--[\w-]+\)$/.test(value)) return divideColor(value);
7103
7755
  return null;
7104
7756
  },
7105
7757
  handleCustomProperty: (value, _ctx, token) => token.prefix === "divide" ? divideColor(`var(${value})`) : [],
@@ -7518,8 +8170,9 @@ functionalUtility({
7518
8170
  themeKeys: ["colors"],
7519
8171
  supportsArbitrary: true,
7520
8172
  supportsCustomProperty: true,
8173
+ supportsOpacity: true,
7521
8174
  handle: (value, ctx, token, extra) => {
7522
- if (extra?.realThemeValue) return [decl("fill", `var(--color-${extra.realThemeValue})`)];
8175
+ if (extra?.realThemeValue) return themeColorDecls("fill", value, extra);
7523
8176
  return [decl("fill", value)];
7524
8177
  },
7525
8178
  description: "fill utility (static, theme, arbitrary, custom property supported)",
@@ -7535,6 +8188,7 @@ functionalUtility({
7535
8188
  themeKeys: ["colors", "strokeWidth"],
7536
8189
  supportsArbitrary: true,
7537
8190
  supportsCustomProperty: true,
8191
+ supportsOpacity: true,
7538
8192
  handle: (value, ctx, token, extra) => {
7539
8193
  if (parseNumber(value) || extra?.themeNamespace === "strokeWidth") return [decl("stroke-width", value)];
7540
8194
  if (token.arbitrary) {
@@ -7542,7 +8196,7 @@ functionalUtility({
7542
8196
  if (hint) return [decl("stroke-width", hint[2])];
7543
8197
  if (!parseColor(value) && (STROKE_LENGTH.test(value) || /^calc\(/.test(value))) return [decl("stroke-width", value)];
7544
8198
  }
7545
- if (extra?.realThemeValue) return [decl("stroke", `var(--color-${extra.realThemeValue})`)];
8199
+ if (extra?.realThemeValue) return themeColorDecls("stroke", value, extra);
7546
8200
  return [decl("stroke", value)];
7547
8201
  },
7548
8202
  handleCustomProperty: (value) => {
@@ -8551,56 +9205,10 @@ functionalModifier((mod) => /^child-(.+)$/.test(mod), ({ selector, mod }) => {
8551
9205
  };
8552
9206
  }, void 0);
8553
9207
  //#endregion
8554
- //#region src/core/rule-order.ts
8555
- var LEADING_AT = /^\s*@(media|container)\s+([^{]*)\{/;
8556
- var LATE_MEDIA = /prefers-color-scheme|\bprint\b|forced-colors|orientation/;
8557
- var MIN_W = /(?:min-width\s*:\s*|width\s*>=?\s*)([\d.]+)(px|rem|em)?/;
8558
- var MAX_W = /(?:max-width\s*:\s*|width\s*<=?\s*)([\d.]+)(px|rem|em)?/;
8559
- function toPx(n, unit) {
8560
- const v = parseFloat(n);
8561
- return unit === "rem" || unit === "em" ? v * 16 : v;
8562
- }
8563
- function preludeKey(kind, prelude) {
8564
- const container = kind === "container";
8565
- if (!container && /^\s*not\b/i.test(prelude)) return [0, 0];
8566
- const min = MIN_W.exec(prelude);
8567
- if (min) return [container ? 4 : 2, toPx(min[1], min[2])];
8568
- const max = MAX_W.exec(prelude);
8569
- if (max) return [container ? 3 : 1, -toPx(max[1], max[2])];
8570
- if (!container && LATE_MEDIA.test(prelude)) return [5, 0];
8571
- return [0, 0];
8572
- }
8573
- function ruleSortKey(rule) {
8574
- const key = [];
8575
- let rest = rule;
8576
- let m;
8577
- while (m = LEADING_AT.exec(rest)) {
8578
- const [g, v] = preludeKey(m[1], m[2]);
8579
- key.push(g, v);
8580
- rest = rest.slice(m[0].length);
8581
- }
8582
- return key;
8583
- }
8584
- function compareKeys(a, b) {
8585
- const n = Math.min(a.length, b.length);
8586
- for (let i = 0; i < n; i++) if (a[i] !== b[i]) return a[i] - b[i];
8587
- return a.length - b.length;
8588
- }
8589
- /** Index after the last key <= `key` (stable upper bound) in sorted `keys`. */
8590
- function upperBound(keys, key) {
8591
- let lo = 0;
8592
- let hi = keys.length;
8593
- while (lo < hi) {
8594
- const mid = lo + hi >> 1;
8595
- if (compareKeys(keys[mid], key) <= 0) lo = mid + 1;
8596
- else hi = mid;
8597
- }
8598
- return lo;
8599
- }
8600
- //#endregion
8601
9208
  exports.AstCache = AstCache;
8602
9209
  exports.IncrementalParser = IncrementalParser;
8603
9210
  exports.ParseResultCache = ParseResultCache;
9211
+ exports.REJECT_CLASS = REJECT_CLASS;
8604
9212
  exports.UtilityCache = UtilityCache;
8605
9213
  exports.WeakCache = WeakCache;
8606
9214
  exports.arbitraryPropertyRegistration = arbitraryPropertyRegistration;