tailwind-to-style 3.2.0 → 3.2.2

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/cx.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tailwind-to-style v3.2.0
2
+ * tailwind-to-style v3.2.2
3
3
  * Runtime Tailwind CSS to inline styles converter
4
4
  *
5
5
  * @author Bigetion
package/dist/cx.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tailwind-to-style v3.2.0
2
+ * tailwind-to-style v3.2.2
3
3
  * Runtime Tailwind CSS to inline styles converter
4
4
  *
5
5
  * @author Bigetion
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tailwind-to-style v3.2.0
2
+ * tailwind-to-style v3.2.2
3
3
  * Runtime Tailwind CSS to inline styles converter
4
4
  *
5
5
  * @author Bigetion
@@ -9614,7 +9614,7 @@ function processClass(cls, selector, styles) {
9614
9614
 
9615
9615
  // Get cssObject from singleton cache
9616
9616
  const cssObject = tailwindCache.getOrGenerate(generateTailwindCssString, convertCssToObject);
9617
- let declarations = cssObject[baseClassName] || cssObject[baseClassName.replace(ESCAPE_SLASH_REGEX, "\\$1")] || cssObject[baseClassName.replace(ESCAPE_DOT_REGEX, "\\.")];
9617
+ let declarations = cssObject[baseClassName] || cssObject[baseClassName.replace(ESCAPE_SLASH_REGEX, "\\/")] || cssObject[baseClassName.replace(ESCAPE_DOT_REGEX, "\\.")];
9618
9618
  if (!declarations && baseClassName.includes("[")) {
9619
9619
  const match = CUSTOM_VALUE_FULL_REGEX.exec(baseClassName);
9620
9620
  if (match) {
@@ -9836,11 +9836,26 @@ function flattenStyleObject(obj) {
9836
9836
  flatArray.push(item);
9837
9837
  } else if (isSelectorObject(item)) {
9838
9838
  const nested = flattenStyleObject(item, currentSelector);
9839
- Object.assign(result, nested);
9839
+ // Merge nested results, handling & that resolves to same selector
9840
+ for (const ns in nested) {
9841
+ if (ns === currentSelector) {
9842
+ // & resolved to same selector — include in this array
9843
+ if (Array.isArray(nested[ns])) {
9844
+ flatArray.push(...nested[ns]);
9845
+ } else {
9846
+ flatArray.push(nested[ns]);
9847
+ }
9848
+ } else {
9849
+ result[ns] = nested[ns];
9850
+ }
9851
+ }
9840
9852
  }
9841
9853
  }
9842
9854
  if (flatArray.length > 0) {
9843
9855
  result[currentSelector] = result[currentSelector] || [];
9856
+ if (!Array.isArray(result[currentSelector])) {
9857
+ result[currentSelector] = [result[currentSelector]];
9858
+ }
9844
9859
  result[currentSelector].push(...flatArray);
9845
9860
  }
9846
9861
  } else if (isSelectorObject(val)) {
@@ -9972,6 +9987,20 @@ function twsxNoCache(obj) {
9972
9987
  // Process each selector within the media query
9973
9988
  for (const innerSelector in val) {
9974
9989
  const innerVal = val[innerSelector];
9990
+
9991
+ // Handle @css string directive inside media queries
9992
+ if (typeof innerVal === "string") {
9993
+ const trimmedInner = innerVal.trim();
9994
+ if (trimmedInner.startsWith('@css')) {
9995
+ const cssMatch = trimmedInner.match(/^@css\s*\{([\s\S]*)\}\s*$/);
9996
+ if (cssMatch) {
9997
+ const rawCss = cssMatch[1].trim();
9998
+ styles[selector][innerSelector] = styles[selector][innerSelector] || '';
9999
+ styles[selector][innerSelector] += rawCss.split(';').filter(d => d.trim()).map(d => d.trim() + ';').join(' ') + '\n';
10000
+ continue;
10001
+ }
10002
+ }
10003
+ }
9975
10004
  const baseClass = typeof innerVal === "string" ? expandGroupedClass(innerVal) : "";
9976
10005
 
9977
10006
  // Process Tailwind classes for this selector
@@ -10011,6 +10040,17 @@ function twsxNoCache(obj) {
10011
10040
  } else if (Array.isArray(val)) {
10012
10041
  for (const item of val) {
10013
10042
  if (typeof item === "string") {
10043
+ // Handle @css strings inside arrays
10044
+ const trimmedItem = item.trim();
10045
+ if (trimmedItem.startsWith('@css')) {
10046
+ const cssMatch = trimmedItem.match(/^@css\s*\{([\s\S]*)\}\s*$/);
10047
+ if (cssMatch) {
10048
+ const rawCss = cssMatch[1].trim();
10049
+ styles[selector] = styles[selector] || '';
10050
+ styles[selector] += rawCss.split(';').filter(d => d.trim()).map(d => d.trim() + ';').join(' ') + '\n';
10051
+ continue;
10052
+ }
10053
+ }
10014
10054
  baseClass += (baseClass ? " " : "") + expandGroupedClass(item);
10015
10055
  } else if (typeof item === "object" && item !== null) {
10016
10056
  Object.assign(nested, item);
@@ -10565,6 +10605,9 @@ function twsxVariantsNoCache(className) {
10565
10605
  // Skip if it's the default value
10566
10606
  if (value === defaultVariants[key]) continue;
10567
10607
 
10608
+ // Skip if the variant value doesn't exist in the variant options
10609
+ if (!variants[key].hasOwnProperty(value) && value !== true && value !== "true" && value !== false && value !== "false") continue;
10610
+
10568
10611
  // Handle boolean variants
10569
10612
  if (value === true || value === "true") {
10570
10613
  variantParts.push(key);
@@ -10677,27 +10720,40 @@ function fastObjectHash(obj) {
10677
10720
  */
10678
10721
  function twsx(obj) {
10679
10722
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
10680
- // Create fast hash key from input (100x faster than JSON.stringify)
10723
+ // Create fast hash key from input content (100x faster than JSON.stringify)
10681
10724
  const cacheKey = fastObjectHash(obj, options);
10725
+ const {
10726
+ inject = true
10727
+ } = options;
10728
+
10729
+ // Derive a STABLE registry key from the object's top-level selector KEYS only.
10730
+ // This key is independent of the CSS class values, so when styles are edited
10731
+ // during HMR (same selectors, different Tailwind classes) the old slot is
10732
+ // replaced rather than a new slot being created — preventing CSS accumulation.
10733
+ const registryKey = obj && typeof obj === "object" && !Array.isArray(obj) ? Object.keys(obj).sort().join("|") : cacheKey; // fallback for edge cases
10682
10734
 
10683
10735
  // Check cache first
10684
10736
  if (_twsxInputCache.has(cacheKey)) {
10685
10737
  const cached = _twsxInputCache.get(cacheKey);
10686
10738
 
10687
- // Handle injection for cached result
10688
- const {
10689
- inject = true
10690
- } = options;
10739
+ // Re-inject with registryKey so the slot stays registered (no-op when unchanged).
10691
10740
  if (inject && (IS_BROWSER || _ssrCollecting)) {
10692
- autoInjectCss(cached);
10741
+ autoInjectCss(cached, registryKey);
10693
10742
  }
10694
10743
  return cached;
10695
10744
  }
10696
10745
 
10697
- // Cache miss: call original twsxNoCache and cache result
10698
- const result = twsxNoCache(obj, options);
10746
+ // Cache miss: generate CSS without internal auto-injection so that twsx owns
10747
+ // the injection and can pass the selector-based registryKey for slot replacement.
10748
+ const result = twsxNoCache(obj, {
10749
+ ...options,
10750
+ inject: false
10751
+ });
10699
10752
  _twsxInputCache.set(cacheKey, result);
10700
10753
  evictMap(_twsxInputCache);
10754
+ if (inject && (IS_BROWSER || _ssrCollecting)) {
10755
+ autoInjectCss(result, registryKey);
10756
+ }
10701
10757
  return result;
10702
10758
  }
10703
10759
 
@@ -10753,7 +10809,27 @@ function getCssHash(str) {
10753
10809
 
10754
10810
  // Enhanced auto-inject CSS with performance monitoring & SSR support
10755
10811
  const injectedCssHashSet = new Set();
10812
+
10813
+ // Registry of sourceKey → cssBlock for smart slot-based replacement.
10814
+ // Prevents stale CSS chunks from accumulating across HMR cycles.
10815
+ const _cssBlockRegistry = new Map();
10816
+
10817
+ /**
10818
+ * Rebuild the single twsx style tag from the full CSS block registry.
10819
+ * Called whenever a block is added or updated.
10820
+ */
10821
+ function rebuildStyleTag() {
10822
+ let styleTag = document.getElementById("twsx-auto-style");
10823
+ if (!styleTag) {
10824
+ styleTag = document.createElement("style");
10825
+ styleTag.id = "twsx-auto-style";
10826
+ styleTag.setAttribute("data-twsx", "");
10827
+ document.head.appendChild(styleTag);
10828
+ }
10829
+ styleTag.textContent = [..._cssBlockRegistry.values()].join("\n");
10830
+ }
10756
10831
  function autoInjectCss(cssString) {
10832
+ let sourceKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
10757
10833
  const marker = performanceMonitor.start("css:inject");
10758
10834
  try {
10759
10835
  // SSR mode: collect CSS strings instead of DOM injection
@@ -10770,6 +10846,28 @@ function autoInjectCss(cssString) {
10770
10846
  return;
10771
10847
  }
10772
10848
  if (IS_BROWSER) {
10849
+ if (sourceKey) {
10850
+ // Slot-based update: each unique twsx(obj) call owns its own CSS block.
10851
+ // When styles change (new content, same logical call site via cacheKey)
10852
+ // the old slot is replaced and the style tag is fully rebuilt, so no
10853
+ // stale rules from previous HMR cycles can pile up.
10854
+ const existing = _cssBlockRegistry.get(sourceKey);
10855
+ if (existing === cssString) {
10856
+ // Identical content – nothing to do.
10857
+ performanceMonitor.end(marker);
10858
+ return;
10859
+ }
10860
+ _cssBlockRegistry.set(sourceKey, cssString);
10861
+ rebuildStyleTag();
10862
+ if (_cssBlockRegistry.size % 10 === 0) {
10863
+ logger.debug(`CSS registry stats: ${_cssBlockRegistry.size} blocks registered`);
10864
+ }
10865
+ performanceMonitor.end(marker);
10866
+ return;
10867
+ }
10868
+
10869
+ // Fallback path (e.g. direct autoInjectCss calls without a sourceKey):
10870
+ // keep the original hash-based dedup + append behaviour.
10773
10871
  const cssHash = getCssHash(cssString);
10774
10872
  if (injectedCssHashSet.has(cssHash)) {
10775
10873
  performanceMonitor.end(marker);
@@ -10785,30 +10883,11 @@ function autoInjectCss(cssString) {
10785
10883
  document.head.appendChild(styleTag);
10786
10884
  }
10787
10885
 
10788
- // Use insertRule for better performance (avoids full stylesheet reparse)
10789
- try {
10790
- const sheet = styleTag.sheet;
10791
- if (sheet) {
10792
- // Split CSS by closing brace to insert individual rules
10793
- const rules = cssString.split('}').filter(r => r.trim());
10794
- for (const rule of rules) {
10795
- const trimmed = rule.trim();
10796
- if (trimmed) {
10797
- try {
10798
- sheet.insertRule(trimmed + '}', sheet.cssRules.length);
10799
- } catch (e) {
10800
- // Fallback for complex rules (e.g., @keyframes, @media)
10801
- styleTag.textContent += `\n${trimmed}}`;
10802
- }
10803
- }
10804
- }
10805
- } else {
10806
- styleTag.textContent += `\n${cssString}`;
10807
- }
10808
- } catch (e) {
10809
- // Ultimate fallback
10810
- styleTag.textContent += `\n${cssString}`;
10811
- }
10886
+ // Append CSS to style tag using textContent for reliability
10887
+ // Note: insertRule + textContent mixing destroys CSSOM rules,
10888
+ // so we use textContent exclusively for consistent behavior
10889
+ // with @keyframes, @media, and other nested CSS blocks.
10890
+ styleTag.textContent += `\n${cssString}`;
10812
10891
 
10813
10892
  // Log injection stats periodically
10814
10893
  if (injectedCssHashSet.size % 10 === 0) {
@@ -10854,6 +10933,7 @@ const performanceUtils = {
10854
10933
  },
10855
10934
  injectionStats: {
10856
10935
  uniqueStylesheets: injectedCssHashSet.size,
10936
+ cssBlocks: _cssBlockRegistry.size,
10857
10937
  keyframes: _injectedKeyframes.size
10858
10938
  }
10859
10939
  };
@@ -10868,6 +10948,14 @@ const performanceUtils = {
10868
10948
  // Clear new input-level caches
10869
10949
  _twsxInputCache.clear();
10870
10950
  _twsxVariantsResultCache.clear();
10951
+
10952
+ // Clear CSS block registry and rebuild (empty) the style tag
10953
+ _cssBlockRegistry.clear();
10954
+ injectedCssHashSet.clear();
10955
+ if (IS_BROWSER) {
10956
+ const styleTag = document.getElementById("twsx-auto-style");
10957
+ if (styleTag) styleTag.textContent = "";
10958
+ }
10871
10959
  logger.info("All caches cleared");
10872
10960
  performanceMonitor.end(marker);
10873
10961
  },
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tailwind-to-style v3.2.0
2
+ * tailwind-to-style v3.2.2
3
3
  * Runtime Tailwind CSS to inline styles converter
4
4
  *
5
5
  * @author Bigetion
@@ -9612,7 +9612,7 @@ function processClass(cls, selector, styles) {
9612
9612
 
9613
9613
  // Get cssObject from singleton cache
9614
9614
  const cssObject = tailwindCache.getOrGenerate(generateTailwindCssString, convertCssToObject);
9615
- let declarations = cssObject[baseClassName] || cssObject[baseClassName.replace(ESCAPE_SLASH_REGEX, "\\$1")] || cssObject[baseClassName.replace(ESCAPE_DOT_REGEX, "\\.")];
9615
+ let declarations = cssObject[baseClassName] || cssObject[baseClassName.replace(ESCAPE_SLASH_REGEX, "\\/")] || cssObject[baseClassName.replace(ESCAPE_DOT_REGEX, "\\.")];
9616
9616
  if (!declarations && baseClassName.includes("[")) {
9617
9617
  const match = CUSTOM_VALUE_FULL_REGEX.exec(baseClassName);
9618
9618
  if (match) {
@@ -9834,11 +9834,26 @@ function flattenStyleObject(obj) {
9834
9834
  flatArray.push(item);
9835
9835
  } else if (isSelectorObject(item)) {
9836
9836
  const nested = flattenStyleObject(item, currentSelector);
9837
- Object.assign(result, nested);
9837
+ // Merge nested results, handling & that resolves to same selector
9838
+ for (const ns in nested) {
9839
+ if (ns === currentSelector) {
9840
+ // & resolved to same selector — include in this array
9841
+ if (Array.isArray(nested[ns])) {
9842
+ flatArray.push(...nested[ns]);
9843
+ } else {
9844
+ flatArray.push(nested[ns]);
9845
+ }
9846
+ } else {
9847
+ result[ns] = nested[ns];
9848
+ }
9849
+ }
9838
9850
  }
9839
9851
  }
9840
9852
  if (flatArray.length > 0) {
9841
9853
  result[currentSelector] = result[currentSelector] || [];
9854
+ if (!Array.isArray(result[currentSelector])) {
9855
+ result[currentSelector] = [result[currentSelector]];
9856
+ }
9842
9857
  result[currentSelector].push(...flatArray);
9843
9858
  }
9844
9859
  } else if (isSelectorObject(val)) {
@@ -9970,6 +9985,20 @@ function twsxNoCache(obj) {
9970
9985
  // Process each selector within the media query
9971
9986
  for (const innerSelector in val) {
9972
9987
  const innerVal = val[innerSelector];
9988
+
9989
+ // Handle @css string directive inside media queries
9990
+ if (typeof innerVal === "string") {
9991
+ const trimmedInner = innerVal.trim();
9992
+ if (trimmedInner.startsWith('@css')) {
9993
+ const cssMatch = trimmedInner.match(/^@css\s*\{([\s\S]*)\}\s*$/);
9994
+ if (cssMatch) {
9995
+ const rawCss = cssMatch[1].trim();
9996
+ styles[selector][innerSelector] = styles[selector][innerSelector] || '';
9997
+ styles[selector][innerSelector] += rawCss.split(';').filter(d => d.trim()).map(d => d.trim() + ';').join(' ') + '\n';
9998
+ continue;
9999
+ }
10000
+ }
10001
+ }
9973
10002
  const baseClass = typeof innerVal === "string" ? expandGroupedClass(innerVal) : "";
9974
10003
 
9975
10004
  // Process Tailwind classes for this selector
@@ -10009,6 +10038,17 @@ function twsxNoCache(obj) {
10009
10038
  } else if (Array.isArray(val)) {
10010
10039
  for (const item of val) {
10011
10040
  if (typeof item === "string") {
10041
+ // Handle @css strings inside arrays
10042
+ const trimmedItem = item.trim();
10043
+ if (trimmedItem.startsWith('@css')) {
10044
+ const cssMatch = trimmedItem.match(/^@css\s*\{([\s\S]*)\}\s*$/);
10045
+ if (cssMatch) {
10046
+ const rawCss = cssMatch[1].trim();
10047
+ styles[selector] = styles[selector] || '';
10048
+ styles[selector] += rawCss.split(';').filter(d => d.trim()).map(d => d.trim() + ';').join(' ') + '\n';
10049
+ continue;
10050
+ }
10051
+ }
10012
10052
  baseClass += (baseClass ? " " : "") + expandGroupedClass(item);
10013
10053
  } else if (typeof item === "object" && item !== null) {
10014
10054
  Object.assign(nested, item);
@@ -10563,6 +10603,9 @@ function twsxVariantsNoCache(className) {
10563
10603
  // Skip if it's the default value
10564
10604
  if (value === defaultVariants[key]) continue;
10565
10605
 
10606
+ // Skip if the variant value doesn't exist in the variant options
10607
+ if (!variants[key].hasOwnProperty(value) && value !== true && value !== "true" && value !== false && value !== "false") continue;
10608
+
10566
10609
  // Handle boolean variants
10567
10610
  if (value === true || value === "true") {
10568
10611
  variantParts.push(key);
@@ -10675,27 +10718,40 @@ function fastObjectHash(obj) {
10675
10718
  */
10676
10719
  function twsx(obj) {
10677
10720
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
10678
- // Create fast hash key from input (100x faster than JSON.stringify)
10721
+ // Create fast hash key from input content (100x faster than JSON.stringify)
10679
10722
  const cacheKey = fastObjectHash(obj, options);
10723
+ const {
10724
+ inject = true
10725
+ } = options;
10726
+
10727
+ // Derive a STABLE registry key from the object's top-level selector KEYS only.
10728
+ // This key is independent of the CSS class values, so when styles are edited
10729
+ // during HMR (same selectors, different Tailwind classes) the old slot is
10730
+ // replaced rather than a new slot being created — preventing CSS accumulation.
10731
+ const registryKey = obj && typeof obj === "object" && !Array.isArray(obj) ? Object.keys(obj).sort().join("|") : cacheKey; // fallback for edge cases
10680
10732
 
10681
10733
  // Check cache first
10682
10734
  if (_twsxInputCache.has(cacheKey)) {
10683
10735
  const cached = _twsxInputCache.get(cacheKey);
10684
10736
 
10685
- // Handle injection for cached result
10686
- const {
10687
- inject = true
10688
- } = options;
10737
+ // Re-inject with registryKey so the slot stays registered (no-op when unchanged).
10689
10738
  if (inject && (IS_BROWSER || _ssrCollecting)) {
10690
- autoInjectCss(cached);
10739
+ autoInjectCss(cached, registryKey);
10691
10740
  }
10692
10741
  return cached;
10693
10742
  }
10694
10743
 
10695
- // Cache miss: call original twsxNoCache and cache result
10696
- const result = twsxNoCache(obj, options);
10744
+ // Cache miss: generate CSS without internal auto-injection so that twsx owns
10745
+ // the injection and can pass the selector-based registryKey for slot replacement.
10746
+ const result = twsxNoCache(obj, {
10747
+ ...options,
10748
+ inject: false
10749
+ });
10697
10750
  _twsxInputCache.set(cacheKey, result);
10698
10751
  evictMap(_twsxInputCache);
10752
+ if (inject && (IS_BROWSER || _ssrCollecting)) {
10753
+ autoInjectCss(result, registryKey);
10754
+ }
10699
10755
  return result;
10700
10756
  }
10701
10757
 
@@ -10751,7 +10807,27 @@ function getCssHash(str) {
10751
10807
 
10752
10808
  // Enhanced auto-inject CSS with performance monitoring & SSR support
10753
10809
  const injectedCssHashSet = new Set();
10810
+
10811
+ // Registry of sourceKey → cssBlock for smart slot-based replacement.
10812
+ // Prevents stale CSS chunks from accumulating across HMR cycles.
10813
+ const _cssBlockRegistry = new Map();
10814
+
10815
+ /**
10816
+ * Rebuild the single twsx style tag from the full CSS block registry.
10817
+ * Called whenever a block is added or updated.
10818
+ */
10819
+ function rebuildStyleTag() {
10820
+ let styleTag = document.getElementById("twsx-auto-style");
10821
+ if (!styleTag) {
10822
+ styleTag = document.createElement("style");
10823
+ styleTag.id = "twsx-auto-style";
10824
+ styleTag.setAttribute("data-twsx", "");
10825
+ document.head.appendChild(styleTag);
10826
+ }
10827
+ styleTag.textContent = [..._cssBlockRegistry.values()].join("\n");
10828
+ }
10754
10829
  function autoInjectCss(cssString) {
10830
+ let sourceKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
10755
10831
  const marker = performanceMonitor.start("css:inject");
10756
10832
  try {
10757
10833
  // SSR mode: collect CSS strings instead of DOM injection
@@ -10768,6 +10844,28 @@ function autoInjectCss(cssString) {
10768
10844
  return;
10769
10845
  }
10770
10846
  if (IS_BROWSER) {
10847
+ if (sourceKey) {
10848
+ // Slot-based update: each unique twsx(obj) call owns its own CSS block.
10849
+ // When styles change (new content, same logical call site via cacheKey)
10850
+ // the old slot is replaced and the style tag is fully rebuilt, so no
10851
+ // stale rules from previous HMR cycles can pile up.
10852
+ const existing = _cssBlockRegistry.get(sourceKey);
10853
+ if (existing === cssString) {
10854
+ // Identical content – nothing to do.
10855
+ performanceMonitor.end(marker);
10856
+ return;
10857
+ }
10858
+ _cssBlockRegistry.set(sourceKey, cssString);
10859
+ rebuildStyleTag();
10860
+ if (_cssBlockRegistry.size % 10 === 0) {
10861
+ logger.debug(`CSS registry stats: ${_cssBlockRegistry.size} blocks registered`);
10862
+ }
10863
+ performanceMonitor.end(marker);
10864
+ return;
10865
+ }
10866
+
10867
+ // Fallback path (e.g. direct autoInjectCss calls without a sourceKey):
10868
+ // keep the original hash-based dedup + append behaviour.
10771
10869
  const cssHash = getCssHash(cssString);
10772
10870
  if (injectedCssHashSet.has(cssHash)) {
10773
10871
  performanceMonitor.end(marker);
@@ -10783,30 +10881,11 @@ function autoInjectCss(cssString) {
10783
10881
  document.head.appendChild(styleTag);
10784
10882
  }
10785
10883
 
10786
- // Use insertRule for better performance (avoids full stylesheet reparse)
10787
- try {
10788
- const sheet = styleTag.sheet;
10789
- if (sheet) {
10790
- // Split CSS by closing brace to insert individual rules
10791
- const rules = cssString.split('}').filter(r => r.trim());
10792
- for (const rule of rules) {
10793
- const trimmed = rule.trim();
10794
- if (trimmed) {
10795
- try {
10796
- sheet.insertRule(trimmed + '}', sheet.cssRules.length);
10797
- } catch (e) {
10798
- // Fallback for complex rules (e.g., @keyframes, @media)
10799
- styleTag.textContent += `\n${trimmed}}`;
10800
- }
10801
- }
10802
- }
10803
- } else {
10804
- styleTag.textContent += `\n${cssString}`;
10805
- }
10806
- } catch (e) {
10807
- // Ultimate fallback
10808
- styleTag.textContent += `\n${cssString}`;
10809
- }
10884
+ // Append CSS to style tag using textContent for reliability
10885
+ // Note: insertRule + textContent mixing destroys CSSOM rules,
10886
+ // so we use textContent exclusively for consistent behavior
10887
+ // with @keyframes, @media, and other nested CSS blocks.
10888
+ styleTag.textContent += `\n${cssString}`;
10810
10889
 
10811
10890
  // Log injection stats periodically
10812
10891
  if (injectedCssHashSet.size % 10 === 0) {
@@ -10852,6 +10931,7 @@ const performanceUtils = {
10852
10931
  },
10853
10932
  injectionStats: {
10854
10933
  uniqueStylesheets: injectedCssHashSet.size,
10934
+ cssBlocks: _cssBlockRegistry.size,
10855
10935
  keyframes: _injectedKeyframes.size
10856
10936
  }
10857
10937
  };
@@ -10866,6 +10946,14 @@ const performanceUtils = {
10866
10946
  // Clear new input-level caches
10867
10947
  _twsxInputCache.clear();
10868
10948
  _twsxVariantsResultCache.clear();
10949
+
10950
+ // Clear CSS block registry and rebuild (empty) the style tag
10951
+ _cssBlockRegistry.clear();
10952
+ injectedCssHashSet.clear();
10953
+ if (IS_BROWSER) {
10954
+ const styleTag = document.getElementById("twsx-auto-style");
10955
+ if (styleTag) styleTag.textContent = "";
10956
+ }
10869
10957
  logger.info("All caches cleared");
10870
10958
  performanceMonitor.end(marker);
10871
10959
  },