tailwind-to-style 3.0.0 → 3.1.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/README.md CHANGED
@@ -156,6 +156,80 @@ const formatted = twsx(styles, { format: 'pretty' })
156
156
  - `'.nested'` - Descendants
157
157
  - `'@media ...'` - Media queries (root level only)
158
158
 
159
+ ### `twsxVariants(className, config)`
160
+
161
+ Create variant-based component styles with automatic CSS generation. Similar to `tailwind-variants` but with auto-injection.
162
+
163
+ ```javascript
164
+ import { twsxVariants } from 'tailwind-to-style'
165
+
166
+ const btn = twsxVariants('.btn', {
167
+ base: 'px-4 py-2 rounded-lg font-medium transition-all',
168
+ variants: {
169
+ variant: {
170
+ solid: 'border-transparent',
171
+ outline: 'bg-transparent border-2',
172
+ ghost: 'bg-transparent',
173
+ },
174
+ color: {
175
+ primary: 'bg-blue-500 text-white hover:bg-blue-600',
176
+ danger: 'bg-red-500 text-white hover:bg-red-600',
177
+ },
178
+ size: {
179
+ sm: 'px-3 py-1.5 text-sm',
180
+ md: 'px-4 py-2 text-base',
181
+ lg: 'px-6 py-3 text-lg',
182
+ },
183
+ },
184
+ compoundVariants: [
185
+ { variant: 'outline', color: 'primary', class: 'border-blue-500 text-blue-600' },
186
+ { variant: 'outline', color: 'danger', class: 'border-red-500 text-red-600' },
187
+ ],
188
+ defaultVariants: { variant: 'solid', color: 'primary', size: 'md' }
189
+ })
190
+
191
+ // Usage - returns class name string
192
+ btn() // "btn"
193
+ btn({ color: 'danger' }) // "btn btn-danger"
194
+ btn({ variant: 'outline', size: 'lg' }) // "btn btn-outline-lg"
195
+
196
+ // In React
197
+ const Button = ({ variant, color, size, children, ...props }) => (
198
+ <button className={btn({ variant, color, size })} {...props}>
199
+ {children}
200
+ </button>
201
+ )
202
+ ```
203
+
204
+ **Nested Selectors** - Style child elements:
205
+
206
+ ```javascript
207
+ const alert = twsxVariants('.alert', {
208
+ base: 'p-4 rounded-lg border flex gap-3',
209
+ variants: {
210
+ status: {
211
+ info: 'bg-blue-50 text-blue-800',
212
+ error: 'bg-red-50 text-red-800',
213
+ },
214
+ },
215
+ defaultVariants: { status: 'info' },
216
+ nested: {
217
+ '.alert-icon': 'flex-shrink-0 mt-0.5',
218
+ '.alert-content': 'flex-1',
219
+ '.alert-dismiss': 'p-1 rounded hover:bg-black/10',
220
+ }
221
+ })
222
+
223
+ // Generates CSS:
224
+ // .alert .alert-icon { ... }
225
+ // .alert .alert-content { ... }
226
+ ```
227
+
228
+ **Class Naming Convention:**
229
+ - `.btn` = all defaults
230
+ - `.btn-outline` = outline variant (non-default)
231
+ - `.btn-outline-danger-lg` = multiple non-defaults
232
+
159
233
  ### `configure(config)`
160
234
 
161
235
  Customize theme with your colors, spacing, fonts, and more.
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tailwind-to-style v3.0.0
2
+ * tailwind-to-style v3.1.0
3
3
  * Runtime Tailwind CSS to inline styles converter
4
4
  * Core only: tws, twsx, configure
5
5
  *
@@ -8586,10 +8586,10 @@ function parseCustomClassWithPatterns(className) {
8586
8586
  return null;
8587
8587
  }
8588
8588
 
8589
- /**
8590
- * Resolve all CSS custom properties (var) in a CSS string and output only clear CSS (no custom property)
8591
- * @param {string} cssString
8592
- * @returns {string} e.g. 'color: rgba(255,255,255,1); background: #fff;'
8589
+ /**
8590
+ * Resolve all CSS custom properties (var) in a CSS string and output only clear CSS (no custom property)
8591
+ * @param {string} cssString
8592
+ * @returns {string} e.g. 'color: rgba(255,255,255,1); background: #fff;'
8593
8593
  */
