@mochi-css/vanilla 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -28,45 +28,6 @@ clsx = __toESM(clsx);
28
28
  let react = require("react");
29
29
  react = __toESM(react);
30
30
 
31
- //#region src/values/number.ts
32
- var NumericValue = class {
33
- constructor(value, suffix) {
34
- this.value = value;
35
- this.suffix = suffix;
36
- }
37
- toString() {
38
- return `${this.value}${this.suffix}`;
39
- }
40
- };
41
-
42
- //#endregion
43
- //#region src/values/length.ts
44
- var PixelValue = class extends NumericValue {
45
- constructor(value) {
46
- super(value, "px");
47
- }
48
- };
49
- var PercentValue = class extends NumericValue {
50
- constructor(value) {
51
- super(value, "%");
52
- }
53
- };
54
- function asLength(value) {
55
- switch (typeof value) {
56
- case "number": return `${value}px`;
57
- case "string": return value;
58
- default: return asLength(value.value);
59
- }
60
- }
61
-
62
- //#endregion
63
- //#region src/values/color.ts
64
- function asColor(value) {
65
- if (typeof value === "string") return value;
66
- return value.value;
67
- }
68
-
69
- //#endregion
70
31
  //#region src/token.ts
71
32
  /**
72
33
  * Represents a CSS custom property (design token) with type information.
@@ -119,77 +80,384 @@ function createToken(name) {
119
80
  return new Token(name);
120
81
  }
121
82
 
83
+ //#endregion
84
+ //#region src/propertyUnits.generated.ts
85
+ const propertyUnits = {
86
+ animation: "ms",
87
+ animationDelay: "ms",
88
+ animationDuration: "ms",
89
+ animationRange: "px",
90
+ animationRangeCenter: "px",
91
+ animationRangeEnd: "px",
92
+ animationRangeStart: "px",
93
+ background: "px",
94
+ backgroundPosition: "px",
95
+ backgroundPositionBlock: "px",
96
+ backgroundPositionInline: "px",
97
+ backgroundPositionX: "px",
98
+ backgroundPositionY: "px",
99
+ backgroundSize: "px",
100
+ backgroundTbd: "px",
101
+ baselineShift: "px",
102
+ blockSize: "px",
103
+ blockStep: "px",
104
+ blockStepSize: "px",
105
+ border: "px",
106
+ borderBlock: "px",
107
+ borderBlockClip: "px",
108
+ borderBlockEnd: "px",
109
+ borderBlockEndClip: "px",
110
+ borderBlockEndRadius: "px",
111
+ borderBlockEndWidth: "px",
112
+ borderBlockStart: "px",
113
+ borderBlockStartClip: "px",
114
+ borderBlockStartRadius: "px",
115
+ borderBlockStartWidth: "px",
116
+ borderBlockWidth: "px",
117
+ borderBottom: "px",
118
+ borderBottomClip: "px",
119
+ borderBottomLeftRadius: "px",
120
+ borderBottomRadius: "px",
121
+ borderBottomRightRadius: "px",
122
+ borderBottomWidth: "px",
123
+ borderClip: "px",
124
+ borderEndEndRadius: "px",
125
+ borderEndStartRadius: "px",
126
+ borderImage: "%",
127
+ borderImageOutset: "px",
128
+ borderImageSlice: "%",
129
+ borderImageWidth: "px",
130
+ borderInline: "px",
131
+ borderInlineClip: "px",
132
+ borderInlineEnd: "px",
133
+ borderInlineEndClip: "px",
134
+ borderInlineEndRadius: "px",
135
+ borderInlineEndWidth: "px",
136
+ borderInlineStart: "px",
137
+ borderInlineStartClip: "px",
138
+ borderInlineStartRadius: "px",
139
+ borderInlineStartWidth: "px",
140
+ borderInlineWidth: "px",
141
+ borderLeft: "px",
142
+ borderLeftClip: "px",
143
+ borderLeftRadius: "px",
144
+ borderLeftWidth: "px",
145
+ borderLimit: "px",
146
+ borderRadius: "px",
147
+ borderRight: "px",
148
+ borderRightClip: "px",
149
+ borderRightRadius: "px",
150
+ borderRightWidth: "px",
151
+ borderSpacing: "px",
152
+ borderStartEndRadius: "px",
153
+ borderStartStartRadius: "px",
154
+ borderTop: "px",
155
+ borderTopClip: "px",
156
+ borderTopLeftRadius: "px",
157
+ borderTopRadius: "px",
158
+ borderTopRightRadius: "px",
159
+ borderTopWidth: "px",
160
+ borderWidth: "px",
161
+ bottom: "px",
162
+ boxShadow: "px",
163
+ boxShadowBlur: "px",
164
+ boxShadowOffset: "px",
165
+ boxShadowSpread: "px",
166
+ columnGap: "px",
167
+ columnHeight: "px",
168
+ columnRule: "px",
169
+ columnRuleEdgeInset: "px",
170
+ columnRuleEdgeInsetEnd: "px",
171
+ columnRuleEdgeInsetStart: "px",
172
+ columnRuleInset: "px",
173
+ columnRuleInsetEnd: "px",
174
+ columnRuleInsetStart: "px",
175
+ columnRuleInteriorInset: "px",
176
+ columnRuleInteriorInsetEnd: "px",
177
+ columnRuleInteriorInsetStart: "px",
178
+ columnRuleWidth: "px",
179
+ columns: "px",
180
+ columnWidth: "px",
181
+ containIntrinsicBlockSize: "px",
182
+ containIntrinsicHeight: "px",
183
+ containIntrinsicInlineSize: "px",
184
+ containIntrinsicSize: "px",
185
+ containIntrinsicWidth: "px",
186
+ corner: "px",
187
+ cornerBlockEnd: "px",
188
+ cornerBlockStart: "px",
189
+ cornerBottom: "px",
190
+ cornerBottomLeft: "px",
191
+ cornerBottomRight: "px",
192
+ cornerEndEnd: "px",
193
+ cornerEndStart: "px",
194
+ cornerInlineEnd: "px",
195
+ cornerInlineStart: "px",
196
+ cornerLeft: "px",
197
+ cornerRight: "px",
198
+ cornerStartEnd: "px",
199
+ cornerStartStart: "px",
200
+ cornerTop: "px",
201
+ cornerTopLeft: "px",
202
+ cornerTopRight: "px",
203
+ cx: "px",
204
+ cy: "px",
205
+ fillOpacity: "%",
206
+ fillPosition: "px",
207
+ fillSize: "px",
208
+ flex: "px",
209
+ flexBasis: "px",
210
+ floatOffset: "px",
211
+ floodOpacity: "%",
212
+ flowTolerance: "px",
213
+ font: "deg",
214
+ fontSize: "px",
215
+ fontStretch: "%",
216
+ fontStyle: "deg",
217
+ fontWidth: "%",
218
+ gap: "px",
219
+ grid: "px",
220
+ gridAutoColumns: "px",
221
+ gridAutoRows: "px",
222
+ gridColumnGap: "px",
223
+ gridGap: "px",
224
+ gridRowGap: "px",
225
+ gridTemplate: "px",
226
+ gridTemplateColumns: "px",
227
+ gridTemplateRows: "px",
228
+ height: "px",
229
+ hyphenateLimitZone: "px",
230
+ imageOrientation: "deg",
231
+ imageResolution: "dppx",
232
+ initialLetterWrap: "px",
233
+ inlineSize: "px",
234
+ inset: "px",
235
+ insetBlock: "px",
236
+ insetBlockEnd: "px",
237
+ insetBlockStart: "px",
238
+ insetInline: "px",
239
+ insetInlineEnd: "px",
240
+ insetInlineStart: "px",
241
+ interestDelay: "ms",
242
+ interestDelayEnd: "ms",
243
+ interestDelayStart: "ms",
244
+ itemFlow: "px",
245
+ left: "px",
246
+ letterSpacing: "px",
247
+ lineHeight: "px",
248
+ lineHeightStep: "px",
249
+ linePadding: "px",
250
+ margin: "px",
251
+ marginBlock: "px",
252
+ marginBlockEnd: "px",
253
+ marginBlockStart: "px",
254
+ marginBottom: "px",
255
+ marginInline: "px",
256
+ marginInlineEnd: "px",
257
+ marginInlineStart: "px",
258
+ marginLeft: "px",
259
+ marginRight: "px",
260
+ marginTop: "px",
261
+ mask: "px",
262
+ maskBorder: "%",
263
+ maskBorderOutset: "px",
264
+ maskBorderSlice: "%",
265
+ maskBorderWidth: "px",
266
+ maskPosition: "px",
267
+ maskSize: "px",
268
+ maxBlockSize: "px",
269
+ maxHeight: "px",
270
+ maxInlineSize: "px",
271
+ maxWidth: "px",
272
+ minBlockSize: "px",
273
+ minHeight: "px",
274
+ minInlineSize: "px",
275
+ minWidth: "px",
276
+ objectPosition: "px",
277
+ offset: "px",
278
+ offsetAnchor: "px",
279
+ offsetDistance: "px",
280
+ offsetPosition: "px",
281
+ offsetRotate: "deg",
282
+ opacity: "%",
283
+ outline: "px",
284
+ outlineOffset: "px",
285
+ outlineWidth: "px",
286
+ overflowClipMargin: "px",
287
+ overflowClipMarginBlock: "px",
288
+ overflowClipMarginBlockEnd: "px",
289
+ overflowClipMarginBlockStart: "px",
290
+ overflowClipMarginBottom: "px",
291
+ overflowClipMarginInline: "px",
292
+ overflowClipMarginInlineEnd: "px",
293
+ overflowClipMarginInlineStart: "px",
294
+ overflowClipMarginLeft: "px",
295
+ overflowClipMarginRight: "px",
296
+ overflowClipMarginTop: "px",
297
+ padding: "px",
298
+ paddingBlock: "px",
299
+ paddingBlockEnd: "px",
300
+ paddingBlockStart: "px",
301
+ paddingBottom: "px",
302
+ paddingInline: "px",
303
+ paddingInlineEnd: "px",
304
+ paddingInlineStart: "px",
305
+ paddingLeft: "px",
306
+ paddingRight: "px",
307
+ paddingTop: "px",
308
+ pause: "ms",
309
+ pauseAfter: "ms",
310
+ pauseBefore: "ms",
311
+ perspective: "px",
312
+ perspectiveOrigin: "px",
313
+ r: "px",
314
+ rest: "ms",
315
+ restAfter: "ms",
316
+ restBefore: "ms",
317
+ right: "px",
318
+ rotate: "deg",
319
+ rowGap: "px",
320
+ rowRule: "px",
321
+ rowRuleEdgeInset: "px",
322
+ rowRuleEdgeInsetEnd: "px",
323
+ rowRuleEdgeInsetStart: "px",
324
+ rowRuleInset: "px",
325
+ rowRuleInsetEnd: "px",
326
+ rowRuleInsetStart: "px",
327
+ rowRuleInteriorInset: "px",
328
+ rowRuleInteriorInsetEnd: "px",
329
+ rowRuleInteriorInsetStart: "px",
330
+ rowRuleWidth: "px",
331
+ rule: "px",
332
+ ruleEdgeInset: "px",
333
+ ruleInset: "px",
334
+ ruleInsetEnd: "px",
335
+ ruleInsetStart: "px",
336
+ ruleInteriorInset: "px",
337
+ ruleWidth: "px",
338
+ rx: "px",
339
+ ry: "px",
340
+ scale: "%",
341
+ scrollMargin: "px",
342
+ scrollMarginBlock: "px",
343
+ scrollMarginBlockEnd: "px",
344
+ scrollMarginBlockStart: "px",
345
+ scrollMarginBottom: "px",
346
+ scrollMarginInline: "px",
347
+ scrollMarginInlineEnd: "px",
348
+ scrollMarginInlineStart: "px",
349
+ scrollMarginLeft: "px",
350
+ scrollMarginRight: "px",
351
+ scrollMarginTop: "px",
352
+ scrollPadding: "px",
353
+ scrollPaddingBlock: "px",
354
+ scrollPaddingBlockEnd: "px",
355
+ scrollPaddingBlockStart: "px",
356
+ scrollPaddingBottom: "px",
357
+ scrollPaddingInline: "px",
358
+ scrollPaddingInlineEnd: "px",
359
+ scrollPaddingInlineStart: "px",
360
+ scrollPaddingLeft: "px",
361
+ scrollPaddingRight: "px",
362
+ scrollPaddingTop: "px",
363
+ shapeImageThreshold: "%",
364
+ shapeMargin: "px",
365
+ shapePadding: "px",
366
+ stopOpacity: "%",
367
+ strokeDasharray: "px",
368
+ strokeDashcorner: "px",
369
+ strokeDashCorner: "px",
370
+ strokeDashoffset: "px",
371
+ strokeOpacity: "%",
372
+ strokePosition: "px",
373
+ strokeSize: "px",
374
+ strokeWidth: "px",
375
+ tabSize: "px",
376
+ textDecoration: "px",
377
+ textDecorationInset: "px",
378
+ textDecorationThickness: "px",
379
+ textIndent: "px",
380
+ textShadow: "px",
381
+ textSizeAdjust: "%",
382
+ textUnderlineOffset: "px",
383
+ timelineTrigger: "px",
384
+ timelineTriggerExitRange: "px",
385
+ timelineTriggerExitRangeEnd: "px",
386
+ timelineTriggerExitRangeStart: "px",
387
+ timelineTriggerRange: "px",
388
+ timelineTriggerRangeEnd: "px",
389
+ timelineTriggerRangeStart: "px",
390
+ top: "px",
391
+ transformOrigin: "px",
392
+ transition: "ms",
393
+ transitionDelay: "ms",
394
+ transitionDuration: "ms",
395
+ translate: "px",
396
+ verticalAlign: "px",
397
+ viewTimeline: "px",
398
+ viewTimelineInset: "px",
399
+ voiceDuration: "ms",
400
+ voicePitch: "Hz",
401
+ voiceRange: "Hz",
402
+ voiceRate: "%",
403
+ width: "px",
404
+ wordSpacing: "px",
405
+ x: "px",
406
+ y: "px",
407
+ zoom: "%"
408
+ };
409
+
122
410
  //#endregion
123
411
  //#region src/props.ts
124
412
  /**
125
- * Converts a CSS-like value to its string representation.
126
- * Handles strings, numbers, and wrapped values with a `.value` property.
127
- * @param v - The value to convert
128
- * @returns The string representation of the value
413
+ * Converts a kebab-case string to camelCase.
129
414
  */
