@tenphi/tasty 2.2.0 → 2.3.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.
Files changed (54) hide show
  1. package/README.md +4 -1
  2. package/dist/{collector-LuU1vZ68.d.ts → collector-BQHl-atL.d.ts} +12 -2
  3. package/dist/{collector-MOYY2SOr.js → collector-DROCOiaT.js} +24 -11
  4. package/dist/collector-DROCOiaT.js.map +1 -0
  5. package/dist/{config-vuCRkBWX.d.ts → config-CzzTHmtS.d.ts} +48 -4
  6. package/dist/{config-A237aY9H.js → config-JokB1Lc8.js} +193 -77
  7. package/dist/config-JokB1Lc8.js.map +1 -0
  8. package/dist/core/index.d.ts +5 -5
  9. package/dist/core/index.js +6 -6
  10. package/dist/{core-BkKav78f.js → core-CW4XEUFk.js} +18 -12
  11. package/dist/core-CW4XEUFk.js.map +1 -0
  12. package/dist/{css-writer-Cos9tQRM.js → css-writer-Jv468wSl.js} +28 -6
  13. package/dist/css-writer-Jv468wSl.js.map +1 -0
  14. package/dist/{format-rules-C2oiTsEO.js → format-rules-B0vbh8Qz.js} +2 -2
  15. package/dist/{format-rules-C2oiTsEO.js.map → format-rules-B0vbh8Qz.js.map} +1 -1
  16. package/dist/{hydrate-miFzWIKR.js → hydrate-BO6nlAeD.js} +2 -2
  17. package/dist/{hydrate-miFzWIKR.js.map → hydrate-BO6nlAeD.js.map} +1 -1
  18. package/dist/{index-dUtwpOux.d.ts → index-Dy74C11K.d.ts} +8 -1
  19. package/dist/{index-ZRxZWzlj.d.ts → index-mWACW3QW.d.ts} +32 -6
  20. package/dist/index.d.ts +5 -5
  21. package/dist/index.js +10 -10
  22. package/dist/index.js.map +1 -1
  23. package/dist/{keyframes-DDtNo_hl.js → keyframes-J_JNrpdh.js} +3 -2
  24. package/dist/{keyframes-DDtNo_hl.js.map → keyframes-J_JNrpdh.js.map} +1 -1
  25. package/dist/{merge-styles-CtDJMhpJ.d.ts → merge-styles-BS-mpcci.d.ts} +2 -2
  26. package/dist/{merge-styles-D_HbBOlq.js → merge-styles-Du-eC7zp.js} +2 -2
  27. package/dist/{merge-styles-D_HbBOlq.js.map → merge-styles-Du-eC7zp.js.map} +1 -1
  28. package/dist/{resolve-recipes-B7-823LL.js → resolve-recipes-DPRT3FMM.js} +3 -3
  29. package/dist/{resolve-recipes-B7-823LL.js.map → resolve-recipes-DPRT3FMM.js.map} +1 -1
  30. package/dist/ssr/astro-client.js +1 -1
  31. package/dist/ssr/astro.js +3 -3
  32. package/dist/ssr/astro.js.map +1 -1
  33. package/dist/ssr/index.d.ts +1 -1
  34. package/dist/ssr/index.js +3 -3
  35. package/dist/ssr/next.d.ts +1 -1
  36. package/dist/ssr/next.js +4 -4
  37. package/dist/ssr/next.js.map +1 -1
  38. package/dist/static/index.d.ts +2 -2
  39. package/dist/static/index.js +1 -1
  40. package/dist/static/index.js.map +1 -1
  41. package/dist/zero/babel.d.ts +1 -1
  42. package/dist/zero/babel.js +16 -8
  43. package/dist/zero/babel.js.map +1 -1
  44. package/dist/zero/index.d.ts +1 -1
  45. package/dist/zero/index.js +1 -1
  46. package/docs/configuration.md +44 -0
  47. package/docs/dsl.md +13 -11
  48. package/docs/ssr.md +5 -3
  49. package/docs/tasty-static.md +15 -0
  50. package/package.json +1 -1
  51. package/dist/collector-MOYY2SOr.js.map +0 -1
  52. package/dist/config-A237aY9H.js.map +0 -1
  53. package/dist/core-BkKav78f.js.map +0 -1
  54. package/dist/css-writer-Cos9tQRM.js.map +0 -1
@@ -1374,6 +1374,93 @@ function isDevEnv() {
1374
1374
  return nodeEnv !== "test" && nodeEnv !== "production";
1375
1375
  }
1376
1376
  //#endregion
