tailwind-to-style 3.1.3 → 3.2.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.esm.js CHANGED
@@ -1,7 +1,6 @@
1
1
  /**
2
- * tailwind-to-style v3.1.3
2
+ * tailwind-to-style v3.2.0
3
3
  * Runtime Tailwind CSS to inline styles converter
4
- * Core only: tws, twsx, configure
5
4
  *
6
5
  * @author Bigetion
7
6
  * @license MIT
@@ -1810,9 +1809,9 @@ try {
1810
1809
  }
1811
1810
  const logger = new Logger(logLevel);
1812
1811
 
1813
- /**
1814
- * User Configuration Management
1815
- * Handles theme extensions and custom plugin registration
1812
+ /**
1813
+ * User Configuration Management
1814
+ * Handles theme extensions and custom plugin registration
1816
1815
  */
1817
1816
 
1818
1817
 
@@ -1824,8 +1823,8 @@ function setClearConfigCache(fn) {
1824
1823
  clearConfigCache$1 = fn;
1825
1824
  }
1826
1825
 
1827
- /**
1828
- * User configuration state
1826
+ /**
1827
+ * User configuration state
1829
1828
  */
1830
1829
  let userConfig = {
1831
1830
  theme: {
@@ -1848,11 +1847,11 @@ let userConfig = {
1848
1847
  // Cache for extended theme to avoid redundant lookups
1849
1848
  const extendedThemeCache = new Map();
1850
1849
 
1851
- /**
1852
- * Deep merge two objects
1853
- * @param {Object} target - Target object
1854
- * @param {Object} source - Source object
1855
- * @returns {Object} Merged object
1850
+ /**
1851
+ * Deep merge two objects
1852
+ * @param {Object} target - Target object
1853
+ * @param {Object} source - Source object
1854
+ * @returns {Object} Merged object
1856
1855
  */
1857
1856
  function deepMerge(target, source) {
1858
1857
  const result = {
@@ -1868,31 +1867,31 @@ function deepMerge(target, source) {
1868
1867
  return result;
1869
1868
  }
1870
1869
 
1871
- /**
1872
- * Configure tailwind-to-style with custom theme and plugins
1873
- * @param {Object} config - Configuration object
1874
- * @param {Object} [config.theme] - Theme configuration
1875
- * @param {Object} [config.theme.extend] - Theme extensions
1876
- * @param {Array} [config.plugins] - Array of plugins
1877
- * @param {Object} [config.corePlugins] - Core plugins to enable/disable
1878
- * @param {string} [config.prefix] - Prefix for all classes
1879
- *
1880
- * @example
1881
- * configure({
1882
- * theme: {
1883
- * extend: {
1884
- * colors: {
1885
- * brand: {
1886
- * 500: '#3B82F6',
1887
- * },
1888
- * },
1889
- * spacing: {
1890
- * 128: '32rem',
1891
- * },
1892
- * },
1893
- * },
1894
- * plugins: [myCustomPlugin],
1895
- * });
1870
+ /**
1871
+ * Configure tailwind-to-style with custom theme and plugins
1872
+ * @param {Object} config - Configuration object
1873
+ * @param {Object} [config.theme] - Theme configuration
1874
+ * @param {Object} [config.theme.extend] - Theme extensions
1875
+ * @param {Array} [config.plugins] - Array of plugins
1876
+ * @param {Object} [config.corePlugins] - Core plugins to enable/disable
1877
+ * @param {string} [config.prefix] - Prefix for all classes
1878
+ *
1879
+ * @example
1880
+ * configure({
1881
+ * theme: {
1882
+ * extend: {
1883
+ * colors: {
1884
+ * brand: {
1885
+ * 500: '#3B82F6',
1886
+ * },
1887
+ * },
1888
+ * spacing: {
1889
+ * 128: '32rem',
1890
+ * },
1891
+ * },
1892
+ * },
1893
+ * plugins: [myCustomPlugin],
1894
+ * });
1896
1895
  */
1897
1896
  function configure() {
1898
1897
  let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -1903,6 +1902,32 @@ function configure() {
1903
1902
  return;
1904
1903
  }
1905
1904
 
1905
+ // Validate config structure
1906
+ const validTopKeys = ['theme', 'plugins', 'corePlugins', 'prefix', 'styled'];
1907
+ const invalidKeys = Object.keys(config).filter(k => !validTopKeys.includes(k));
1908
+ if (invalidKeys.length > 0) {
1909
+ logger.warn(`configure: Unrecognized config keys: ${invalidKeys.join(', ')}. ` + `Valid keys are: ${validTopKeys.join(', ')}`);
1910
+ }
1911
+ if (config.theme) {
1912
+ if (typeof config.theme !== 'object' || Array.isArray(config.theme)) {
1913
+ logger.warn('configure: theme must be an object');
1914
+ return;
1915
+ }
1916
+ const validThemeKeys = ['extend', 'colors', 'spacing', 'borderRadius', 'fontSize', 'fontFamily', 'screens', 'breakpoints'];
1917
+ const invalidThemeKeys = Object.keys(config.theme).filter(k => !validThemeKeys.includes(k) && typeof config.theme[k] !== 'object');
1918
+ if (invalidThemeKeys.length > 0) {
1919
+ logger.warn(`configure: Unrecognized theme keys: ${invalidThemeKeys.join(', ')}. ` + `Common keys are: ${validThemeKeys.join(', ')}`);
1920
+ }
1921
+ }
1922
+ if (config.plugins !== undefined && !Array.isArray(config.plugins)) {
1923
+ logger.warn('configure: plugins must be an array');
1924
+ return;
1925
+ }
1926
+ if (config.prefix !== undefined && typeof config.prefix !== 'string') {
1927
+ logger.warn('configure: prefix must be a string');
1928
+ return;
1929
+ }
1930
+
1906
1931
  // Clear extended theme cache when config changes
1907
1932
  extendedThemeCache.clear();
1908
1933
 
@@ -1954,9 +1979,9 @@ function configure() {
1954
1979
  }
1955
1980
  }
1956
1981
 
1957
- /**
1958
- * Get current user configuration
1959
- * @returns {Object} Current configuration
1982
+ /**
1983
+ * Get current user configuration
1984
+ * @returns {Object} Current configuration
1960
1985
  */
1961
1986
  function getConfig() {
1962
1987
  return {
@@ -1964,8 +1989,8 @@ function getConfig() {
1964
1989
  };
1965
1990
  }
1966
1991
 
1967
- /**
1968
- * Reset configuration to defaults
1992
+ /**
1993
+ * Reset configuration to defaults
1969
1994
  */
1970
1995
  function resetConfig() {
1971
1996
  userConfig = {
@@ -1981,10 +2006,10 @@ function resetConfig() {
1981
2006
  logger.info("Configuration reset to defaults");
1982
2007
  }
1983
2008
 
1984
- /**
1985
- * Get extended theme value
1986
- * @param {string} key - Theme key (e.g., 'colors', 'spacing')
1987
- * @returns {Object} Extended theme values
2009
+ /**
2010
+ * Get extended theme value
2011
+ * @param {string} key - Theme key (e.g., 'colors', 'spacing')
2012
+ * @returns {Object} Extended theme values
1988
2013
  */
1989
2014
  function getExtendedTheme(key) {
1990
2015
  // Check cache first
@@ -1998,17 +2023,17 @@ function getExtendedTheme(key) {
1998
2023
  return result;
1999
2024
  }
2000
2025
 
2001
- /**
2002
- * Get all registered plugins
2003
- * @returns {Array} Array of plugins
2026
+ /**
2027
+ * Get all registered plugins
2028
+ * @returns {Array} Array of plugins
2004
2029
  */
2005
2030
  function getPlugins() {
2006
2031
  return userConfig.plugins;
2007
2032
  }
2008
2033
 
2009
- /**
2010
- * Get configured prefix
2011
- * @returns {string} Prefix string
2034
+ /**
2035
+ * Get configured prefix
2036
+ * @returns {string} Prefix string
2012
2037
  */
2013
2038
  function getPrefix() {
2014
2039
  return userConfig.prefix;
@@ -2523,9 +2548,9 @@ function staggerAnimations(elements, animationName) {
2523
2548
  });
2524
2549
  }
2525
2550
 
2526
- /**
2527
- * Animation Generator
2528
- * Generates animation utility classes with dynamic inline animations
2551
+ /**
2552
+ * Animation Generator
2553
+ * Generates animation utility classes with dynamic inline animations
2529
2554
  */
2530
2555
 
2531
2556
  function generator$2I() {
@@ -7243,9 +7268,9 @@ function generator$d() {
7243
7268
  return responsiveCssString;
7244
7269
  }
7245
7270
 
7246
- /**
7247
- * Transition Delay Generator
7248
- * Generates transition-delay utility classes
7271
+ /**
7272
+ * Transition Delay Generator
7273
+ * Generates transition-delay utility classes
7249
7274
  */
7250
7275
 
7251
7276
  function generator$c() {
@@ -7274,9 +7299,9 @@ function generator$c() {
7274
7299
  return responsiveCssString;
7275
7300
  }
7276
7301
 
7277
- /**
7278
- * Transition Duration Generator
7279
- * Generates transition-duration utility classes
7302
+ /**
7303
+ * Transition Duration Generator
7304
+ * Generates transition-duration utility classes
7280
7305
  */
7281
7306
 
7282
7307
  function generator$b() {
@@ -7305,9 +7330,9 @@ function generator$b() {
7305
7330
  return responsiveCssString;
7306
7331
  }
7307
7332
 
7308
- /**
7309
- * Transition Property Generator
7310
- * Generates transition-property utility classes
7333
+ /**
7334
+ * Transition Property Generator
7335
+ * Generates transition-property utility classes
7311
7336
  */
7312
7337
 
7313
7338
  function generator$a() {
@@ -7347,9 +7372,9 @@ function generator$a() {
7347
7372
  return responsiveCssString;
7348
7373
  }
7349
7374
 
7350
- /**
7351
- * Transition Timing Function Generator
7352
- * Generates transition-timing-function utility classes (ease)
7375
+ /**
7376
+ * Transition Timing Function Generator
7377
+ * Generates transition-timing-function utility classes (ease)
7353
7378
  */
7354
7379
 
7355
7380
  function generator$9() {
@@ -7988,6 +8013,108 @@ const patterns = {
7988
8013
  ...fontFamily
7989
8014
  };
7990
8015
 
8016
+ /**
8017
+ * CX - Conditional Class Name Builder
8018
+ *
8019
+ * A lightweight utility for conditionally joining Tailwind class names.
8020
+ * Similar to `clsx`/`classnames` but designed specifically for tailwind-to-style.
8021
+ *
8022
+ * @module cx
8023
+ */
8024
+
8025
+ /**
8026
+ * Conditionally join class names into a single string.
8027
+ *
8028
+ * Accepts strings, objects (key=className, value=condition), arrays, and nested combinations.
8029
+ * Falsy values (null, undefined, false, 0, '') are ignored.
8030
+ *
8031
+ * @param {...(string|Object|Array|boolean|null|undefined)} args - Class name inputs
8032
+ * @returns {string} Joined class names string
8033
+ *
8034
+ * @example
8035
+ * // Strings
8036
+ * cx('bg-blue-500', 'text-white')
8037
+ * // → 'bg-blue-500 text-white'
8038
+ *
8039
+ * @example
8040
+ * // Conditionals
8041
+ * cx('p-4', isActive && 'bg-blue-500', isDisabled && 'opacity-50')
8042
+ * // → 'p-4 bg-blue-500' (if isActive=true, isDisabled=false)
8043
+ *
8044
+ * @example
8045
+ * // Object syntax
8046
+ * cx('p-4', { 'bg-blue-500': isActive, 'opacity-50': isDisabled, 'cursor-pointer': true })
8047
+ * // → 'p-4 bg-blue-500 cursor-pointer'
8048
+ *
8049
+ * @example
8050
+ * // Arrays (nested)
8051
+ * cx(['p-4', 'bg-white'], isActive && ['ring-2', 'ring-blue-500'])
8052
+ * // → 'p-4 bg-white ring-2 ring-blue-500'
8053
+ *
8054
+ * @example
8055
+ * // Mixed
8056
+ * cx(
8057
+ * 'base-class',
8058
+ * condition && 'conditional-class',
8059
+ * { 'object-class': true, 'ignored-class': false },
8060
+ * ['array-class-1', 'array-class-2']
8061
+ * )
8062
+ */
8063
+ function cx() {
8064
+ const classes = [];
8065
+ for (let i = 0; i < arguments.length; i++) {
8066
+ const arg = i < 0 || arguments.length <= i ? undefined : arguments[i];
8067
+
8068
+ // Skip falsy values
8069
+ if (!arg) continue;
8070
+ const type = typeof arg;
8071
+ if (type === 'string') {
8072
+ classes.push(arg);
8073
+ } else if (Array.isArray(arg)) {
8074
+ // Recursively process arrays
8075
+ const inner = cx(...arg);
8076
+ if (inner) classes.push(inner);
8077
+ } else if (type === 'object') {
8078
+ // Object: keys are class names, values are conditions
8079
+ const keys = Object.keys(arg);
8080
+ for (let j = 0; j < keys.length; j++) {
8081
+ if (arg[keys[j]]) {
8082
+ classes.push(keys[j]);
8083
+ }
8084
+ }
8085
+ }
8086
+ }
8087
+ return classes.join(' ');
8088
+ }
8089
+
8090
+ /**
8091
+ * Create a cx function bound with base classes.
8092
+ * Useful for component-level class composition.
8093
+ *
8094
+ * @param {...(string|Object|Array)} baseArgs - Base class arguments always included
8095
+ * @returns {Function} A cx function pre-filled with base classes
8096
+ *
8097
+ * @example
8098
+ * const btnClasses = cx.with('px-4 py-2 rounded font-medium transition-colors')
8099
+ *
8100
+ * btnClasses('bg-blue-500 text-white')
8101
+ * // → 'px-4 py-2 rounded font-medium transition-colors bg-blue-500 text-white'
8102
+ *
8103
+ * btnClasses({ 'opacity-50 cursor-not-allowed': isDisabled })
8104
+ * // → 'px-4 py-2 rounded font-medium transition-colors opacity-50 cursor-not-allowed'
8105
+ */
8106
+ cx.with = function () {
8107
+ for (var _len = arguments.length, baseArgs = new Array(_len), _key = 0; _key < _len; _key++) {
8108
+ baseArgs[_key] = arguments[_key];
8109
+ }
8110
+ return function () {
8111
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
8112
+ args[_key2] = arguments[_key2];
8113
+ }
8114
+ return cx(...baseArgs, ...args);
8115
+ };
8116
+ };
8117
+
7991
8118
  /**
7992
8119
  * Web Animations API Integration
7993
8120
  * Dynamic animations without keyframes injection
@@ -8309,16 +8436,89 @@ function applyDynamicAnimation(element, templateName) {
8309
8436
  return animationName;
8310
8437
  }
8311
8438
 
8439
+ // ============================================================================
8440
+ // SSR (Server-Side Rendering) Support
8441
+ // Detect environment once at module load for zero-cost runtime checks
8442
+ // ============================================================================
8443
+ const IS_BROWSER = typeof window !== "undefined" && typeof document !== "undefined";
8444
+ const IS_SERVER = !IS_BROWSER;
8445
+
8446
+ // SSR CSS collector - accumulates CSS strings during server rendering
8447
+ let _ssrCollectedCss = [];
8448
+ let _ssrCollecting = false;
8449
+
8450
+ /**
8451
+ * Start collecting CSS for SSR. Call before rendering.
8452
+ * @returns {void}
8453
+ * @example
8454
+ * import { startSSR, stopSSR } from 'tailwind-to-style'
8455
+ * startSSR()
8456
+ * const html = renderToString(<App />)
8457
+ * const css = stopSSR()
8458
+ * // Inject css into <head> of your HTML response
8459
+ */
8460
+ function startSSR() {
8461
+ _ssrCollectedCss = [];
8462
+ _ssrCollecting = true;
8463
+ }
8464
+
8465
+ /**
8466
+ * Stop collecting CSS and return all collected CSS as a single string.
8467
+ * @returns {string} All CSS collected during SSR
8468
+ */
8469
+ function stopSSR() {
8470
+ _ssrCollecting = false;
8471
+ const css = _ssrCollectedCss.join('\n');
8472
+ _ssrCollectedCss = [];
8473
+ return css;
8474
+ }
8475
+
8476
+ /**
8477
+ * Get collected CSS without stopping collection.
8478
+ * @returns {string} Currently collected CSS
8479
+ */
8480
+ function getSSRStyles() {
8481
+ return _ssrCollectedCss.join('\n');
8482
+ }
8483
+
8484
+ // ============================================================================
8485
+ // Bounded Caches (prevent memory leaks in long-running SPAs)
8486
+ // ============================================================================
8487
+ const MAX_CACHE_SIZE = 5000;
8488
+ const MAX_SET_SIZE = 10000;
8489
+
8312
8490
  // Global registry to track injected keyframes (prevents duplication)
8313
8491
  const _injectedKeyframes = new Set();
8314
8492
 
8315
- // Global cache Maps for cached versions (twsxCache, twsxVariantsCache functions)
8493
+ // Global cache Maps with bounded eviction
8316
8494
  const _twsxInputCache = new Map();
8317
8495
  const _twsxVariantsResultCache = new Map();
8318
8496
 
8319
8497
  // WeakMap for object identity-based caching (fast lookup for repeated objects)
8320
8498
  const _objectIdentityCache = new WeakMap();
8321
8499
 
8500
+ /** Evict oldest entries from a Map when it exceeds maxSize */
8501
+ function evictMap(map) {
8502
+ let maxSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : MAX_CACHE_SIZE;
8503
+ if (map.size <= maxSize) return;
8504
+ const excess = map.size - maxSize;
8505
+ const iter = map.keys();
8506
+ for (let i = 0; i < excess; i++) {
8507
+ map.delete(iter.next().value);
8508
+ }
8509
+ }
8510
+
8511
+ /** Evict oldest entries from a Set when it exceeds maxSize */
8512
+ function evictSet(set) {
8513
+ let maxSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : MAX_SET_SIZE;
8514
+ if (set.size <= maxSize) return;
8515
+ const excess = set.size - maxSize;
8516
+ const iter = set.values();
8517
+ for (let i = 0; i < excess; i++) {
8518
+ set.delete(iter.next().value);
8519
+ }
8520
+ }
8521
+
8322
8522
  // Mapping of animation names to their keyframe definitions
8323
8523
  const BUILTIN_KEYFRAMES = {
8324
8524
  spin: {
@@ -8403,8 +8603,8 @@ function generateMinifiedKeyframes(animationNames) {
8403
8603
  // by avoiding repeated regex object creation in hot code paths
8404
8604
  // ============================================================================
8405
8605
 
8406
- // Class parsing
8407
- const CLASS_PARSER_REGEX = /[\w-\/]+(?:\/\d+)?(?:\[[^\]]+\])?/g;
8606
+ // Class parsing (includes . for decimal values like p-0.5)
8607
+ const CLASS_PARSER_REGEX = /[\w.\-\/]+(?:\/\d+)?(?:\[[^\]]+\])?/g;
8408
8608
 
8409
8609
  // Opacity modifiers
8410
8610
  const OPACITY_MODIFIER_REGEX = /\/(\d+)$/;
@@ -8440,8 +8640,8 @@ const VARIANT_GROUP_REGEX = /(\w+):\(([^()]+(?:\((?:[^()]+)\))?[^()]*)\)/g;
8440
8640
  const WHITESPACE_SPLIT_REGEX = /\s+/;
8441
8641
  const VARIANT_COLON_SPLIT_REGEX = /:/;
8442
8642
 
8443
- // CSS variable resolution
8444
- const CSS_VAR_REGEX = /var\((--[\w-]+)(?:,\s*([^)]+))?\)/g;
8643
+ // CSS variable resolution — supports nested parens in fallback (e.g. rgba(...))
8644
+ const CSS_VAR_REGEX = /var\((--[\w-]+)(?:,\s*((?:[^()]+|\([^()]*\))*))?\)/g;
8445
8645
  const CAMEL_CASE_REGEX = /-([a-z])/g;
8446
8646
 
8447
8647
  // Animation detection
@@ -8679,11 +8879,11 @@ function parseCustomClassWithPatterns(className) {
8679
8879
  return null;
8680
8880
  }
8681
8881
 
8682
- /**
8683
- * Resolve all CSS custom properties (var) in a CSS string and output only clear CSS (no custom property)
8684
- * Optimized with for loops and indexOf for 2-3x better performance
8685
- * @param {string} cssString
8686
- * @returns {string} e.g. 'color: rgba(255,255,255,1); background: #fff;'
8882
+ /**
8883
+ * Resolve all CSS custom properties (var) in a CSS string and output only clear CSS (no custom property)
8884
+ * Optimized with for loops and indexOf for 2-3x better performance
8885
+ * @param {string} cssString
8886
+ * @returns {string} e.g. 'color: rgba(255,255,255,1); background: #fff;'
8687
8887
  */
8688
8888
  function resolveCssToClearCss(cssString) {
8689
8889
  const customVars = {};
@@ -8735,9 +8935,9 @@ function resolveCssToClearCss(cssString) {
8735
8935
  // Cache for getConfigOptions - use LRU cache
8736
8936
  const configOptionsCache = new LRUCache(500);
8737
8937
 
8738
- /**
8739
- * Clear config options cache (internal use)
8740
- * Called when user configuration changes
8938
+ /**
8939
+ * Clear config options cache (internal use)
8940
+ * Called when user configuration changes
8741
8941
  */
8742
8942
  function clearConfigCache() {
8743
8943
  configOptionsCache.clear();
@@ -9003,6 +9203,9 @@ function separateAndResolveCSS(arr) {
9003
9203
  // Prioritize more specific values (e.g., !important)
9004
9204
  if (value.includes("!important") || !cssProperties[key]) {
9005
9205
  cssProperties[key] = value;
9206
+ } else if (key === "--gradient-color-stops" && value.includes("--gradient-via-color") && !cssProperties[key].includes("--gradient-via-color")) {
9207
+ // Allow 3-stop gradient (with via) to overwrite 2-stop version
9208
+ cssProperties[key] = value;
9006
9209
  }
9007
9210
  }
9008
9211
  }
@@ -9014,10 +9217,10 @@ function separateAndResolveCSS(arr) {
9014
9217
  ...cssProperties
9015
9218
  };
9016
9219
 
9017
- /**
9018
- * Optimized CSS variable resolution using regex-based approach
9019
- * 2-3x faster than manual char-by-char parsing
9020
- * Handles nested var() with fallback values
9220
+ /**
9221
+ * Optimized CSS variable resolution using regex-based approach
9222
+ * 2-3x faster than manual char-by-char parsing
9223
+ * Handles nested var() with fallback values
9021
9224
  */
9022
9225
  const resolveValue = function (value, variables) {
9023
9226
  let maxDepth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
@@ -9106,11 +9309,11 @@ function separateAndResolveCSS(arr) {
9106
9309
  }
9107
9310
  }
9108
9311
 
9109
- /**
9110
- * Process opacity modifier from class name (e.g., text-red-500/50 -> 50% opacity)
9111
- * @param {string} className - Class name with potential opacity modifier
9112
- * @param {string} cssDeclaration - CSS declaration to modify
9113
- * @returns {string} Modified CSS declaration with opacity applied
9312
+ /**
9313
+ * Process opacity modifier from class name (e.g., text-red-500/50 -> 50% opacity)
9314
+ * @param {string} className - Class name with potential opacity modifier
9315
+ * @param {string} cssDeclaration - CSS declaration to modify
9316
+ * @returns {string} Modified CSS declaration with opacity applied
9114
9317
  */
9115
9318
  function processOpacityModifier(className, cssDeclaration) {
9116
9319
  const opacityMatch = OPACITY_MODIFIER_REGEX.exec(className);
@@ -9173,18 +9376,18 @@ function processOpacityModifier(className, cssDeclaration) {
9173
9376
  return modifiedDeclaration;
9174
9377
  }
9175
9378
 
9176
- /**
9177
- * Convert Tailwind class string to inline CSS styles or JSON object
9178
- * @param {string} classNames - String containing Tailwind classes to convert
9179
- * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
9180
- * @returns {string|Object} Inline CSS string or style JSON object
9379
+ /**
9380
+ * Convert Tailwind class string to inline CSS styles or JSON object
9381
+ * @param {string} classNames - String containing Tailwind classes to convert
9382
+ * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
9383
+ * @returns {string|Object} Inline CSS string or style JSON object
9181
9384
  */
9182
9385
  function tws(classNames, convertToJson) {
9183
9386
  const totalMarker = performanceMonitor.start("tws:total");
9184
9387
  try {
9185
9388
  // Initialize CSS object using singleton cache
9186
9389
  const cssObject = tailwindCache.getOrGenerate(generateTailwindCssString, convertCssToObject);
9187
- if ([!classNames, typeof classNames !== "string", classNames.trim() === ""].includes(true)) {
9390
+ if (!classNames || typeof classNames !== "string" || classNames.trim() === "") {
9188
9391
  performanceMonitor.end(totalMarker);
9189
9392
  return convertToJson ? {} : "";
9190
9393
  }
@@ -9505,6 +9708,17 @@ function walkStyleTree(selector, val, styles, walk) {
9505
9708
  processNestedSelectors(nested, selector, styles, walk);
9506
9709
  } else if (typeof val === "string") {
9507
9710
  if (val.trim() === "") return;
9711
+ // Handle @css string directive: '@css { ... }'
9712
+ const trimmedVal = val.trim();
9713
+ if (trimmedVal.startsWith('@css')) {
9714
+ const cssMatch = trimmedVal.match(/^@css\s*\{([\s\S]*)\}\s*$/);
9715
+ if (cssMatch) {
9716
+ const rawCss = cssMatch[1].trim();
9717
+ styles[selector] = styles[selector] || '';
9718
+ styles[selector] += rawCss.split(';').filter(d => d.trim()).map(d => d.trim() + ';').join(' ') + '\n';
9719
+ return;
9720
+ }
9721
+ }
9508
9722
  walk(selector, [expandGroupedClass(val)]);
9509
9723
  } else if (typeof val === "object" && val !== null) {
9510
9724
  const {
@@ -9681,13 +9895,13 @@ function generateCssString(styles) {
9681
9895
  return cssString.trim();
9682
9896
  }
9683
9897
 
9684
- /**
9685
- * Generate CSS string from style object with SCSS-like syntax (NO CACHE)
9686
- * This is the original non-cached version. Use twsx() for cached version.
9687
- * Supports nested selectors, state variants, responsive variants, and @css directives
9688
- * @param {Object} obj - Object with SCSS-like style format
9689
- * @param {Object} [options] - Additional options, e.g. { inject: true/false }
9690
- * @returns {string} Generated CSS string
9898
+ /**
9899
+ * Generate CSS string from style object with SCSS-like syntax (NO CACHE)
9900
+ * This is the original non-cached version. Use twsx() for cached version.
9901
+ * Supports nested selectors, state variants, responsive variants, and @css directives
9902
+ * @param {Object} obj - Object with SCSS-like style format
9903
+ * @param {Object} [options] - Additional options, e.g. { inject: true/false }
9904
+ * @returns {string} Generated CSS string
9691
9905
  */
9692
9906
  function twsxNoCache(obj) {
9693
9907
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -9773,6 +9987,17 @@ function twsxNoCache(obj) {
9773
9987
  let baseClass = "";
9774
9988
  const nested = {};
9775
9989
  if (typeof val === "string") {
9990
+ // Handle @css string directive: '@css { ... }'
9991
+ const trimmedVal = val.trim();
9992
+ if (trimmedVal.startsWith('@css')) {
9993
+ const cssMatch = trimmedVal.match(/^@css\s*\{([\s\S]*)\}\s*$/);
9994
+ if (cssMatch) {
9995
+ const rawCss = cssMatch[1].trim();
9996
+ styles[selector] = styles[selector] || '';
9997
+ styles[selector] += rawCss.split(';').filter(d => d.trim()).map(d => d.trim() + ';').join(' ') + '\n';
9998
+ continue;
9999
+ }
10000
+ }
9776
10001
  // Check if this is a @css property value - if so, don't process through expandGroupedClass
9777
10002
  if (selector.includes(" @css ")) {
9778
10003
  // This is a CSS property value from @css flattening - keep as-is
@@ -9883,42 +10108,42 @@ function twsxNoCache(obj) {
9883
10108
  }
9884
10109
  }
9885
10110
 
9886
- /**
9887
- * Create a variant-based style generator (NO CACHE)
9888
- * This is the original non-cached version. Use twsxVariants() for cached version.
9889
- * Supports base styles, variants, compound variants, and default variants
9890
- *
9891
- * @param {Object} config - Configuration object
9892
- * @param {string} config.base - Base Tailwind classes applied to all variants
9893
- * @param {Object} config.variants - Variant definitions with their options
9894
- * @param {Array} config.compoundVariants - Compound variant rules for multi-variant combinations
9895
- * @param {Object} config.defaultVariants - Default variant values
9896
- * @returns {Function} A function that accepts variant props and returns merged classes
9897
- *
9898
- * @example
9899
- * const button = twsxVariantsNoCache({
9900
- * base: 'px-4 py-2 rounded font-medium',
9901
- * variants: {
9902
- * color: {
9903
- * primary: 'bg-blue-500 text-white',
9904
- * secondary: 'bg-gray-500 text-white'
9905
- * },
9906
- * size: {
9907
- * sm: 'text-sm',
9908
- * lg: 'text-lg'
9909
- * }
9910
- * },
9911
- * compoundVariants: [
9912
- * { color: 'primary', size: 'lg', class: 'font-bold shadow-lg' }
9913
- * ],
9914
- * defaultVariants: {
9915
- * color: 'primary',
9916
- * size: 'sm'
9917
- * }
9918
- * });
9919
- *
9920
- * // Usage:
9921
- * button({ color: 'primary', size: 'lg' }) // Returns merged classes
10111
+ /**
10112
+ * Create a variant-based style generator (NO CACHE)
10113
+ * This is the original non-cached version. Use twsxVariants() for cached version.
10114
+ * Supports base styles, variants, compound variants, and default variants
10115
+ *
10116
+ * @param {Object} config - Configuration object
10117
+ * @param {string} config.base - Base Tailwind classes applied to all variants
10118
+ * @param {Object} config.variants - Variant definitions with their options
10119
+ * @param {Array} config.compoundVariants - Compound variant rules for multi-variant combinations
10120
+ * @param {Object} config.defaultVariants - Default variant values
10121
+ * @returns {Function} A function that accepts variant props and returns merged classes
10122
+ *
10123
+ * @example
10124
+ * const button = twsxVariantsNoCache({
10125
+ * base: 'px-4 py-2 rounded font-medium',
10126
+ * variants: {
10127
+ * color: {
10128
+ * primary: 'bg-blue-500 text-white',
10129
+ * secondary: 'bg-gray-500 text-white'
10130
+ * },
10131
+ * size: {
10132
+ * sm: 'text-sm',
10133
+ * lg: 'text-lg'
10134
+ * }
10135
+ * },
10136
+ * compoundVariants: [
10137
+ * { color: 'primary', size: 'lg', class: 'font-bold shadow-lg' }
10138
+ * ],
10139
+ * defaultVariants: {
10140
+ * color: 'primary',
10141
+ * size: 'sm'
10142
+ * }
10143
+ * });
10144
+ *
10145
+ * // Usage:
10146
+ * button({ color: 'primary', size: 'lg' }) // Returns merged classes
9922
10147
  */
9923
10148
  function twsxVariantsNoCache(className) {
9924
10149
  let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -9930,11 +10155,11 @@ function twsxVariantsNoCache(className) {
9930
10155
  nested = {}
9931
10156
  } = config;
9932
10157
 
9933
- /**
9934
- * Check if a compound variant matches the current props
9935
- * @param {Object} compound - Compound variant definition
9936
- * @param {Object} props - Current variant props
9937
- * @returns {boolean}
10158
+ /**
10159
+ * Check if a compound variant matches the current props
10160
+ * @param {Object} compound - Compound variant definition
10161
+ * @param {Object} props - Current variant props
10162
+ * @returns {boolean}
9938
10163
  */
9939
10164
  function matchesCompoundVariant(compound, props) {
9940
10165
  for (const key in compound) {
@@ -9955,19 +10180,19 @@ function twsxVariantsNoCache(className) {
9955
10180
  return true;
9956
10181
  }
9957
10182
 
9958
- /**
9959
- * Merge multiple class strings with conflict resolution
9960
- * Later classes override earlier ones for the same CSS property
9961
- * @param {...string} classStrings - Class strings to merge
9962
- * @returns {string}
10183
+ /**
10184
+ * Merge multiple class strings with conflict resolution
10185
+ * Later classes override earlier ones for the same CSS property
10186
+ * @param {...string} classStrings - Class strings to merge
10187
+ * @returns {string}
9963
10188
  */
9964
10189
  function mergeClasses() {
9965
10190
  const result = [];
9966
10191
  const propertyMap = new Map(); // Track index by conflict key
9967
10192
 
9968
- /**
9969
- * Extract conflict key from Tailwind class
9970
- * Classes that affect the same CSS property should have the same key
10193
+ /**
10194
+ * Extract conflict key from Tailwind class
10195
+ * Classes that affect the same CSS property should have the same key
9971
10196
  */
9972
10197
  function getConflictKey(cls) {
9973
10198
  // Extract variant prefix (hover:, focus:, etc.)
@@ -10199,10 +10424,10 @@ function twsxVariantsNoCache(className) {
10199
10424
  return result.filter(Boolean).join(" ");
10200
10425
  }
10201
10426
 
10202
- /**
10203
- * Generate classes for a specific variant combination
10204
- * @param {Object} props - Variant props
10205
- * @returns {string} Merged Tailwind classes
10427
+ /**
10428
+ * Generate classes for a specific variant combination
10429
+ * @param {Object} props - Variant props
10430
+ * @returns {string} Merged Tailwind classes
10206
10431
  */
10207
10432
  function generateClasses() {
10208
10433
  let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -10318,10 +10543,10 @@ function twsxVariantsNoCache(className) {
10318
10543
  }
10319
10544
  }
10320
10545
 
10321
- /**
10322
- * Build class name string from props (for component usage)
10323
- * @param {Object} props - Variant props
10324
- * @returns {string} Class name string like "alert alert-warning" or "btn btn-outline-danger"
10546
+ /**
10547
+ * Build class name string from props (for component usage)
10548
+ * @param {Object} props - Variant props
10549
+ * @returns {string} Class name string like "alert alert-warning" or "btn btn-outline-danger"
10325
10550
  */
10326
10551
  function buildClassName() {
10327
10552
  let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -10363,10 +10588,10 @@ function twsxVariantsNoCache(className) {
10363
10588
  // FAST HASHING UTILITIES FOR OBJECT CACHING
10364
10589
  // ============================================================================
10365
10590
 
10366
- /**
10367
- * FNV-1a hash algorithm - Fast and good distribution for strings
10368
- * @param {string} str - String to hash
10369
- * @returns {number} 32-bit hash
10591
+ /**
10592
+ * FNV-1a hash algorithm - Fast and good distribution for strings
10593
+ * @param {string} str - String to hash
10594
+ * @returns {number} 32-bit hash
10370
10595
  */
10371
10596
  function hashString(str) {
10372
10597
  let hash = 2166136261; // FNV offset basis
@@ -10377,16 +10602,16 @@ function hashString(str) {
10377
10602
  return hash >>> 0; // Convert to unsigned 32-bit
10378
10603
  }
10379
10604
 
10380
- /**
10381
- * Fast deep hash for objects - Optimized for style objects
10382
- * Strategy:
10383
- * 1. Use object identity (WeakMap) for exact same object references
10384
- * 2. For different objects, create stable hash from sorted keys + values
10385
- * 3. Cache hash per object to avoid recomputation
10386
- *
10387
- * @param {any} obj - Object to hash
10388
- * @param {Object} options - Additional options to include in hash
10389
- * @returns {string} Hash key for caching
10605
+ /**
10606
+ * Fast deep hash for objects - Optimized for style objects
10607
+ * Strategy:
10608
+ * 1. Use object identity (WeakMap) for exact same object references
10609
+ * 2. For different objects, create stable hash from sorted keys + values
10610
+ * 3. Cache hash per object to avoid recomputation
10611
+ *
10612
+ * @param {any} obj - Object to hash
10613
+ * @param {Object} options - Additional options to include in hash
10614
+ * @returns {string} Hash key for caching
10390
10615
  */
10391
10616
  function fastObjectHash(obj) {
10392
10617
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -10431,22 +10656,22 @@ function fastObjectHash(obj) {
10431
10656
  return options && Object.keys(options).length > 0 ? `${hashKey}:${JSON.stringify(options)}` : hashKey;
10432
10657
  }
10433
10658
 
10434
- /**
10435
- * 🚀 CACHED VERSION OF TWSX (DEFAULT)
10436
- * Generate CSS string with input-level caching for repeated calls
10437
- * Uses fast FNV-1a hash + WeakMap for 10-100x performance improvement
10438
- *
10439
- * @param {Object} obj - Object with SCSS-like style format
10440
- * @param {Object} [options] - Additional options, e.g. { inject: true/false }
10441
- * @returns {string} Generated CSS string
10442
- *
10443
- * @example
10444
- * const styles = { '.btn': 'bg-blue-500 text-white p-4' };
10445
- * twsx(styles); // First call: ~40ms
10446
- * twsx(styles); // Cached: ~0.1ms (100x faster!)
10447
- *
10448
- * // For non-cached version (rare case):
10449
- * twsxNoCache(styles);
10659
+ /**
10660
+ * 🚀 CACHED VERSION OF TWSX (DEFAULT)
10661
+ * Generate CSS string with input-level caching for repeated calls
10662
+ * Uses fast FNV-1a hash + WeakMap for 10-100x performance improvement
10663
+ *
10664
+ * @param {Object} obj - Object with SCSS-like style format
10665
+ * @param {Object} [options] - Additional options, e.g. { inject: true/false }
10666
+ * @returns {string} Generated CSS string
10667
+ *
10668
+ * @example
10669
+ * const styles = { '.btn': 'bg-blue-500 text-white p-4' };
10670
+ * twsx(styles); // First call: ~40ms
10671
+ * twsx(styles); // Cached: ~0.1ms (100x faster!)
10672
+ *
10673
+ * // For non-cached version (rare case):
10674
+ * twsxNoCache(styles);
10450
10675
  */
10451
10676
  function twsx(obj) {
10452
10677
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -10461,7 +10686,7 @@ function twsx(obj) {
10461
10686
  const {
10462
10687
  inject = true
10463
10688
  } = options;
10464
- if (inject && typeof window !== "undefined" && typeof document !== "undefined") {
10689
+ if (inject && (IS_BROWSER || _ssrCollecting)) {
10465
10690
  autoInjectCss(cached);
10466
10691
  }
10467
10692
  return cached;
@@ -10470,27 +10695,28 @@ function twsx(obj) {
10470
10695
  // Cache miss: call original twsxNoCache and cache result
10471
10696
  const result = twsxNoCache(obj, options);
10472
10697
  _twsxInputCache.set(cacheKey, result);
10698
+ evictMap(_twsxInputCache);
10473
10699
  return result;
10474
10700
  }
10475
10701
 
10476
- /**
10477
- * 🚀 CACHED VERSION OF TWSXVARIANTS (DEFAULT)
10478
- * Create variant-based style generator with config-level caching
10479
- * Uses fast FNV-1a hash for 10-100x performance improvement
10480
- *
10481
- * @param {string} className - Base class name
10482
- * @param {Object} config - Configuration object (base, variants, compoundVariants, etc.)
10483
- * @returns {Function} A function that accepts variant props and returns merged classes
10484
- *
10485
- * @example
10486
- * const button = twsxVariants('btn', {
10487
- * variants: { color: { primary: {...}, secondary: {...} } }
10488
- * });
10489
- * // First call: ~50ms to generate function
10490
- * // Subsequent calls with same config: ~0.1ms (returns cached function)
10491
- *
10492
- * // For non-cached version (rare case):
10493
- * twsxVariantsNoCache('btn', config);
10702
+ /**
10703
+ * 🚀 CACHED VERSION OF TWSXVARIANTS (DEFAULT)
10704
+ * Create variant-based style generator with config-level caching
10705
+ * Uses fast FNV-1a hash for 10-100x performance improvement
10706
+ *
10707
+ * @param {string} className - Base class name
10708
+ * @param {Object} config - Configuration object (base, variants, compoundVariants, etc.)
10709
+ * @returns {Function} A function that accepts variant props and returns merged classes
10710
+ *
10711
+ * @example
10712
+ * const button = twsxVariants('btn', {
10713
+ * variants: { color: { primary: {...}, secondary: {...} } }
10714
+ * });
10715
+ * // First call: ~50ms to generate function
10716
+ * // Subsequent calls with same config: ~0.1ms (returns cached function)
10717
+ *
10718
+ * // For non-cached version (rare case):
10719
+ * twsxVariantsNoCache('btn', config);
10494
10720
  */
10495
10721
  function twsxVariants(className) {
10496
10722
  let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -10505,6 +10731,7 @@ function twsxVariants(className) {
10505
10731
  // Cache miss: call original twsxVariantsNoCache and cache result
10506
10732
  const result = twsxVariantsNoCache(className, config);
10507
10733
  _twsxVariantsResultCache.set(cacheKey, result);
10734
+ evictMap(_twsxVariantsResultCache);
10508
10735
  return result;
10509
10736
  }
10510
10737
 
@@ -10522,25 +10749,64 @@ function getCssHash(str) {
10522
10749
  return hash;
10523
10750
  }
10524
10751
 
10525
- // Enhanced auto-inject CSS with performance monitoring
10752
+ // Enhanced auto-inject CSS with performance monitoring & SSR support
10526
10753
  const injectedCssHashSet = new Set();
10527
10754
  function autoInjectCss(cssString) {
10528
10755
  const marker = performanceMonitor.start("css:inject");
10529
10756
  try {
10530
- if (typeof window !== "undefined" && typeof document !== "undefined") {
10757
+ // SSR mode: collect CSS strings instead of DOM injection
10758
+ if (_ssrCollecting) {
10759
+ const cssHash = getCssHash(cssString);
10760
+ if (injectedCssHashSet.has(cssHash)) {
10761
+ performanceMonitor.end(marker);
10762
+ return;
10763
+ }
10764
+ injectedCssHashSet.add(cssHash);
10765
+ evictSet(injectedCssHashSet);
10766
+ _ssrCollectedCss.push(cssString);
10767
+ performanceMonitor.end(marker);
10768
+ return;
10769
+ }
10770
+ if (IS_BROWSER) {
10531
10771
  const cssHash = getCssHash(cssString);
10532
10772
  if (injectedCssHashSet.has(cssHash)) {
10533
10773
  performanceMonitor.end(marker);
10534
10774
  return;
10535
10775
  }
10536
10776
  injectedCssHashSet.add(cssHash);
10777
+ evictSet(injectedCssHashSet);
10537
10778
  let styleTag = document.getElementById("twsx-auto-style");
10538
10779
  if (!styleTag) {
10539
10780
  styleTag = document.createElement("style");
10540
10781
  styleTag.id = "twsx-auto-style";
10782
+ styleTag.setAttribute("data-twsx", "");
10541
10783
  document.head.appendChild(styleTag);
10542
10784
  }
10543
- styleTag.textContent += `\n${cssString}`;
10785
+
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
+ }
10544
10810
 
10545
10811
  // Log injection stats periodically
10546
10812
  if (injectedCssHashSet.size % 10 === 0) {
@@ -10555,19 +10821,19 @@ function autoInjectCss(cssString) {
10555
10821
  }
10556
10822
 
10557
10823
  // Enhanced debounced functions with performance monitoring configuration
10558
- /**
10559
- * Debounced version of tws function with performance monitoring
10560
- * @param {string} classNames - String containing Tailwind classes to convert
10561
- * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
10562
- * @returns {string|Object} Inline CSS string or style JSON object
10824
+ /**
10825
+ * Debounced version of tws function with performance monitoring
10826
+ * @param {string} classNames - String containing Tailwind classes to convert
10827
+ * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
10828
+ * @returns {string|Object} Inline CSS string or style JSON object
10563
10829
  */
10564
10830
  const debouncedTws = debounce(tws, 50); // Faster debounce for tws
10565
10831
 
10566
- /**
10567
- * Debounced version of twsx function with performance monitoring
10568
- * @param {Object} obj - Object with SCSS-like style format
10569
- * @param {Object} [options] - Additional options
10570
- * @returns {string} Generated CSS string
10832
+ /**
10833
+ * Debounced version of twsx function with performance monitoring
10834
+ * @param {Object} obj - Object with SCSS-like style format
10835
+ * @param {Object} [options] - Additional options
10836
+ * @returns {string} Generated CSS string
10571
10837
  */
10572
10838
  const debouncedTwsx = debounce(twsx, 100); // Standard debounce for twsx
10573
10839
 
@@ -10610,6 +10876,5 @@ const performanceUtils = {
10610
10876
  }
10611
10877
  };
10612
10878
 
10613
- // End of exports - v3.0.0 core only (tws, twsx, twsxVariants, configure)
10614
-
10615
- export { DYNAMIC_TEMPLATES, INLINE_ANIMATIONS, LRUCache, Logger, TwsError, animateElement, applyDynamicAnimation, applyInlineAnimation, applyWebAnimation, chainAnimations, clearConfigCache, configure, createDynamicAnimation, createPlugin, createTemplateAnimation, createUtilityPlugin, createVariantPlugin, debouncedTws, debouncedTwsx, getConfig, getTailwindCache, handleError, initWebAnimations, logger, onError, performanceUtils, resetConfig, resetTailwindCache, staggerAnimations, tws, twsx, twsxVariants };
10879
+ export { DYNAMIC_TEMPLATES, INLINE_ANIMATIONS, IS_BROWSER, IS_SERVER, LRUCache, Logger, TwsError, animateElement, applyDynamicAnimation, applyInlineAnimation, applyWebAnimation, chainAnimations, clearConfigCache, configure, createDynamicAnimation, createPlugin, createTemplateAnimation, createUtilityPlugin, createVariantPlugin, cx, debouncedTws, debouncedTwsx, getConfig, getSSRStyles, getTailwindCache, handleError, initWebAnimations, logger, onError, performanceUtils, resetConfig, resetTailwindCache, staggerAnimations, startSSR, stopSSR, tws, twsx, twsxVariants };
10880
+ //# sourceMappingURL=index.esm.js.map