130
- function asEnum(v) {
131
- if (typeof v === "string") return v;
132
- if (typeof v === "number") return v.toString();
133
- return v.value.toString();
415
+ function kebabToCamel(str) {
416
+ return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
417
+ }
418
+ /**
419
+ * Converts a camelCase string to kebab-case.
420
+ */
421
+ function camelToKebab(str) {
422
+ return str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
423
+ }
424
+ function getUnitForProperty(propertyName) {
425
+ return propertyName in propertyUnits ? propertyUnits[propertyName] : void 0;
134
426
  }
135
- const styles = {
136
- borderBlockEndWidth: asLength,
137
- borderBlockStartWidth: asLength,
138
- borderBottomColor: asColor,
139
- borderBottomLeftRadius: asLength,
140
- borderBottomRightRadius: asLength,
141
- borderBottomWidth: asLength,
142
- borderEndEndRadius: asLength,
143
- borderEndStartRadius: asLength,
144
- borderInlineEndWidth: asLength,
145
- borderInlineStartWidth: asLength,
146
- borderLeftWidth: asLength,
147
- borderRightWidth: asLength,
148
- borderStartEndRadius: asLength,
149
- borderStartStartRadius: asLength,
150
- borderTopLeftRadius: asLength,
151
- borderTopRightRadius: asLength,
152
- borderTopWidth: asLength,
153
- borderBlockWidth: asLength,
154
- borderInlineWidth: asLength,
155
- borderWidth: asLength,
156
- gap: asLength,
157
- height: asLength,
158
- lineHeight: asLength,
159
- marginBottom: asLength,
160
- marginLeft: asLength,
161
- marginRight: asLength,
162
- marginTop: asLength,
163
- paddingBottom: asLength,
164
- paddingLeft: asLength,
165
- paddingRight: asLength,
166
- paddingTop: asLength,
167
- width: asLength
168
- };
169
- /** Set of all known CSS property names in camelCase format */
170
- const knownPropertySet = new Set(known_css_properties.default.all.map(kebabToCamel));
171
427
  /**
172
- * Type guard that checks if a string starts with a specific prefix.
428
+ * Converts a CSS-like value to its string representation.
429
+ * For properties with known units, numbers are automatically suffixed.
173
430
  */
174
- function startsWith(value, prefix) {
175
- return value.startsWith(prefix);
431
+ function formatValue(value, propertyName, maxDepth = 10) {
432
+ if (maxDepth <= 0) return "";
433
+ if (typeof value === "string") return value;
434
+ if (typeof value === "number") {
435
+ const unit = getUnitForProperty(propertyName);
436
+ if (unit === "%") return `${value * 100}${unit}`;
437
+ if (value === 0) return "0";
438
+ return unit ? `${value}${unit}` : value.toString();
439
+ }
440
+ return formatValue(value.value, propertyName, maxDepth - 1);
176
441
  }
442
+ const knownPropertySet = new Set(known_css_properties.default.all.map(kebabToCamel));
177
443
  /**
178
444
  * Checks if a property name is a CSS custom property (variable).
179
445
  */
180
446
  function isCssVariableName(key) {
181
- return startsWith(key, "--");
447
+ return key.startsWith("--");
182
448
  }
183
449
  /**
184
450
  * Converts a CSS-like value to a string for use as a CSS variable value.
185
451
  * @param value - The value to convert (string, number, or wrapped value)
452
+ * @param maxDepth - Maximum recursion depth for evaluating the value
186
453
  * @returns The string representation
187
454
  */
188
- function asVar(value) {
455
+ function asVar(value, maxDepth = 10) {
456
+ if (maxDepth <= 0) return "";
189
457
  switch (typeof value) {
190
458
  case "string": return value;
191
- case "number": return `${value}`;
192
- default: return asVar(value.value);
459
+ case "number": return value.toString();
460
+ default: return asVar(value.value, maxDepth - 1);
193
461
  }
194
462
  }
195
463
  /**
@@ -199,16 +467,14 @@ function isKnownPropertyName(key) {
199
467
  return knownPropertySet.has(key);
200
468
  }
201
469
  /**
202
- * Converts a value to a CSS property string using the appropriate parser.
203
- * Uses specialized parsers for properties with unit requirements (lengths, colors).
470
+ * Converts a value to a CSS property string.
471
+ * Automatically appends units to numeric values for properties that require them.
204
472
  * @param value - The value to convert
205
473
  * @param key - The CSS property name
206
474
  * @returns The formatted CSS value string
207
475
  */
208
476
  function asKnownProp(value, key) {
209
- const parser = key in styles ? styles[key] : void 0;
210
- if (!parser) return asEnum(value);
211
- return parser(value, key);
477
+ return formatValue(value, key);
212
478
  }
213
479
  /**
214
480
  * Checks if a key represents a nested CSS selector.
@@ -223,18 +489,6 @@ function isMediaSelector(key) {
223
489
  return key.startsWith("@");
224
490
  }
225
491
  /**
226
- * Converts a camelCase string to kebab-case.
227
- */
228
- function camelToKebab(str) {
229
- return str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
230
- }
231
- /**
232
- * Converts a kebab-case string to camelCase.
233
- */
234
- function kebabToCamel(str) {
235
- return str.replace(/-[a-z]/g, (m) => m.substring(1).toUpperCase());
236
- }
237
- /**
238
492
  * Converts a SimpleStyleProps object to a CSS properties record.
239
493
  * Transforms camelCase property names to kebab-case and applies value converters.
240
494
  * @param props - The style properties object
@@ -475,11 +729,15 @@ var CssObjectSubBlock = class CssObjectSubBlock {
475
729
  });
476
730
  continue;
477
731
  }
478
- if (isMediaSelector(key)) propsToProcess.push({
479
- key,
480
- props: value,
481
- selector: selector.wrap(key)
482
- });
732
+ if (isMediaSelector(key)) {
733
+ propsToProcess.push({
734
+ key,
735
+ props: value,
736
+ selector: selector.wrap(key)
737
+ });
738
+ continue;
739
+ }
740
+ if (process.env["NODE_ENV"] !== "production") console.warn(`[mochi-css] Unknown style property "${key}" will be ignored`);
483
741
  }
484
742
  return [new CssObjectSubBlock(cssProps, selector), ...propsToProcess.toSorted(stringPropComparator("key")).flatMap(({ props: props$1, selector: selector$1 }) => CssObjectSubBlock.fromProps(props$1, selector$1))];
485
743
  }
@@ -498,8 +756,8 @@ var CssObjectBlock = class {
498
756
  * Generates a unique class name based on the content hash.
499
757
  * @param styles - The style properties to compile
500
758
  */
501
- constructor(styles$1) {
502
- const blocks = CssObjectSubBlock.fromProps(styles$1);
759
+ constructor(styles) {
760
+ const blocks = CssObjectSubBlock.fromProps(styles);
503
761
  this.className = "c" + shortHash(blocks.map((b) => b.hash).join("+"));
504
762
  this.subBlocks = blocks;
505
763
  }
@@ -543,22 +801,29 @@ var CSSObject = class {
543
801
  variantBlocks;
544
802
  /** Default variant selections */
545
803
  variantDefaults;
804
+ /** Compound variant conditions and their parsed sub-blocks */
805
+ compoundVariants;
546
806
  /**
547
807
  * Creates a new CSSObject from style props.
548
808
  * Compiles main styles and all variant options into CSS blocks.
549
- * @param props - Style properties with optional variants and defaults
550
809
  */
551
- constructor({ variants, defaultVariants,...props }) {
810
+ constructor({ variants, defaultVariants, compoundVariants,...props }) {
552
811
  this.mainBlock = new CssObjectBlock(props);
553
812
  this.variantBlocks = {};
554
- this.variantDefaults = {};
555
- if (!variants) return;
556
- for (const variantGroupName in variants) {
813
+ this.variantDefaults = defaultVariants ?? {};
814
+ this.compoundVariants = [];
815
+ if (variants) for (const variantGroupName in variants) {
557
816
  this.variantBlocks[variantGroupName] = {};
558
817
  const variantGroup = variants[variantGroupName];
559
- for (const variantItemName in variantGroup) this.variantBlocks[variantGroupName][variantItemName] = new CssObjectBlock(variantGroup[variantItemName]);
818
+ for (const variantItemName in variantGroup) this.variantBlocks[variantGroupName][variantItemName] = new CssObjectBlock(variantGroup[variantItemName] ?? {});
819
+ }
820
+ if (compoundVariants) for (const compound of compoundVariants) {
821
+ const { css: styles,...conditions } = compound;
822
+ this.compoundVariants.push({
823
+ conditions,
824
+ subBlocks: CssObjectSubBlock.fromProps(styles)
825
+ });
560
826
  }
561
- this.variantDefaults = defaultVariants;
562
827
  }
563
828
  /**
564
829
  * Serializes the entire CSS object to a CSS string.
@@ -566,7 +831,17 @@ var CSSObject = class {
566
831
  * @returns Complete CSS string ready for injection into a stylesheet
567
832
  */
568
833
  asCssString() {
569
- return [this.mainBlock.asCssString(this.mainBlock.selector), ...Object.entries(this.variantBlocks).toSorted(compareStringKey).flatMap(([_, b]) => Object.entries(b).toSorted(compareStringKey)).map(([_, b]) => b.asCssString(this.mainBlock.selector))].join("\n\n");
834
+ return [
835
+ this.mainBlock.asCssString(this.mainBlock.selector),
836
+ ...Object.entries(this.variantBlocks).toSorted(compareStringKey).flatMap(([_, b]) => Object.entries(b).toSorted(compareStringKey)).map(([_, b]) => b.asCssString(this.mainBlock.selector)),
837
+ ...this.compoundVariants.map(({ conditions, subBlocks }) => {
838
+ const variantSelectors = Object.entries(conditions).toSorted(compareStringKey).map(([variantName, optionName]) => {
839
+ return (this.variantBlocks[variantName]?.[optionName])?.selector ?? "";
840
+ }).filter(Boolean).join("");
841
+ const combinedSelector = `${this.mainBlock.selector}${variantSelectors}`;
842
+ return subBlocks.map((b) => b.asCssString(combinedSelector)).join("\n\n");
843
+ })
844
+ ].join("\n\n");
570
845
  }
571
846
  };
572
847
 
@@ -600,14 +875,23 @@ var MochiCSS = class MochiCSS {
600
875
  }
601
876
  /**
602
877
  * Computes the final className string based on variant selections.
878
+ * Compound variants are handled purely via CSS combined selectors,
879
+ * so no runtime matching is needed here.
603
880
  * @param props - Variant selections
604
881
  * @returns Combined className string for use in components
605
882
  */
606
883
  variant(props) {
607
884
  const keys = new Set([...Object.keys(props), ...Object.keys(this.defaultVariants)].filter((k) => k in this.variantClassNames));
608
885
  return (0, clsx.default)(this.classNames, ...keys.values().map((k) => {
609
- const variantKey = (k in props ? props[k] : void 0) ?? this.defaultVariants[k];
610
- return this.variantClassNames[k][`${variantKey}`];
886
+ const variantGroup = this.variantClassNames[k];
887
+ if (!variantGroup) return false;
888
+ const variantKey = ((k in props ? props[k] : void 0) ?? this.defaultVariants[k])?.toString();
889
+ if (variantKey == null) return false;
890
+ const selectedClassname = variantGroup[variantKey];
891
+ if (selectedClassname !== void 0) return selectedClassname;
892
+ const defaultKey = this.defaultVariants[k];
893
+ if (defaultKey == null) return false;
894
+ return variantGroup[defaultKey.toString()];
611
895
  }));
612
896
  }
613
897
  /**
@@ -622,7 +906,7 @@ var MochiCSS = class MochiCSS {
622
906
  return [key, Object.fromEntries(Object.entries(variantOptions).map(([optionKey, block]) => {
623
907
  return [optionKey, block.className];
624
908
  }))];
625
- })), object.variantDefaults ?? {});
909
+ })), object.variantDefaults);
626
910
  }
627
911
  };
628
912
  /**
@@ -655,11 +939,15 @@ var MochiCSS = class MochiCSS {
655
939
  * // Merging multiple styles
656
940
  * const combined = css(baseStyles, additionalStyles)
657
941
  */
942
+ const emptyMochiCSS = new MochiCSS([], {}, {});
658
943
  function css(...props) {
659
- const cssToMerge = props.map((p) => {
660
- if (p instanceof MochiCSS) return p;
661
- return MochiCSS.from(new CSSObject(p));
662
- });
944
+ const cssToMerge = [];
945
+ for (const p of props) {
946
+ if (p == null || typeof p !== "object") continue;
947
+ if (p instanceof MochiCSS) cssToMerge.push(p);
948
+ else cssToMerge.push(MochiCSS.from(new CSSObject(p)));
949
+ }
950
+ if (cssToMerge.length === 0) return emptyMochiCSS;
663
951
  return new MochiCSS(cssToMerge.flatMap((css$1) => css$1.classNames), cssToMerge.reduce((a, b) => Object.assign(a, b.variantClassNames), {}), cssToMerge.reduce((a, b) => Object.assign(a, b.defaultVariants), {}));
664
952
  }
665
953
 
@@ -694,27 +982,65 @@ function css(...props) {
694
982
  * // Usage: <Button size="large" variant="primary">Click me</Button>
695
983
  */
696
984
  function styled(target, ...props) {
697
- const styles$1 = css(...props);
985
+ const styles = css(...props);
698
986
  return ({ className,...p }) => (0, react.createElement)(target, {
699
- className: (0, clsx.default)(styles$1.variant(p), className),
987
+ className: (0, clsx.default)(styles.variant(p), className),
700
988
  ...p
701
989
  });
702
990
  }
703
991
 
992
+ //#endregion
993
+ //#region src/keyframesObject.ts
994
+ var KeyframesObject = class KeyframesObject {
995
+ name;
996
+ body;
997
+ constructor(stops) {
998
+ this.body = KeyframesObject.generateBody(stops);
999
+ this.name = "kf" + shortHash(this.body);
1000
+ }
1001
+ asCssString() {
1002
+ return `@keyframes ${this.name} {\n${this.body}\n}`;
1003
+ }
1004
+ static generateBody(stops) {
1005
+ return Object.entries(stops).toSorted(compareStringKey).map(([stopKey, props]) => {
1006
+ const cssProps = cssFromProps(props);
1007
+ return ` ${stopKey} {\n${Object.entries(cssProps).toSorted(compareStringKey).map(([k, v]) => ` ${k}: ${v};`).join("\n")}\n }`;
1008
+ }).join("\n\n");
1009
+ }
1010
+ };
1011
+
1012
+ //#endregion
1013
+ //#region src/keyframes.ts
1014
+ var MochiKeyframes = class MochiKeyframes {
1015
+ constructor(name) {
1016
+ this.name = name;
1017
+ }
1018
+ toString() {
1019
+ return this.name;
1020
+ }
1021
+ get value() {
1022
+ return this.name;
1023
+ }
1024
+ static from(object) {
1025
+ return new MochiKeyframes(object.name);
1026
+ }
1027
+ };
1028
+ function keyframes(stops) {
1029
+ return MochiKeyframes.from(new KeyframesObject(stops));
1030
+ }
1031
+
704
1032
  //#endregion
705
1033
  exports.CSSObject = CSSObject;
706
1034
  exports.CssObjectBlock = CssObjectBlock;
707
1035
  exports.CssObjectSubBlock = CssObjectSubBlock;
1036
+ exports.KeyframesObject = KeyframesObject;
708
1037
  exports.MochiCSS = MochiCSS;
1038
+ exports.MochiKeyframes = MochiKeyframes;
709
1039
  exports.MochiSelector = MochiSelector;
710
- exports.NumericValue = NumericValue;
711
- exports.PercentValue = PercentValue;
712
- exports.PixelValue = PixelValue;
713
1040
  exports.Token = Token;
714
- exports.asColor = asColor;
715
- exports.asLength = asLength;
716
1041
  exports.createToken = createToken;
717
1042
  exports.css = css;
718
1043
  exports.cssFromProps = cssFromProps;
1044
+ exports.keyframes = keyframes;
719
1045
  exports.styled = styled;
720
1046
  //# sourceMappingURL=index.js.map