1377
+ //#region src/utils/name-prefix.ts
1378
+ /**
1379
+ * Name prefix utilities for generated identifiers.
1380
+ *
1381
+ * Tasty generates three kinds of identifiers from content hashes:
1382
+ * - class names (used in DOM `class` attribute)
1383
+ * - keyframe names (used in CSS `animation`)
1384
+ * - counter-style names (used in CSS `list-style-type`)
1385
+ *
1386
+ * All three derive from a single configurable prefix so that an app
1387
+ * can namespace every identifier under one string. Discriminator letters
1388
+ * (`k`, `c`) keep the three kinds visually distinct in devtools — they
1389
+ * are not required for correctness (CSS keeps these in separate
1390
+ * namespaces), only for readability.
1391
+ *
1392
+ * The runtime / SSR / RSC paths must agree on the prefix; otherwise the
1393
+ * client-side hash for a given style will not match the server-rendered
1394
+ * class and hydration breaks. The zero-runtime build path uses a
1395
+ * different default (`'ts'`) so its classes can't collide with runtime
1396
+ * (`'t'`) classes when both are loaded on the same page.
1397
+ */
1398
+ /** Default prefix used by the runtime / SSR / RSC paths. */
1399
+ const DEFAULT_NAME_PREFIX = "t";
1400
+ /** Default prefix used by the zero-runtime (`tastyStatic`) build path. */
1401
+ const DEFAULT_ZERO_NAME_PREFIX = "ts";
1402
+ /**
1403
+ * Allowed shape: starts with a letter or underscore, then letters/
1404
+ * digits/underscore/hyphen. Length capped at 32 to keep generated
1405
+ * names sane. Matches the CSS identifier rules for the common case
1406
+ * while keeping the surface conservative.
1407
+ */
1408
+ const NAME_PREFIX_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$/;
1409
+ /**
1410
+ * Validate a `namePrefix` value.
1411
+ * Throws a TypeError with a descriptive message on invalid input so
1412
+ * misconfiguration fails loudly at `configure()` time rather than
1413
+ * surfacing later as broken hydration.
1414
+ */
1415
+ function validateNamePrefix(prefix) {
1416
+ if (typeof prefix !== "string") throw new TypeError(`[Tasty] namePrefix must be a string, got ${typeof prefix}.`);
1417
+ if (!NAME_PREFIX_PATTERN.test(prefix)) throw new TypeError(`[Tasty] namePrefix "${prefix}" is invalid. It must start with a letter (a-z, A-Z) or "_", contain only letters, digits, "_" or "-", and be 1-32 characters long. Examples: "t", "ts", "myapp-", "_foo".`);
1418
+ }
1419
+ /**
1420
+ * Build a class name: `${prefix}${hash}`.
1421
+ * The hash is appended verbatim — supply a separator inside the prefix
1422
+ * itself if you want one (e.g. `'myapp-'`).
1423
+ */
1424
+ function makeClassName(prefix, hash) {
1425
+ return `${prefix}${hash}`;
1426
+ }
1427
+ /**
1428
+ * Build a keyframe name: `${prefix}k${suffix}`.
1429
+ * The `k` discriminator keeps keyframe names visually distinct from
1430
+ * class names sharing the same prefix. `suffix` is typically a content
1431
+ * hash but may be a counter for ad-hoc allocation.
1432
+ */
1433
+ function makeKeyframeName(prefix, suffix) {
1434
+ return `${prefix}k${suffix}`;
1435
+ }
1436
+ /**
1437
+ * Build a counter-style name: `${prefix}c${suffix}`.
1438
+ * The `c` discriminator keeps counter-style names visually distinct
1439
+ * from class names sharing the same prefix.
1440
+ */
1441
+ function makeCounterStyleName(prefix, suffix) {
1442
+ return `${prefix}c${suffix}`;
1443
+ }
1444
+ /** Escape a string for safe inclusion in a regex literal. */
1445
+ function escapeRegex(str) {
1446
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1447
+ }
1448
+ /**
1449
+ * Regex matching any tasty class for the given prefix.
1450
+ * Used by the runtime GC's DOM scan and class-allocation bookkeeping.
1451
+ */
1452
+ function tastyClassRegex(prefix) {
1453
+ return new RegExp(`^${escapeRegex(prefix)}[a-z0-9]+$`);
1454
+ }
1455
+ /**
1456
+ * Global regex extracting tasty class names from RSC-inlined CSS.
1457
+ * Looks for the doubled-specificity pattern `.cls.cls` that
1458
+ * `formatRules()` always emits, which makes extraction reliable.
1459
+ */
1460
+ function rscClassRegexGlobal(prefix) {
1461
+ return new RegExp(`\\.(${escapeRegex(prefix)}[a-z0-9]+)\\.\\1`, "g");
1462
+ }
1463
+ //#endregion
1377
1464
  //#region src/parser/types.ts
