@barocss/kit 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -219,6 +219,15 @@ export declare type AstNode = {
219
219
  * clear caches that belong to another context.
220
220
  */
221
221
  clearCacheOnContextChange?: boolean;
222
+ /**
223
+ * #287: static custom utilities, the runtime mirror of a stylesheet's static `@utility name { ... }`.
224
+ * Name → declarations (property → value; CSS custom properties allowed). Each one is registered on
225
+ * this context only; variants and `!` apply as usual. A name equal to a built-in extends it like
226
+ * `@utility` in Tailwind 4: the built-in declarations are emitted first, then the custom ones (a later
227
+ * duplicate property wins). An entry with an invalid name, property or value is skipped whole.
228
+ * @example utilities: { 'max-w-app': { 'max-width': '72rem', 'margin-inline': 'auto' } }
229
+ */
230
+ utilities?: CustomUtilities;
222
231
  [key: string]: unknown;
223
232
  }
224
233
 
@@ -237,6 +246,12 @@ export declare type AstNode = {
237
246
 
238
247
  export declare function createContext(configObj: Config): Context;
239
248
 
249
+ /** #287: static custom utilities by class name. */
250
+ export declare type CustomUtilities = Record<string, CustomUtilityDeclarations>;
251
+
252
+ /** #287: declarations of one static custom utility (property → value). */
253
+ export declare type CustomUtilityDeclarations = Record<string, string | number>;
254
+
240
255
  export declare function decl(prop: string, value: string | [string, string][], source?: string): AstNode;
241
256
 
242
257
  export declare type DeclPath = PathNode[];
package/dist/index.js CHANGED
@@ -555,14 +555,23 @@ function parseClassName(className, ctx) {
555
555
  if (cache.has(className)) {
556
556
  return cache.get(className);
557
557
  }
558
- let important = false;
559
558
  let realClassName = className;
560
- if (className.startsWith("!")) {
559
+ const classPrefix = ctx ? configuredClassPrefix(ctx) : "";
560
+ if (classPrefix) {
561
+ if (!className.startsWith(classPrefix + ":")) {
562
+ const none = { modifiers: [], utility: null };
563
+ cache.set(className, none);
564
+ return none;
565
+ }
566
+ realClassName = className.slice(classPrefix.length + 1);
567
+ }
568
+ let important = false;
569
+ if (realClassName.startsWith("!")) {
561
570
  important = true;
562
- realClassName = className.slice(1);
563
- } else if (className.length > 1 && className.endsWith("!")) {
571
+ realClassName = realClassName.slice(1);
572
+ } else if (realClassName.length > 1 && realClassName.endsWith("!")) {
564
573
  important = true;
565
- realClassName = className.slice(0, -1);
574
+ realClassName = realClassName.slice(0, -1);
566
575
  }
567
576
  const tokens = tokenize(realClassName);
568
577
  const result = parseTokens(tokens, ctx);
@@ -572,6 +581,10 @@ function parseClassName(className, ctx) {
572
581
  cache.set(className, result);
573
582
  return result;
574
583
  }
584
+ function configuredClassPrefix(ctx) {
585
+ const configured = ctx.config("prefix");
586
+ return typeof configured === "string" && /^[a-z]+$/.test(configured) ? configured : "";
587
+ }
575
588
  function parseTokens(tokens, ctx) {
576
589
  const modifiers = [];
577
590
  let utility = null;
@@ -1095,24 +1108,41 @@ function animationToCssVars(animations) {
1095
1108
  }
1096
1109
  return result;
1097
1110
  }
1098
- function keyframesToCss(keyframes) {
1099
- if (!keyframes) return "";
1100
- let css = "";
1101
- for (const name in keyframes) {
1102
- const frames = keyframes[name];
1103
- css += `@keyframes ${name} {
1111
+ const COMMENT_OR_BLOCK = /\/\*|\*\/|[{};]/;
1112
+ function keyframesBlock(name, frames) {
1113
+ if (!name || COMMENT_OR_BLOCK.test(name) || /\s/.test(name) || !frames || typeof frames !== "object") return "";
1114
+ let body = "";
1115
+ for (const [step, props] of Object.entries(frames)) {
1116
+ if (COMMENT_OR_BLOCK.test(step) || !props || typeof props !== "object") return "";
1117
+ let decls = "";
1118
+ for (const [prop, value] of Object.entries(props)) {
1119
+ const v2 = String(value);
1120
+ if (COMMENT_OR_BLOCK.test(prop) || COMMENT_OR_BLOCK.test(v2)) return "";
1121
+ decls += ` ${prop}: ${v2};
1104
1122
  `;
1105
- for (const step in frames) {
1106
- css += ` ${step} {`;
1107
- const props = frames[step];
1108
- for (const prop in props) {
1109
- css += ` ${prop}: ${props[prop]};`;
1110
- }
1111
- css += " }\n";
1112
1123
  }
1113
- css += "}\n";
1124
+ body += ` ${step} {
1125
+ ${decls} }
1126
+ `;
1127
+ }
1128
+ return `@keyframes ${name} {
1129
+ ${body}}`;
1130
+ }
1131
+ function referencedKeyframes(css, ctx) {
1132
+ if (!css.includes("animation")) return [];
1133
+ const all = ctx.theme("keyframes");
1134
+ if (!all || typeof all !== "object") return [];
1135
+ const names = /* @__PURE__ */ new Set();
1136
+ for (const m of css.matchAll(/(?:^|[\s;{])animation(?:-name)?\s*:\s*([^;}]+)/g)) {
1137
+ const value = m[1].replace(/var\(--animate-([\w-]+)\)/g, (whole, key) => {
1138
+ const v2 = ctx.theme("animations", key) ?? ctx.theme("animation", key);
1139
+ return typeof v2 === "string" ? v2 : whole;
1140
+ });
1141
+ for (const word of value.split(/[\s,()]+/)) {
1142
+ if (word && Object.prototype.hasOwnProperty.call(all, word)) names.add(word);
1143
+ }
1114
1144
  }
1115
- return css;
1145
+ return [...names].map((n) => keyframesBlock(n, all[n])).filter(Boolean);
1116
1146
  }
1117
1147
  function transitionTimingFunctionToCssVars(transition) {
1118
1148
  const result = {};
@@ -1180,7 +1210,7 @@ function themeToCssVarsAll(theme) {
1180
1210
  ...borderRadiusToCssVars(theme.borderRadius),
1181
1211
  ...zIndexToCssVars(theme.zIndex),
1182
1212
  ...opacityToCssVars(theme.opacity),
1183
- ...animationToCssVars(theme.animations),
1213
+ ...animationToCssVars({ ...theme.animations, ...theme.animation }),
1184
1214
  ...transitionTimingFunctionToCssVars(theme.transitionTimingFunction),
1185
1215
  ...transitionDurationToCssVars(theme.transitionDuration),
1186
1216
  ...transitionDelayToCssVars(theme.transitionDelay),
@@ -1550,7 +1580,11 @@ function generateCss(classList, ctx, opts) {
1550
1580
  }
1551
1581
  return result;
1552
1582
  }).join(opts?.minify ? "" : "\n");
1553
- const rootRules = [...new Set(allAtRootNodes.filter((node) => node.type === "at-rule").map((node) => rootToCss([node], { minify: opts?.minify })))];
1583
+ const rootRules = [.../* @__PURE__ */ new Set([
1584
+ ...allAtRootNodes.filter((node) => node.type === "at-rule").map((node) => rootToCss([node], { minify: opts?.minify })),
1585
+ // #274: the @keyframes the class rules reference, once per sheet.
1586
+ ...referencedKeyframes(results, ctx)
1587
+ ])];
1554
1588
  const rootDeclarations = [...new Set(allAtRootNodes.filter((node) => node.type === "decl").map((node) => rootToCss([node], { minify: opts?.minify })).filter((decl2) => decl2 !== ""))];
1555
1589
  const rootCss = [
1556
1590
  ...rootRules,
@@ -1608,6 +1642,7 @@ function generateCssRules(classList, ctx, opts) {
1608
1642
  const css = rootToCss([node]);
1609
1643
  rootCssList.push(css);
1610
1644
  }
1645
+ rootCssList.push(...referencedKeyframes(cssList.join("\n"), ctx));
1611
1646
  return {
1612
1647
  cls,
1613
1648
  ast: allCleanAst,
@@ -1898,6 +1933,48 @@ class IncrementalParser {
1898
1933
  return Array.from(this.processedClasses);
1899
1934
  }
1900
1935
  }
1936
+ const customUtilityName = /^[A-Za-z_][A-Za-z0-9_-]*$/;
1937
+ const customUtilityProp = /^(--[A-Za-z0-9_-]+|-?[A-Za-z][A-Za-z0-9-]*)$/;
1938
+ function validateCustomUtility(name, decls) {
1939
+ if (typeof name !== "string" || !customUtilityName.test(name)) return null;
1940
+ if (!decls || typeof decls !== "object" || Array.isArray(decls)) return null;
1941
+ const out = [];
1942
+ for (const [prop, raw2] of Object.entries(decls)) {
1943
+ if (typeof raw2 !== "string" && typeof raw2 !== "number") return null;
1944
+ const value = String(raw2).trim();
1945
+ if (!customUtilityProp.test(prop) || !value || !isStructureSafeValue(value) || hasCommentDelimiter(value)) return null;
1946
+ out.push([prop, value]);
1947
+ }
1948
+ return out.length ? out : null;
1949
+ }
1950
+ function registerCustomUtilities(ctx, utilities) {
1951
+ if (!utilities || typeof utilities !== "object" || Array.isArray(utilities)) return;
1952
+ const list = getUtility(ctx);
1953
+ const builtins = [...list];
1954
+ const before = list.length;
1955
+ for (const [name, decls] of Object.entries(utilities)) {
1956
+ const safe = validateCustomUtility(name, decls);
1957
+ if (!safe) {
1958
+ debugWarn(`[BAROCSS] Ignoring invalid custom utility "${name}"`);
1959
+ continue;
1960
+ }
1961
+ const shadowed = builtins.filter((u) => u.match(name));
1962
+ registerUtility({
1963
+ name,
1964
+ category: "custom",
1965
+ match: (className) => className === name,
1966
+ handler: (value, c, token) => {
1967
+ let base = [];
1968
+ for (const reg of shadowed) {
1969
+ base = reg.handler(value, c, token, reg) || [];
1970
+ if (base.length > 0) break;
1971
+ }
1972
+ return [...base, ...safe.map(([prop, v]) => decl(prop, v))];
1973
+ }
1974
+ }, ctx);
1975
+ }
1976
+ if (list.length > before) list.unshift(...list.splice(before));
1977
+ }
1901
1978
  const preflightMinimalCSS = `
1902
1979
  /* BaroCSS Preflight - Minimal Reset */
1903
1980
  /* ================================= */
@@ -2683,7 +2760,6 @@ function getPreflightCSS(level = true) {
2683
2760
  return "";
2684
2761
  }
2685
2762
  const defaultConfig = {
2686
- prefix: "barocss-",
2687
2763
  darkMode: "media"
2688
2764
  // same as default
2689
2765
  };
@@ -2785,9 +2861,7 @@ function resolveTheme(config) {
2785
2861
  }
2786
2862
  function themeToCssVars(theme) {
2787
2863
  const vars = themeToCssVarsAll(theme);
2788
- const result = toCssVarsBlock(vars, `
2789
- ${keyframesToCss(theme.keyframes || {})}
2790
- `);
2864
+ const result = toCssVarsBlock(vars);
2791
2865
  return result;
2792
2866
  }
2793
2867
  function createContext(configObj) {
@@ -2841,6 +2915,7 @@ function createContext(configObj) {
2841
2915
  }
2842
2916
  };
2843
2917
  initializeContextState(ctx, getUtility(), getModifier());
2918
+ registerCustomUtilities(ctx, configObj.utilities);
2844
2919
  return ctx;
2845
2920
  }
2846
2921
  function jsonToAst(input, ctx) {
@@ -3612,10 +3687,12 @@ staticUtility("animate-bounce", [["animation", "var(--animate-bounce)"]], { cate
3612
3687
  staticUtility("animate-none", [["animation", "none"]], { category: "transitions" });
3613
3688
  functionalUtility({
3614
3689
  name: "animate",
3615
- prop: "animation",
3690
+ // #274: theme.animations (and Tailwind's theme.animation) names, e.g. theme.extend.animation.wiggle.
3691
+ themeKeys: ["animations", "animation"],
3616
3692
  supportsArbitrary: true,
3617
3693
  supportsCustomProperty: true,
3618
- handle: (value, ctx, token) => {
3694
+ handle: (value, ctx, token, extra) => {
3695
+ if (extra?.realThemeValue) return [decl("animation", `var(--animate-${extra.realThemeValue})`)];
3619
3696
  if (token.customProperty) {
3620
3697
  return [decl("animation", `var(${value})`)];
3621
3698
  }