8594
8594
  function resolveCssToClearCss(cssString) {
8595
8595
  const customVars = {};
@@ -8623,9 +8623,9 @@ function resolveCssToClearCss(cssString) {
8623
8623
  // Cache for getConfigOptions - use LRU cache
8624
8624
  const configOptionsCache = new LRUCache(500);
8625
8625
 
8626
- /**
8627
- * Clear config options cache (internal use)
8628
- * Called when user configuration changes
8626
+ /**
8627
+ * Clear config options cache (internal use)
8628
+ * Called when user configuration changes
8629
8629
  */
8630
8630
  function clearConfigCache() {
8631
8631
  configOptionsCache.clear();
@@ -8997,11 +8997,11 @@ function separateAndResolveCSS(arr) {
8997
8997
  }
8998
8998
  }
8999
8999
 
9000
- /**
9001
- * Process opacity modifier from class name (e.g., text-red-500/50 -> 50% opacity)
9002
- * @param {string} className - Class name with potential opacity modifier
9003
- * @param {string} cssDeclaration - CSS declaration to modify
9004
- * @returns {string} Modified CSS declaration with opacity applied
9000
+ /**
9001
+ * Process opacity modifier from class name (e.g., text-red-500/50 -> 50% opacity)
9002
+ * @param {string} className - Class name with potential opacity modifier
9003
+ * @param {string} cssDeclaration - CSS declaration to modify
9004
+ * @returns {string} Modified CSS declaration with opacity applied
9005
9005
  */
9006
9006
  function processOpacityModifier(className, cssDeclaration) {
9007
9007
  const opacityMatch = className.match(/\/(\d+)$/);
@@ -9062,11 +9062,11 @@ function processOpacityModifier(className, cssDeclaration) {
9062
9062
  return modifiedDeclaration;
9063
9063
  }
9064
9064
 
9065
- /**
9066
- * Convert Tailwind class string to inline CSS styles or JSON object
9067
- * @param {string} classNames - String containing Tailwind classes to convert
9068
- * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
9069
- * @returns {string|Object} Inline CSS string or style JSON object
9065
+ /**
9066
+ * Convert Tailwind class string to inline CSS styles or JSON object
9067
+ * @param {string} classNames - String containing Tailwind classes to convert
9068
+ * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
9069
+ * @returns {string|Object} Inline CSS string or style JSON object
9070
9070
  */
9071
9071
  function tws(classNames, convertToJson) {
9072
9072
  const totalMarker = performanceMonitor.start("tws:total");
@@ -9565,12 +9565,12 @@ function generateCssString(styles) {
9565
9565
  return cssString.trim();
9566
9566
  }
9567
9567
 
9568
- /**
9569
- * Generate CSS string from style object with SCSS-like syntax
9570
- * Supports nested selectors, state variants, responsive variants, and @css directives
9571
- * @param {Object} obj - Object with SCSS-like style format
9572
- * @param {Object} [options] - Additional options, e.g. { inject: true/false }
9573
- * @returns {string} Generated CSS string
9568
+ /**
9569
+ * Generate CSS string from style object with SCSS-like syntax
9570
+ * Supports nested selectors, state variants, responsive variants, and @css directives
9571
+ * @param {Object} obj - Object with SCSS-like style format
9572
+ * @param {Object} [options] - Additional options, e.g. { inject: true/false }
9573
+ * @returns {string} Generated CSS string
9574
9574
  */
9575
9575
  function twsx(obj) {
9576
9576
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -9766,6 +9766,481 @@ function twsx(obj) {
9766
9766
  }
9767
9767
  }
9768
9768
 
9769
+ /**
9770
+ * Create a variant-based style generator (similar to tailwind-variants)
9771
+ * Supports base styles, variants, compound variants, and default variants
9772
+ *
9773
+ * @param {Object} config - Configuration object
9774
+ * @param {string} config.base - Base Tailwind classes applied to all variants
9775
+ * @param {Object} config.variants - Variant definitions with their options
9776
+ * @param {Array} config.compoundVariants - Compound variant rules for multi-variant combinations
9777
+ * @param {Object} config.defaultVariants - Default variant values
9778
+ * @returns {Function} A function that accepts variant props and returns merged classes
9779
+ *
9780
+ * @example
9781
+ * const button = twsxVariants({
9782
+ * base: 'px-4 py-2 rounded font-medium',
9783
+ * variants: {
9784
+ * color: {
9785
+ * primary: 'bg-blue-500 text-white',
9786
+ * secondary: 'bg-gray-500 text-white'
9787
+ * },
9788
+ * size: {
9789
+ * sm: 'text-sm',
9790
+ * lg: 'text-lg'
9791
+ * }
9792
+ * },
9793
+ * compoundVariants: [
9794
+ * { color: 'primary', size: 'lg', class: 'font-bold shadow-lg' }
9795
+ * ],
9796
+ * defaultVariants: {
9797
+ * color: 'primary',
9798
+ * size: 'sm'
9799
+ * }
9800
+ * });
9801
+ *
9802
+ * // Usage:
9803
+ * button({ color: 'primary', size: 'lg' }) // Returns merged classes
9804
+ */
9805
+ function twsxVariants(className) {
9806
+ let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
9807
+ const {
9808
+ base = "",
9809
+ variants = {},
9810
+ compoundVariants = [],
9811
+ defaultVariants = {},
9812
+ nested = {}
9813
+ } = config;
9814
+
9815
+ /**
9816
+ * Check if a compound variant matches the current props
9817
+ * @param {Object} compound - Compound variant definition
9818
+ * @param {Object} props - Current variant props
9819
+ * @returns {boolean}
9820
+ */
9821
+ function matchesCompoundVariant(compound, props) {
9822
+ for (const key in compound) {
9823
+ if (key === "class" || key === "className") continue;
9824
+ const compoundValue = compound[key];
9825
+ const propValue = props[key];
9826
+
9827
+ // Handle array values in compound (match any)
9828
+ if (Array.isArray(compoundValue)) {
9829
+ if (!compoundValue.includes(propValue)) return false;
9830
+ } else {
9831
+ // Handle boolean variants - convert string 'true'/'false' to boolean
9832
+ const normalizedCompound = compoundValue === "true" ? true : compoundValue === "false" ? false : compoundValue;
9833
+ const normalizedProp = propValue === "true" ? true : propValue === "false" ? false : propValue;
9834
+ if (normalizedCompound !== normalizedProp) return false;
9835
+ }
9836
+ }
9837
+ return true;
9838
+ }
9839
+
9840
+ /**
9841
+ * Merge multiple class strings with conflict resolution
9842
+ * Later classes override earlier ones for the same CSS property
9843
+ * @param {...string} classStrings - Class strings to merge
9844
+ * @returns {string}
9845
+ */
9846
+ function mergeClasses() {
9847
+ const result = [];
9848
+ const propertyMap = new Map(); // Track index by conflict key
9849
+
9850
+ /**
9851
+ * Extract conflict key from Tailwind class
9852
+ * Classes that affect the same CSS property should have the same key
9853
+ */
9854
+ function getConflictKey(cls) {
9855
+ // Extract variant prefix (hover:, focus:, etc.)
9856
+ const variantMatch = cls.match(/^((?:[a-z-]+:)*)/);
9857
+ const variant = variantMatch ? variantMatch[1] : "";
9858
+ const baseClass = cls.slice(variant.length);
9859
+
9860
+ // Check if it's a prefixed standalone (e.g., bg-transparent, text-transparent)
9861
+ const prefixedStandalone = baseClass.match(/^([a-z]+)-(transparent|current|inherit|auto)$/);
9862
+ if (prefixedStandalone) {
9863
+ const prefix = prefixedStandalone[1];
9864
+ return variant + prefix;
9865
+ }
9866
+
9867
+ // Handle standalone utilities (no prefix)
9868
+ const standaloneGroups = {
9869
+ "inline-flex": "display",
9870
+ "inline-block": "display",
9871
+ inline: "display",
9872
+ block: "display",
9873
+ flex: "display",
9874
+ grid: "display",
9875
+ hidden: "display",
9876
+ static: "position",
9877
+ fixed: "position",
9878
+ absolute: "position",
9879
+ relative: "position",
9880
+ sticky: "position",
9881
+ visible: "visibility",
9882
+ invisible: "visibility",
9883
+ underline: "text-decoration",
9884
+ "line-through": "text-decoration",
9885
+ "no-underline": "text-decoration",
9886
+ uppercase: "text-transform",
9887
+ lowercase: "text-transform",
9888
+ capitalize: "text-transform",
9889
+ "normal-case": "text-transform",
9890
+ truncate: "text-overflow",
9891
+ italic: "font-style",
9892
+ "not-italic": "font-style",
9893
+ antialiased: "font-smoothing",
9894
+ "subpixel-antialiased": "font-smoothing"
9895
+ };
9896
+ if (standaloneGroups[baseClass]) {
9897
+ return variant + standaloneGroups[baseClass];
9898
+ }
9899
+
9900
+ // Match pattern: prefix-value or prefix-modifier-value
9901
+ // bg-blue-500, text-xl, px-4, rounded-lg, shadow-md, border-2, etc.
9902
+ const match = baseClass.match(/^([a-z]+)-(.+)$/);
9903
+ if (match) {
9904
+ const prefix = match[1];
9905
+ const value = match[2];
9906
+
9907
+ // Group related prefixes
9908
+ const prefixGroups = {
9909
+ // Background
9910
+ bg: "bg",
9911
+ // Text color vs text size
9912
+ text: /^(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/.test(value) ? "text-size" : "text-color",
9913
+ // Padding
9914
+ p: "p",
9915
+ px: "px",
9916
+ py: "py",
9917
+ pt: "pt",
9918
+ pb: "pb",
9919
+ pl: "pl",
9920
+ pr: "pr",
9921
+ // Margin
9922
+ m: "m",
9923
+ mx: "mx",
9924
+ my: "my",
9925
+ mt: "mt",
9926
+ mb: "mb",
9927
+ ml: "ml",
9928
+ mr: "mr",
9929
+ // Sizing
9930
+ w: "w",
9931
+ h: "h",
9932
+ min: baseClass.startsWith("min-w") ? "min-w" : "min-h",
9933
+ max: baseClass.startsWith("max-w") ? "max-w" : "max-h",
9934
+ // Border width vs color
9935
+ border: /^(\d|t-|b-|l-|r-|x-|y-)/.test(value) ? "border-width" : /^(solid|dashed|dotted|double|none|hidden)$/.test(value) ? "border-style" : "border-color",
9936
+ rounded: "rounded",
9937
+ // Effects
9938
+ shadow: "shadow",
9939
+ opacity: "opacity",
9940
+ // Typography
9941
+ font: /^(sans|serif|mono)$/.test(value) ? "font-family" : "font-weight",
9942
+ leading: "leading",
9943
+ tracking: "tracking",
9944
+ // Layout
9945
+ flex: "flex",
9946
+ grid: "grid",
9947
+ gap: "gap",
9948
+ justify: "justify",
9949
+ items: "items",
9950
+ self: "self",
9951
+ place: "place",
9952
+ // Position
9953
+ top: "top",
9954
+ bottom: "bottom",
9955
+ left: "left",
9956
+ right: "right",
9957
+ inset: "inset",
9958
+ z: "z",
9959
+ // Cursor
9960
+ cursor: "cursor",
9961
+ // Pointer events
9962
+ pointer: "pointer-events",
9963
+ // Transform
9964
+ scale: "scale",
9965
+ rotate: "rotate",
9966
+ translate: "translate",
9967
+ skew: "skew",
9968
+ // Transition
9969
+ transition: "transition",
9970
+ duration: "duration",
9971
+ ease: "ease",
9972
+ delay: "delay",
9973
+ // Ring
9974
+ ring: /^(\d|inset)/.test(value) ? "ring-width" : /^offset/.test(value) ? "ring-offset" : "ring-color",
9975
+ // Outline
9976
+ outline: /^(\d|none)/.test(value) ? "outline-width" : /^(dashed|dotted|double|solid)$/.test(value) ? "outline-style" : /^offset/.test(value) ? "outline-offset" : "outline-color",
9977
+ // Overflow
9978
+ overflow: "overflow",
9979
+ // Object
9980
+ object: "object",
9981
+ // Aspect
9982
+ aspect: "aspect",
9983
+ // Underline
9984
+ underline: "underline-offset",
9985
+ // Decoration
9986
+ decoration: "decoration",
9987
+ // Accent
9988
+ accent: "accent",
9989
+ // Caret
9990
+ caret: "caret",
9991
+ // Fill & Stroke
9992
+ fill: "fill",
9993
+ stroke: /^\d/.test(value) ? "stroke-width" : "stroke-color",
9994
+ // Backdrop
9995
+ backdrop: "backdrop",
9996
+ // Columns
9997
+ columns: "columns",
9998
+ col: "col",
9999
+ row: "row",
10000
+ // Order
10001
+ order: "order",
10002
+ // Grow/Shrink
10003
+ grow: "grow",
10004
+ shrink: "shrink",
10005
+ basis: "basis",
10006
+ // Content
10007
+ content: "content",
10008
+ // List
10009
+ list: "list",
10010
+ // Scroll
10011
+ scroll: "scroll",
10012
+ // Snap
10013
+ snap: "snap",
10014
+ // Touch
10015
+ touch: "touch",
10016
+ // Select
10017
+ select: "select",
10018
+ // Will change
10019
+ will: "will-change",
10020
+ // Appearance
10021
+ appearance: "appearance",
10022
+ // Break
10023
+ break: "break",
10024
+ // Hyphens
10025
+ hyphens: "hyphens",
10026
+ // Whitespace
10027
+ whitespace: "whitespace",
10028
+ // Word break
10029
+ word: "word-break",
10030
+ // Overscroll
10031
+ overscroll: "overscroll",
10032
+ // Resize
10033
+ resize: "resize",
10034
+ // Float
10035
+ float: "float",
10036
+ // Clear
10037
+ clear: "clear",
10038
+ // Isolation
10039
+ isolation: "isolation",
10040
+ // Mix blend
10041
+ mix: "mix-blend",
10042
+ // Background blend
10043
+ bg: "bg",
10044
+ // Filter
10045
+ filter: "filter",
10046
+ blur: "blur",
10047
+ brightness: "brightness",
10048
+ contrast: "contrast",
10049
+ grayscale: "grayscale",
10050
+ hue: "hue-rotate",
10051
+ invert: "invert",
10052
+ saturate: "saturate",
10053
+ sepia: "sepia",
10054
+ drop: "drop-shadow"
10055
+ };
10056
+ const group = prefixGroups[prefix] || prefix;
10057
+ return variant + group;
10058
+ }
10059
+ return null; // No conflict tracking
10060
+ }
10061
+ for (var _len2 = arguments.length, classStrings = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
10062
+ classStrings[_key2] = arguments[_key2];
10063
+ }
10064
+ for (const str of classStrings) {
10065
+ if (!str) continue;
10066
+ const classes = str.trim().split(/\s+/);
10067
+ for (const cls of classes) {
10068
+ if (!cls) continue;
10069
+ const conflictKey = getConflictKey(cls);
10070
+ if (conflictKey) {
10071
+ // Remove previous class with same conflict key
10072
+ const prevIndex = propertyMap.get(conflictKey);
10073
+ if (prevIndex !== undefined) {
10074
+ result[prevIndex] = null; // Mark for removal
10075
+ }
10076
+ propertyMap.set(conflictKey, result.length);
10077
+ }
10078
+ result.push(cls);
10079
+ }
10080
+ }
10081
+ return result.filter(Boolean).join(" ");
10082
+ }
10083
+
10084
+ /**
10085
+ * Generate classes for a specific variant combination
10086
+ * @param {Object} props - Variant props
10087
+ * @returns {string} Merged Tailwind classes
10088
+ */
10089
+ function generateClasses() {
10090
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10091
+ // Merge props with defaults
10092
+ const mergedProps = {
10093
+ ...defaultVariants,
10094
+ ...props
10095
+ };
10096
+
10097
+ // Start with base classes
10098
+ const classes = [base];
10099
+
10100
+ // Add variant classes
10101
+ for (const variantKey in variants) {
10102
+ const variantValue = mergedProps[variantKey];
10103
+ const variantOptions = variants[variantKey];
10104
+ if (variantValue !== undefined && variantOptions[variantValue]) {
10105
+ classes.push(variantOptions[variantValue]);
10106
+ }
10107
+ }
10108
+
10109
+ // Add compound variant classes
10110
+ for (const compound of compoundVariants) {
10111
+ if (matchesCompoundVariant(compound, mergedProps)) {
10112
+ classes.push(compound.class || compound.className || "");
10113
+ }
10114
+ }
10115
+
10116
+ // Merge all classes
10117
+ return mergeClasses(...classes);
10118
+ }
10119
+
10120
+ // If className is provided, auto-generate and inject CSS
10121
+ if (className) {
10122
+ const baseClassName = className.startsWith(".") ? className.slice(1) : className;
10123
+ const baseCss = {};
10124
+ const variantCss = {};
10125
+
10126
+ // Get all variant keys and their options
10127
+ const variantKeys = Object.keys(variants);
10128
+
10129
+ // Helper to generate all combinations
10130
+ function generateCombinations(keys) {
10131
+ let current = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
10132
+ if (keys.length === 0) {
10133
+ // Generate class name from current combination
10134
+ const parts = [baseClassName];
10135
+ let isBaseOnly = true;
10136
+ for (const key of variantKeys) {
10137
+ if (current[key] !== undefined && current[key] !== defaultVariants[key]) {
10138
+ isBaseOnly = false;
10139
+ // Skip boolean 'false' values
10140
+ if (current[key] === false || current[key] === "false") continue;
10141
+ // For boolean 'true', just add the key name
10142
+ if (current[key] === true || current[key] === "true") {
10143
+ parts.push(key);
10144
+ } else {
10145
+ parts.push(current[key]);
10146
+ }
10147
+ }
10148
+ }
10149
+ const selector = "." + parts.join("-");
10150
+ const classes = generateClasses(current);
10151
+
10152
+ // Separate base class from variant classes
10153
+ if (isBaseOnly) {
10154
+ baseCss[selector] = classes;
10155
+ } else {
10156
+ variantCss[selector] = classes;
10157
+ }
10158
+ return;
10159
+ }
10160
+ const [firstKey, ...restKeys] = keys;
10161
+ const options = variants[firstKey];
10162
+
10163
+ // If this variant has no default, also generate without it (undefined)
10164
+ const hasDefault = defaultVariants[firstKey] !== undefined;
10165
+ if (!hasDefault) {
10166
+ // Generate combinations without this variant
10167
+ generateCombinations(restKeys, current);
10168
+ }
10169
+ for (const optionValue of Object.keys(options)) {
10170
+ generateCombinations(restKeys, {
10171
+ ...current,
10172
+ [firstKey]: optionValue
10173
+ });
10174
+ }
10175
+ }
10176
+
10177
+ // Generate all combinations
10178
+ generateCombinations(variantKeys);
10179
+
10180
+ // Inject base CSS first, then variant CSS (order matters for specificity)
10181
+ twsx(baseCss);
10182
+ twsx(variantCss);
10183
+
10184
+ // Handle nested selectors - generate CSS for child elements
10185
+ if (Object.keys(nested).length > 0) {
10186
+ const nestedCss = {};
10187
+ for (const [nestedSelector, nestedClasses] of Object.entries(nested)) {
10188
+ // Build full selector: .alert .icon or .alert.icon (if starts with &)
10189
+ let fullSelector;
10190
+ if (nestedSelector.startsWith("&")) {
10191
+ // &.active -> .alert.active (attached to parent)
10192
+ fullSelector = className + nestedSelector.slice(1);
10193
+ } else {
10194
+ // .icon -> .alert .icon (descendant)
10195
+ fullSelector = className + " " + nestedSelector;
10196
+ }
10197
+ nestedCss[fullSelector] = nestedClasses;
10198
+ }
10199
+ twsx(nestedCss);
10200
+ }
10201
+ }
10202
+
10203
+ /**
10204
+ * Build class name string from props (for component usage)
10205
+ * @param {Object} props - Variant props
10206
+ * @returns {string} Class name string like "alert alert-warning" or "btn btn-outline-danger"
10207
+ */
10208
+ function buildClassName() {
10209
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10210
+ if (!className) {
10211
+ // No className defined, return generated classes directly
10212
+ return generateClasses(props);
10213
+ }
10214
+ const baseClass = className.startsWith(".") ? className.slice(1) : className;
10215
+ const variantParts = [baseClass];
10216
+ for (const key of Object.keys(variants)) {
10217
+ const value = props[key];
10218
+ if (value === undefined || value === null) continue;
10219
+
10220
+ // Skip if it's the default value
10221
+ if (value === defaultVariants[key]) continue;
10222
+
10223
+ // Handle boolean variants
10224
+ if (value === true || value === "true") {
10225
+ variantParts.push(key);
10226
+ } else if (value !== false && value !== "false") {
10227
+ variantParts.push(value);
10228
+ }
10229
+ }
10230
+ const variantClass = variantParts.join("-");
10231
+
10232
+ // Always include base class for consistency and nested selector support
10233
+ // e.g., "btn btn-outline-danger" instead of just "btn-outline-danger"
10234
+ if (variantClass !== baseClass) {
10235
+ return baseClass + " " + variantClass;
10236
+ }
10237
+ return variantClass;
10238
+ }
10239
+
10240
+ // Always return the buildClassName function
10241
+ return buildClassName;
10242
+ }
10243
+
9769
10244
  // Simple hashCode function for CSS deduplication
9770
10245
  function getCssHash(str) {
9771
10246
  let hash = 0,
@@ -9813,19 +10288,19 @@ function autoInjectCss(cssString) {
9813
10288
  }
9814
10289
 
9815
10290
  // Enhanced debounced functions with performance monitoring configuration
9816
- /**
9817
- * Debounced version of tws function with performance monitoring
9818
- * @param {string} classNames - String containing Tailwind classes to convert
9819
- * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
9820
- * @returns {string|Object} Inline CSS string or style JSON object
10291
+ /**
10292
+ * Debounced version of tws function with performance monitoring
10293
+ * @param {string} classNames - String containing Tailwind classes to convert
10294
+ * @param {boolean} convertToJson - If true, result will be JSON object, if false becomes CSS string
10295
+ * @returns {string|Object} Inline CSS string or style JSON object
9821
10296
  */
9822
10297
  const debouncedTws = debounce(tws, 50); // Faster debounce for tws
9823
10298
 
9824
- /**
9825
- * Debounced version of twsx function with performance monitoring
9826
- * @param {Object} obj - Object with SCSS-like style format
9827
- * @param {Object} [options] - Additional options
9828
- * @returns {string} Generated CSS string
10299
+ /**
10300
+ * Debounced version of twsx function with performance monitoring
10301
+ * @param {Object} obj - Object with SCSS-like style format
10302
+ * @param {Object} [options] - Additional options
10303
+ * @returns {string} Generated CSS string
9829
10304
  */
9830
10305
  const debouncedTwsx = debounce(twsx, 100); // Standard debounce for twsx
9831
10306
 
@@ -9861,7 +10336,7 @@ const performanceUtils = {
9861
10336
  }
9862
10337
  };
9863
10338
 
9864
- // End of exports - v3.0.0 core only (tws, twsx, configure)
10339
+ // End of exports - v3.0.0 core only (tws, twsx, twsxVariants, configure)
9865
10340
 
9866
10341
  exports.DYNAMIC_TEMPLATES = DYNAMIC_TEMPLATES;
9867
10342
  exports.INLINE_ANIMATIONS = INLINE_ANIMATIONS;
@@ -9894,3 +10369,4 @@ exports.resetTailwindCache = resetTailwindCache;
9894
10369
  exports.staggerAnimations = staggerAnimations;
9895
10370
  exports.tws = tws;
9896
10371
  exports.twsx = twsx;
10372
+ exports.twsxVariants = twsxVariants;