1378
1465
  let Bucket = /* @__PURE__ */ function(Bucket) {
1379
1466
  Bucket[Bucket["Color"] = 0] = "Color";
@@ -1439,7 +1526,7 @@ function resolveUntilStable(value, opts, recurse, maxIterations = 10) {
1439
1526
  function classify(raw, opts, recurse) {
1440
1527
  const token = raw.trim();
1441
1528
  if (!token) return {
1442
- bucket: Bucket.Mod,
1529
+ bucket: 2,
1443
1530
  processed: ""
1444
1531
  };
1445
1532
  {
@@ -1461,31 +1548,31 @@ function classify(raw, opts, recurse) {
1461
1548
  if (depth !== 0) {
1462
1549
  console.warn("tasty: skipped invalid function token with unmatched parentheses:", token);
1463
1550
  return {
1464
- bucket: Bucket.Mod,
1551
+ bucket: 2,
1465
1552
  processed: ""
1466
1553
  };
1467
1554
  }
1468
1555
  }
1469
1556
  if (token.startsWith("\"") && token.endsWith("\"") || token.startsWith("'") && token.endsWith("'")) return {
1470
- bucket: Bucket.Value,
1557
+ bucket: 1,
1471
1558
  processed: token
1472
1559
  };
1473
1560
  if (token.startsWith("$$")) {
1474
1561
  const name = token.slice(2);
1475
1562
  if (/^[a-z_][a-z0-9-_]*$/i.test(name)) return {
1476
- bucket: Bucket.Value,
1563
+ bucket: 1,
1477
1564
  processed: `--${name}`
1478
1565
  };
1479
1566
  }
1480
1567
  if (token.startsWith("##")) {
1481
1568
  const name = token.slice(2);
1482
1569
  if (/^[a-z_][a-z0-9-_]*$/i.test(name)) return {
1483
- bucket: Bucket.Value,
1570
+ bucket: 1,
1484
1571
  processed: `--${name}-color`
1485
1572
  };
1486
1573
  }
1487
1574
  if (token === "#current") return {
1488
- bucket: Bucket.Color,
1575
+ bucket: 0,
1489
1576
  processed: "currentcolor"
1490
1577
  };
1491
1578
  const currentAlphaMatch = token.match(/^#current\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
@@ -1496,7 +1583,7 @@ function classify(raw, opts, recurse) {
1496
1583
  else if (rawAlpha === "0") percentage = "0%";
1497
1584
  else percentage = `${parseFloat("." + rawAlpha) * 100}%`;
1498
1585
  return {
1499
- bucket: Bucket.Color,
1586
+ bucket: 0,
1500
1587
  processed: `color-mix(in oklab, currentcolor ${percentage}, transparent)`
1501
1588
  };
1502
1589
  }
@@ -1569,7 +1656,7 @@ function classify(raw, opts, recurse) {
1569
1656
  const constructed = `${normalizedFunc}(${hasModernAlpha || hasLegacyAlpha ? normalizeArgs(hasModernAlpha ? args.slice(0, slashIdx).trim() : args.slice(0, lastTopLevelComma).trim()) : normalizeArgs(args)} / ${alpha})`;
1570
1657
  if (!COLOR_FUNCS.has(normalizedFunc) && opts.funcs && normalizedFunc in opts.funcs) return classify(constructed, opts, recurse);
1571
1658
  return {
1572
- bucket: Bucket.Color,
1659
+ bucket: 0,
1573
1660
  processed: constructed
1574
1661
  };
1575
1662
  }
@@ -1580,11 +1667,11 @@ function classify(raw, opts, recurse) {
1580
1667
  }
1581
1668
  }
1582
1669
  if (token.match(/^var\(--([a-z0-9-]+)-color\)$/)) return {
1583
- bucket: Bucket.Color,
1670
+ bucket: 0,
1584
1671
  processed: token
1585
1672
  };
1586
1673
  if (token.startsWith("url(")) return {
1587
- bucket: Bucket.Value,
1674
+ bucket: 1,
1588
1675
  processed: token
1589
1676
  };
1590
1677
  if (token[0] === "$") {
@@ -1593,7 +1680,7 @@ function classify(raw, opts, recurse) {
1593
1680
  const name = identMatch[1];
1594
1681
  const processed = `var(--${name})`;
1595
1682
  return {
1596
- bucket: name.endsWith("-color") ? Bucket.Color : Bucket.Value,
1683
+ bucket: name.endsWith("-color") ? 0 : 1,
1597
1684
  processed
1598
1685
  };
1599
1686
  }
@@ -1607,17 +1694,17 @@ function classify(raw, opts, recurse) {
1607
1694
  else if (rawAlpha === "0") alpha = "0";
1608
1695
  else alpha = `.${rawAlpha}`;
1609
1696
  return {
1610
- bucket: Bucket.Color,
1697
+ bucket: 0,
1611
1698
  processed: `${getColorSpaceFunc()}(var(--${base}-color-${getColorSpaceSuffix()}) / ${alpha})`
1612
1699
  };
1613
1700
  }
1614
1701
  const name = token.slice(1);
1615
1702
  if (RE_HEX.test(name)) return {
1616
- bucket: Bucket.Color,
1703
+ bucket: 0,
1617
1704
  processed: `var(--${name}-color, #${name})`
1618
1705
  };
1619
1706
  return {
1620
- bucket: Bucket.Color,
1707
+ bucket: 0,
1621
1708
  processed: `var(--${name}-color)`
1622
1709
  };
1623
1710
  }
@@ -1628,7 +1715,7 @@ function classify(raw, opts, recurse) {
1628
1715
  if (COLOR_FUNCS.has(fname)) {
1629
1716
  const argProcessed = recurse(inner).output.replace(/,\s+/g, ",");
1630
1717
  return {
1631
- bucket: Bucket.Color,
1718
+ bucket: 0,
1632
1719
  processed: `${canonicalFuncName(fname)}(${argProcessed})`
1633
1720
  };
1634
1721
  }
@@ -1641,7 +1728,7 @@ function classify(raw, opts, recurse) {
1641
1728
  }
1642
1729
  const argProcessed = recurse(inner).output;
1643
1730
  return {
1644
- bucket: Bucket.Value,
1731
+ bucket: 1,
1645
1732
  processed: `${canonicalFuncName(fname)}(${argProcessed})`
1646
1733
  };
1647
1734
  }
@@ -1649,10 +1736,9 @@ function classify(raw, opts, recurse) {
1649
1736
  const colorMatch = token.slice(1, -1).match(/^#([a-z0-9-]+)\s*,\s*(.*)$/i);
1650
1737
  if (colorMatch) {
1651
1738
  const [, name, fallback] = colorMatch;
1652
- const processedFallback = recurse(fallback).output;
1653
1739
  return {
1654
- bucket: Bucket.Color,
1655
- processed: `var(--${name}-color, ${processedFallback})`
1740
+ bucket: 0,
1741
+ processed: `var(--${name}-color, ${recurse(fallback).output})`
1656
1742
  };
1657
1743
  }
1658
1744
  }
@@ -1662,18 +1748,15 @@ function classify(raw, opts, recurse) {
1662
1748
  const [, name, fallback] = match;
1663
1749
  const processedFallback = recurse(fallback).output;
1664
1750
  return {
1665
- bucket: name.endsWith("-color") ? Bucket.Color : Bucket.Value,
1751
+ bucket: name.endsWith("-color") ? 0 : 1,
1666
1752
  processed: `var(--${name}, ${processedFallback})`
1667
1753
  };
1668
1754
  }
1669
1755
  }
1670
- if (token[0] === "(" && token[token.length - 1] === ")") {
1671
- const innerProcessed = recurse(token.slice(1, -1)).output;
1672
- return {
1673
- bucket: Bucket.Value,
1674
- processed: `calc(${innerProcessed})`
1675
- };
1676
- }
1756
+ if (token[0] === "(" && token[token.length - 1] === ")") return {
1757
+ bucket: 1,
1758
+ processed: `calc(${recurse(token.slice(1, -1)).output})`
1759
+ };
1677
1760
  const um = token.match(RE_UNIT_NUM);
1678
1761
  if (um) {
1679
1762
  const unit = um[1];
@@ -1683,47 +1766,43 @@ function classify(raw, opts, recurse) {
1683
1766
  const rawMatch = handler.match(RE_RAW_UNIT);
1684
1767
  if (rawMatch) {
1685
1768
  const [, baseNum, cssUnit] = rawMatch;
1686
- const resolved = resolveUntilStable(`${numericPart * parseFloat(baseNum)}${cssUnit}`, opts, recurse);
1687
1769
  return {
1688
- bucket: Bucket.Value,
1689
- processed: resolved
1770
+ bucket: 1,
1771
+ processed: resolveUntilStable(`${numericPart * parseFloat(baseNum)}${cssUnit}`, opts, recurse)
1690
1772
  };
1691
1773
  }
1692
1774
  const base = handler;
1693
1775
  if (numericPart === 1) return {
1694
- bucket: Bucket.Value,
1776
+ bucket: 1,
1695
1777
  processed: base
1696
1778
  };
1697
1779
  return {
1698
- bucket: Bucket.Value,
1780
+ bucket: 1,
1699
1781
  processed: `calc(${numericPart} * ${base})`
1700
1782
  };
1701
- } else {
1702
- const inner = handler(numericPart);
1703
- return {
1704
- bucket: Bucket.Value,
1705
- processed: inner
1706
- };
1707
- }
1783
+ } else return {
1784
+ bucket: 1,
1785
+ processed: handler(numericPart)
1786
+ };
1708
1787
  }
1709
1788
  if (/^[+-]?(?:\d*\.\d+|\d+)[a-z%]+$/.test(token)) return {
1710
- bucket: Bucket.Value,
1789
+ bucket: 1,
1711
1790
  processed: token
1712
1791
  };
1713
1792
  if (RE_NUMBER.test(token)) return {
1714
- bucket: Bucket.Value,
1793
+ bucket: 1,
1715
1794
  processed: token
1716
1795
  };
1717
1796
  if (VALUE_KEYWORDS.has(token)) return {
1718
- bucket: Bucket.Value,
1797
+ bucket: 1,
1719
1798
  processed: token
1720
1799
  };
1721
1800
  if (token === "transparent" || token === "currentcolor") return {
1722
- bucket: Bucket.Color,
1801
+ bucket: 0,
1723
1802
  processed: token
1724
1803
  };
1725
1804
  return {
1726
- bucket: Bucket.Mod,
1805
+ bucket: 2,
1727
1806
  processed: token
1728
1807
  };
1729
1808
  }
@@ -1823,13 +1902,13 @@ var StyleParser = class {
1823
1902
  return;
1824
1903
  }
1825
1904
  switch (bucket) {
1826
- case Bucket.Color:
1905
+ case 0:
1827
1906
  currentPart.colors.push(processed);
1828
1907
  break;
1829
- case Bucket.Value:
1908
+ case 1:
1830
1909
  currentPart.values.push(processed);
1831
1910
  break;
1832
- case Bucket.Mod:
1911
+ case 2:
1833
1912
  currentPart.mods.push(processed);
1834
1913
  break;
1835
1914
  }
@@ -4809,18 +4888,10 @@ function formatCounterStyleRule(name, descriptors) {
4809
4888
  //#endregion
4810
4889
  //#region src/injector/injector.ts
4811
4890
  /**
4812
- * Generate a deterministic class name from a cache key using content hash.
4813
- * The same cache key always produces the same class name across environments.
4814
- */
4815
- function generateClassName(cacheKey) {
4816
- return `t${hashString(cacheKey)}`;
4817
- }
4818
- const RSC_CLASS_RE = /\.(t[a-z0-9]+)\.\1/g;
4819
- /**
4820
4891
  * Extract class names from `<style data-tasty-rsc>` tags.
4821
4892
  * The doubled-specificity pattern `.tXXX.tXXX` makes extraction reliable.
4822
4893
  */
4823
- function extractRSCClassNames() {
4894
+ function extractRSCClassNames(rscClassRegex) {
4824
4895
  if (typeof document === "undefined") return [];
4825
4896
  const styles = document.querySelectorAll("style[data-tasty-rsc]");
4826
4897
  if (styles.length === 0) return [];
@@ -4829,8 +4900,8 @@ function extractRSCClassNames() {
4829
4900
  const text = style.textContent;
4830
4901
  if (!text) continue;
4831
4902
  let match;
4832
- RSC_CLASS_RE.lastIndex = 0;
4833
- while ((match = RSC_CLASS_RE.exec(text)) !== null) classSet.add(match[1]);
4903
+ rscClassRegex.lastIndex = 0;
4904
+ while ((match = rscClassRegex.exec(text)) !== null) classSet.add(match[1]);
4834
4905
  }
4835
4906
  return Array.from(classSet);
4836
4907
  }
@@ -4844,7 +4915,7 @@ function extractRSCClassNames() {
4844
4915
  * Called inside `inject()` / `allocateClassName()` to pick up
4845
4916
  * class names rendered on the server (including during SPA navigation).
4846
4917
  */
4847
- function syncServerClasses(registry) {
4918
+ function syncServerClasses(registry, rscClassRegex) {
4848
4919
  if (typeof window === "undefined") return;
4849
4920
  const classes = window.__TASTY__;
4850
4921
  if (classes && classes.length > registry.serverClassSyncIndex) {
@@ -4853,7 +4924,7 @@ function syncServerClasses(registry) {
4853
4924
  }
4854
4925
  if (!registry.rscStylesScanned) {
4855
4926
  registry.rscStylesScanned = true;
4856
- for (const cls of extractRSCClassNames()) registerHydratedClass(registry, cls);
4927
+ for (const cls of extractRSCClassNames(rscClassRegex)) registerHydratedClass(registry, cls);
4857
4928
  }
4858
4929
  }
4859
4930
  function registerHydratedClass(registry, className) {
@@ -4865,25 +4936,40 @@ function registerHydratedClass(registry, className) {
4865
4936
  });
4866
4937
  registry.refCounts.set(className, 0);
4867
4938
  }
4868
- var StyleInjector = class StyleInjector {
4939
+ var StyleInjector = class {
4869
4940
  sheetManager;
4870
4941
  config;
4871
4942
  globalRuleCounter = 0;
4872
4943
  pendingGCHandle = null;
4944
+ namePrefix;
4945
+ classRegex;
4946
+ rscClassRegex;
4873
4947
  /** @internal — exposed for debug utilities only */
4874
4948
  get _sheetManager() {
4875
4949
  return this.sheetManager;
4876
4950
  }
4877
4951
  constructor(config = {}) {
4952
+ if (config.namePrefix !== void 0) validateNamePrefix(config.namePrefix);
4878
4953
  this.config = config;
4879
4954
  this.sheetManager = new SheetManager(config);
4955
+ this.namePrefix = config.namePrefix ?? "t";
4956
+ this.classRegex = tastyClassRegex(this.namePrefix);
4957
+ this.rscClassRegex = rscClassRegexGlobal(this.namePrefix);
4958
+ }
4959
+ /**
4960
+ * Generate a deterministic class name from a cache key using content hash.
4961
+ * The same cache key always produces the same class name across environments
4962
+ * with the same `namePrefix`.
4963
+ */
4964
+ generateClassName(cacheKey) {
4965
+ return makeClassName(this.namePrefix, hashString(cacheKey));
4880
4966
  }
4881
4967
  /**
4882
4968
  * Check if `className` was hydrated from server-rendered styles and,
4883
4969
  * if so, wire the cacheKey mapping. Returns true on hit.
4884
4970
  */
4885
4971
  tryHydratedHit(registry, cacheKey, className) {
4886
- syncServerClasses(registry);
4972
+ syncServerClasses(registry, this.rscClassRegex);
4887
4973
  const rule = registry.rules.get(className);
4888
4974
  if (rule && rule.ruleIndex === -2 && rule.sheetIndex === -2) {
4889
4975
  registry.cacheKeyToClassName.set(cacheKey, className);
@@ -4902,7 +4988,7 @@ var StyleInjector = class StyleInjector {
4902
4988
  className: registry.cacheKeyToClassName.get(cacheKey),
4903
4989
  isNewAllocation: false
4904
4990
  };
4905
- const className = generateClassName(cacheKey);
4991
+ const className = this.generateClassName(cacheKey);
4906
4992
  if (this.tryHydratedHit(registry, cacheKey, className)) return {
4907
4993
  className,
4908
4994
  isNewAllocation: false
@@ -4954,7 +5040,7 @@ var StyleInjector = class StyleInjector {
4954
5040
  };
4955
5041
  }
4956
5042
  } else if (cacheKey) {
4957
- className = generateClassName(cacheKey);
5043
+ className = this.generateClassName(cacheKey);
4958
5044
  if (this.tryHydratedHit(registry, cacheKey, className)) {
4959
5045
  registry.refCounts.set(className, (registry.refCounts.get(className) || 0) + 1);
4960
5046
  if (registry.metrics) registry.metrics.hits++;
@@ -4963,7 +5049,10 @@ var StyleInjector = class StyleInjector {
4963
5049
  dispose: () => this.dispose(className, registry)
4964
5050
  };
4965
5051
  }
4966
- } else className = `t${hashString(rules.map((r) => `${r.selector}\0${r.declarations}`).join("\n"))}`;
5052
+ } else {
5053
+ const parts = rules.map((r) => `${r.selector}\0${r.declarations}`);
5054
+ className = makeClassName(this.namePrefix, hashString(parts.join("\n")));
5055
+ }
4967
5056
  const rulesToInsert = rules.map((rule) => {
4968
5057
  let newSelector = rule.selector;
4969
5058
  if (rule.needsClassName) {
@@ -5181,8 +5270,23 @@ var StyleInjector = class StyleInjector {
5181
5270
  }
5182
5271
  const cssName = effectiveResult.cssName;
5183
5272
  const definition = effectiveResult.definition;
5184
- const normalizedDef = normalizePropertyDefinition(definition);
5185
- if (registry.injectedProperties.get(cssName) !== void 0) return;
5273
+ this.insertPropertyRule(registry, root, cssName, definition, name);
5274
+ if (effectiveResult.isColor) {
5275
+ const companionCssName = `${cssName}-${getColorSpaceSuffix()}`;
5276
+ const companionDefinition = {
5277
+ syntax: getComponentPropertySyntax(),
5278
+ inherits: definition.inherits,
5279
+ initialValue: colorInitialValueToComponents(definition.initialValue)
5280
+ };
5281
+ this.insertPropertyRule(registry, root, companionCssName, companionDefinition, `${name}:components`);
5282
+ }
5283
+ }
5284
+ /**
5285
+ * Build and insert a single `@property` rule into the given registry.
5286
+ * No-op if the property was already injected.
5287
+ */
5288
+ insertPropertyRule(registry, root, cssName, definition, cacheKey) {
5289
+ if (registry.injectedProperties.has(cssName)) return;
5186
5290
  const parts = [];
5187
5291
  if (definition.syntax != null) {
5188
5292
  let syntax = String(definition.syntax).trim();
@@ -5202,8 +5306,8 @@ var StyleInjector = class StyleInjector {
5202
5306
  selector: `@property ${cssName}`,
5203
5307
  declarations
5204
5308
  };
5205
- if (!this.sheetManager.insertGlobalRule(registry, [rule], `property:${name}`, root)) return;
5206
- registry.injectedProperties.set(cssName, normalizedDef);
5309
+ if (!this.sheetManager.insertGlobalRule(registry, [rule], `property:${cacheKey}`, root)) return;
5310
+ registry.injectedProperties.set(cssName, normalizePropertyDefinition(definition));
5207
5311
  }
5208
5312
  /**
5209
5313
  * Check whether a given @property name was already injected by this injector.
@@ -5283,12 +5387,12 @@ var StyleInjector = class StyleInjector {
5283
5387
  let actualName;
5284
5388
  if (providedName) {
5285
5389
  const existingContentForName = registry.keyframesNameToContent.get(providedName);
5286
- if (existingContentForName && existingContentForName !== contentHash) actualName = `${providedName}-k${registry.keyframesCounter++}`;
5390
+ if (existingContentForName && existingContentForName !== contentHash) actualName = `${providedName}-${makeKeyframeName(this.namePrefix, String(registry.keyframesCounter++))}`;
5287
5391
  else {
5288
5392
  actualName = providedName;
5289
5393
  registry.keyframesNameToContent.set(providedName, contentHash);
5290
5394
  }
5291
- } else actualName = `k${registry.keyframesCounter++}`;
5395
+ } else actualName = makeKeyframeName(this.namePrefix, String(registry.keyframesCounter++));
5292
5396
  const result = this.sheetManager.insertKeyframes(registry, steps, actualName, root);
5293
5397
  if (!result) return {
5294
5398
  toString: () => "",
@@ -5337,7 +5441,6 @@ var StyleInjector = class StyleInjector {
5337
5441
  }
5338
5442
  }
5339
5443
  }
5340
- static TASTY_CLASS_RE = /^t[a-z0-9]+$/;
5341
5444
  /**
5342
5445
  * Record a render-time usage hit for one or more classNames.
5343
5446
  * Handles space-separated multi-chunk classNames.
@@ -5353,7 +5456,7 @@ var StyleInjector = class StyleInjector {
5353
5456
  const now = Date.now();
5354
5457
  const parts = className.indexOf(" ") === -1 ? [className] : className.split(" ");
5355
5458
  for (const cls of parts) {
5356
- if (!StyleInjector.TASTY_CLASS_RE.test(cls)) continue;
5459
+ if (!this.classRegex.test(cls)) continue;
5357
5460
  if (!registry.rules.has(cls)) continue;
5358
5461
  const entry = registry.usageMap.get(cls);
5359
5462
  if (entry) entry.lastTouchedAt = now;
@@ -5406,7 +5509,7 @@ var StyleInjector = class StyleInjector {
5406
5509
  if (registry.usageMap.size - activeCount <= capacity) return 0;
5407
5510
  }
5408
5511
  const liveClasses = /* @__PURE__ */ new Set();
5409
- for (const el of root.querySelectorAll("[class]")) for (const token of el.classList) if (StyleInjector.TASTY_CLASS_RE.test(token)) liveClasses.add(token);
5512
+ for (const el of root.querySelectorAll("[class]")) for (const token of el.classList) if (this.classRegex.test(token)) liveClasses.add(token);
5410
5513
  let swept = 0;
5411
5514
  if (force) for (const [className] of registry.usageMap) {
5412
5515
  if (liveClasses.has(className)) continue;
@@ -9804,7 +9907,8 @@ function createDefaultConfig(isTest) {
9804
9907
  return {
9805
9908
  maxRulesPerSheet: 8192,
9806
9909
  forceTextInjection: isTest ?? false,
9807
- devMode: isDevEnv()
9910
+ devMode: isDevEnv(),
9911
+ namePrefix: "t"
9808
9912
  };
9809
9913
  }
9810
9914
  /**
@@ -10046,6 +10150,7 @@ function configure(config = {}) {
10046
10150
  warnOnce("configure-after-styles", "[Tasty] Cannot call configure() after styles have been generated.\nConfiguration must be done before the first render. The configuration will be ignored.");
10047
10151
  return;
10048
10152
  }
10153
+ if (config.namePrefix !== void 0) validateNamePrefix(config.namePrefix);
10049
10154
  let mergedStates = {};
10050
10155
  let mergedUnits = {};
10051
10156
  let mergedFuncs = {};
@@ -10197,6 +10302,17 @@ function getConfig() {
10197
10302
  return currentConfig;
10198
10303
  }
10199
10304
  /**
10305
+ * Get the configured prefix used for every generated identifier
10306
+ * (class names, keyframe names, counter-style names).
10307
+ *
10308
+ * Falls back to the default prefix (`'t'`) when `configure()` has not
10309
+ * been called yet — this matches the auto-configuration behavior used
10310
+ * by the rest of the system.
10311
+ */
10312
+ function getNamePrefix() {
10313
+ return currentConfig?.namePrefix ?? "t";
10314
+ }
10315
+ /**
10200
10316
  * Get the global injector instance.
10201
10317
  * Auto-configures with defaults if not already configured.
10202
10318
  */
@@ -10230,6 +10346,6 @@ function resetConfig() {
10230
10346
  delete storage[GLOBAL_INJECTOR_KEY];
10231
10347
  }
10232
10348
  //#endregion
10233
- export { parseStyle as $, extractLocalCounterStyle as A, deprecationWarning as B, camelToKebab as C, Lru as Ct, getGlobalPredefinedStates as D, extractPredefinedStateRefs as E, formatFontFaceRule as F, DIRECTIONS as G, createStyle as H, hasLocalFontFace as I, getGlobalFuncs as J, customFunc as K, SheetManager as L, hasLocalCounterStyle as M, extractLocalFontFace as N, setGlobalPredefinedStates as O, fontFaceContentHash as P, parseColor as Q, STYLE_HANDLER_MAP as R, parseStateKey as S, strToRgb as St, extractLocalPredefinedStates as T, PropertyTypeResolver as U, warn as V, CUSTOM_UNITS as W, getGlobalPredefinedTokens as X, getGlobalParser as Y, normalizeColorTokenValue as Z, resetConfig as _, getComponentPropertySyntax as _t, getGlobalCounterStyle as a, StyleParser as at, isSelector as b, hexToRgb as bt, getGlobalKeyframes as c, hashString as ct, hasGlobalKeyframes as d, hasLocalProperties as dt, resetGlobalPredefinedTokens as et, hasGlobalRecipes as f, parsePropertyToken as ft, markStylesGenerated as g, getColorSpaceSuffix as gt, isTestEnvironment as h, getColorSpaceFunc as ht, getGlobalConfigTokens as i, okhslPlugin as it, formatCounterStyleRule as j, StyleInjector as k, getGlobalRecipes as l, extractLocalProperties as lt, isConfigLocked as m, getColorSpaceComponents as mt, getConfig as n, stringifyStyles as nt, getGlobalFontFace as o, Bucket as ot, hasStylesGenerated as p, colorInitialValueToComponents as pt, filterMods as q, getEffectiveProperties as r, okhslFunc as rt, getGlobalInjector as s, isDevEnv as st, configure as t, setGlobalPredefinedTokens as tt, getGlobalStyles as u, getEffectiveDefinition as ut, generateTypographyTokens as v, getNamedColorHex as vt, createStateParserContext as w, renderStyles as x, hslToRgbValues as xt, hasPipelineCacheEntry as y, getRgbValuesFromRgbaString as yt, styleHandlers as z };
10349
+ export { parseColor as $, StyleInjector as A, strToRgb as At, styleHandlers as B, parseStateKey as C, getColorSpaceFunc as Ct, extractPredefinedStateRefs as D, getRgbValuesFromRgbaString as Dt, extractLocalPredefinedStates as E, getNamedColorHex as Et, fontFaceContentHash as F, CUSTOM_UNITS as G, warn as H, formatFontFaceRule as I, filterMods as J, DIRECTIONS as K, hasLocalFontFace as L, formatCounterStyleRule as M, hasLocalCounterStyle as N, getGlobalPredefinedStates as O, hexToRgb as Ot, extractLocalFontFace as P, normalizeColorTokenValue as Q, SheetManager as R, renderStyles as S, getColorSpaceComponents as St, createStateParserContext as T, getComponentPropertySyntax as Tt, createStyle as U, deprecationWarning as V, PropertyTypeResolver as W, getGlobalParser as X, getGlobalFuncs as Y, getGlobalPredefinedTokens as Z, markStylesGenerated as _, extractLocalProperties as _t, getGlobalCounterStyle as a, okhslPlugin as at, hasPipelineCacheEntry as b, parsePropertyToken as bt, getGlobalKeyframes as c, DEFAULT_NAME_PREFIX as ct, getNamePrefix as d, makeCounterStyleName as dt, parseStyle as et, hasGlobalKeyframes as f, makeKeyframeName as ft, isTestEnvironment as g, hashString as gt, isConfigLocked as h, isDevEnv as ht, getGlobalConfigTokens as i, okhslFunc as it, extractLocalCounterStyle as j, Lru as jt, setGlobalPredefinedStates as k, hslToRgbValues as kt, getGlobalRecipes as l, DEFAULT_ZERO_NAME_PREFIX as lt, hasStylesGenerated as m, validateNamePrefix as mt, getConfig as n, setGlobalPredefinedTokens as nt, getGlobalFontFace as o, StyleParser as ot, hasGlobalRecipes as p, tastyClassRegex as pt, customFunc as q, getEffectiveProperties as r, stringifyStyles as rt, getGlobalInjector as s, Bucket as st, configure as t, resetGlobalPredefinedTokens as tt, getGlobalStyles as u, makeClassName as ut, resetConfig as v, getEffectiveDefinition as vt, camelToKebab as w, getColorSpaceSuffix as wt, isSelector as x, colorInitialValueToComponents as xt, generateTypographyTokens as y, hasLocalProperties as yt, STYLE_HANDLER_MAP as z };
10234
10350
 
10235
- //# sourceMappingURL=config-A237aY9H.js.map
10351
+ //# sourceMappingURL=config-JokB1Lc8.js.map