styled-components-to-stylex-codemod 0.0.52 → 0.0.53

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.
@@ -2,7 +2,7 @@ import { t as createModuleResolver } from "./resolve-imports-DgSAddIF.mjs";
2
2
  import { a as isDirectionalResult, n as mergeMarkerDeclarations, o as assertValidAdapter, r as DEFAULT_THEME_HOOK, t as transformedComponentAcceptsSx } from "./sx-surface-CEPFSTO1.mjs";
3
3
  import { a as findImportSource, c as resolveBarrelReExportBinding, d as UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING, l as CASCADE_CONFLICT_WARNING, n as createPrepassParser, o as getReExportedSourceName, r as fileExports, t as walkAst, u as Logger } from "./ast-walk-BOXS-DT7.mjs";
4
4
  import { n as resolveExistingFilePath, r as toRealPath, t as isRelativeSpecifier } from "./path-utils-BC4U8X_q.mjs";
5
- import { a as isBackgroundImageValue, c as isSingleBackgroundComponent, d as kebabToCamelCase, f as looksLikeLength, h as sanitizeIdentifier, i as getCommentBody, l as isStyleSectionMarkerComment, m as normalizeWhitespace, n as capitalize$1, o as isJSDocBlockComment, p as lowerFirst, r as escapeRegex, s as isPrettierIgnoreComment, t as camelToKebabCase, u as isValidIdentifierName } from "./string-utils-BYTEHwNg.mjs";
5
+ import { a as hasTopLevelMatch, c as isPrettierIgnoreComment, d as isValidIdentifierName, f as kebabToCamelCase, g as sanitizeIdentifier, h as normalizeWhitespace, i as getCommentBody, l as isSingleBackgroundComponent, m as lowerFirst, n as capitalize$1, o as isBackgroundImageValue, p as looksLikeLength, r as escapeRegex, s as isJSDocBlockComment, t as camelToKebabCase, u as isStyleSectionMarkerComment } from "./string-utils-Bo3cWgss.mjs";
6
6
  import { a as terminateStandaloneInterpolationStatements, i as parseStyledTemplateLiteral, n as isTemplatePlaceholderInSelectorContext, r as PLACEHOLDER_RE } from "./selector-context-heuristic-LVizWWOR.mjs";
7
7
  import { _ as walkForImportsAndTemplates, a as mergeComponentPropUsage, i as getExhaustiveObservedStaticValues, n as createComponentPropUsageInfo, o as readStaticJsxLiteral, r as formatObservedVariantCondition, s as collectStylexExportNames, t as KNOWN_NON_ELEMENT_PROPS, u as buildImportMapFromNodes } from "./prop-usage-SADzZJdX.mjs";
8
8
  import { createRequire } from "node:module";
@@ -351,24 +351,33 @@ const UNSUPPORTED_STYLEX_CSS_PROPS = new Set([
351
351
  "all",
352
352
  "scroll-margin-block",
353
353
  "scroll-margin-block-start",
354
- "scroll-margin-block-end",
355
- "scroll-margin-inline",
356
- "scroll-margin-inline-start",
357
- "scroll-margin-inline-end",
358
- "scroll-padding-block",
359
- "scroll-padding-block-start",
360
- "scroll-padding-block-end",
361
- "scroll-padding-inline",
362
- "scroll-padding-inline-start",
363
- "scroll-padding-inline-end"
354
+ "scroll-margin-block-end"
364
355
  ]);
365
356
  /**
357
+ * Logical scroll shorthands that expand to Start/End longhands. StyleX's
358
+ * valid-styles rule accepts only the longhand forms.
359
+ */
360
+ const LOGICAL_SCROLL_AXIS_SHORTHANDS = {
361
+ "scroll-margin-inline": "scrollMarginInline",
362
+ "scroll-padding-block": "scrollPaddingBlock",
363
+ "scroll-padding-inline": "scrollPaddingInline"
364
+ };
365
+ /**
366
366
  * Returns true if the CSS property is a shorthand that StyleX cannot express directly
367
367
  * and requires expansion (e.g., `padding`, `margin`, `border`, `background`).
368
368
  */
369
369
  function isCssShorthandProperty(cssProp) {
370
370
  return cssProp in DIRECTIONAL_SHORTHAND_MAP || cssProp === "border" || /^border-(top|right|bottom|left)$/.test(cssProp) || cssProp === "background";
371
371
  }
372
+ /**
373
+ * True for the logical scroll axis shorthands (`scroll-margin-inline`,
374
+ * `scroll-padding-block`, `scroll-padding-inline`). StyleX accepts only their
375
+ * Start/End longhands, so a static value is expanded; a dynamic value cannot be
376
+ * split losslessly and must bail.
377
+ */
378
+ function isLogicalScrollAxisShorthand(cssProp) {
379
+ return cssProp.trim() in LOGICAL_SCROLL_AXIS_SHORTHANDS;
380
+ }
372
381
  function isUnsupportedStylexProperty(cssProp) {
373
382
  return UNSUPPORTED_STYLEX_CSS_PROPS.has(cssProp.trim());
374
383
  }
@@ -394,6 +403,111 @@ function resolveBackgroundStylexPropForVariants(values) {
394
403
  if (hasGradient && hasColor) return null;
395
404
  return hasGradient ? "backgroundImage" : "backgroundColor";
396
405
  }
406
+ /**
407
+ * Expands a static multi-component `background` shorthand (single layer) into
408
+ * the full set of StyleX background longhands, e.g.
409
+ * `#fff url(a.svg) no-repeat center / cover`. Components omitted from the
410
+ * shorthand are emitted at their CSS initial value (e.g. `backgroundColor:
411
+ * transparent`), reproducing the shorthand's reset semantics so the expansion
412
+ * fully overrides any background longhand inherited from a merged/extended base.
413
+ *
414
+ * Returns null when the value cannot be expanded losslessly: multiple layers
415
+ * (top-level commas), unrecognized tokens, duplicate components, or fewer than
416
+ * two explicit components (single components keep the single-longhand path).
417
+ */
418
+ function expandBackgroundShorthandComponents(rawValue) {
419
+ const value = rawValue.trim();
420
+ if (!value || hasTopLevelMatch(value, /,/)) return null;
421
+ const tokens = tokenizeBackgroundValue(value);
422
+ if (!tokens) return null;
423
+ let color;
424
+ let image;
425
+ let attachment;
426
+ const repeatTokens = [];
427
+ const positionTokens = [];
428
+ const sizeTokens = [];
429
+ const boxTokens = [];
430
+ let inSizeMode = false;
431
+ for (const token of tokens) {
432
+ if (token === "/") {
433
+ if (inSizeMode || positionTokens.length === 0) return null;
434
+ inSizeMode = true;
435
+ continue;
436
+ }
437
+ if (inSizeMode) {
438
+ if (sizeTokens.length >= 2 || !isBackgroundSizeToken(token)) return null;
439
+ sizeTokens.push(token);
440
+ continue;
441
+ }
442
+ if (token === "none" || isBackgroundImageValue(token)) {
443
+ if (image !== void 0) return null;
444
+ image = token;
445
+ continue;
446
+ }
447
+ if (isCssColorToken(token)) {
448
+ if (color !== void 0) return null;
449
+ color = token;
450
+ continue;
451
+ }
452
+ if (BACKGROUND_REPEAT_KEYWORDS.has(token)) {
453
+ if (repeatTokens.length >= 2) return null;
454
+ repeatTokens.push(token);
455
+ continue;
456
+ }
457
+ if (BACKGROUND_ATTACHMENT_KEYWORDS.has(token)) {
458
+ if (attachment !== void 0) return null;
459
+ attachment = token;
460
+ continue;
461
+ }
462
+ if (BACKGROUND_BOX_KEYWORDS.has(token)) {
463
+ if (boxTokens.length >= 2) return null;
464
+ boxTokens.push(token);
465
+ continue;
466
+ }
467
+ if (isBackgroundPositionToken(token)) {
468
+ if (positionTokens.length >= 2) return null;
469
+ positionTokens.push(token);
470
+ continue;
471
+ }
472
+ return null;
473
+ }
474
+ if (inSizeMode && sizeTokens.length === 0) return null;
475
+ if ((color !== void 0 ? 1 : 0) + (image !== void 0 ? 1 : 0) + (attachment !== void 0 ? 1 : 0) + (repeatTokens.length ? 1 : 0) + (positionTokens.length ? 1 : 0) + (sizeTokens.length ? 1 : 0) + (boxTokens.length ? 1 : 0) < 2) return null;
476
+ return [
477
+ {
478
+ prop: "backgroundColor",
479
+ value: color ?? "transparent"
480
+ },
481
+ {
482
+ prop: "backgroundImage",
483
+ value: image ?? "none"
484
+ },
485
+ {
486
+ prop: "backgroundRepeat",
487
+ value: repeatTokens.length ? repeatTokens.join(" ") : "repeat"
488
+ },
489
+ {
490
+ prop: "backgroundAttachment",
491
+ value: attachment ?? "scroll"
492
+ },
493
+ {
494
+ prop: "backgroundPosition",
495
+ value: positionTokens.length ? positionTokens.join(" ") : "0% 0%"
496
+ },
497
+ {
498
+ prop: "backgroundSize",
499
+ value: sizeTokens.length ? sizeTokens.join(" ") : "auto"
500
+ },
501
+ {
502
+ prop: "backgroundOrigin",
503
+ value: boxTokens[0] ?? "padding-box"
504
+ },
505
+ {
506
+ prop: "backgroundClip",
507
+ value: boxTokens[1] ?? boxTokens[0] ?? "border-box"
508
+ }
509
+ ];
510
+ }
397
511
  function parseInterpolatedBorderStaticParts(args) {
398
512
  const { prop, prefix, suffix } = args;
399
513
  const borderMatch = prop.match(/^border(-top|-right|-bottom|-left)?$/);
@@ -464,6 +578,28 @@ function cssDeclarationToStylexDeclarations(decl) {
464
578
  }
465
579
  }));
466
580
  }
581
+ const logicalScrollAxis = LOGICAL_SCROLL_AXIS_SHORTHANDS[prop];
582
+ if (logicalScrollAxis && decl.value.kind === "static") {
583
+ const values = splitCssValueWhitespace(decl.valueRaw.trim());
584
+ if (values.length >= 1 && values.length <= 2) {
585
+ const start = values[0];
586
+ const end = values[1] ?? start;
587
+ const withImportant = (value) => decl.important ? `${value} !important` : value;
588
+ return [{
589
+ prop: `${logicalScrollAxis}Start`,
590
+ value: {
591
+ kind: "static",
592
+ value: withImportant(start)
593
+ }
594
+ }, {
595
+ prop: `${logicalScrollAxis}End`,
596
+ value: {
597
+ kind: "static",
598
+ value: withImportant(end)
599
+ }
600
+ }];
601
+ }
602
+ }
467
603
  if (prop === "background") {
468
604
  const rawVal = (decl.valueRaw ?? "").trim();
469
605
  if (rawVal === "none") return [{
@@ -482,6 +618,22 @@ function cssDeclarationToStylexDeclarations(decl) {
482
618
  }];
483
619
  }
484
620
  if (prop === "display" && decl.value.kind === "static" && decl.valueRaw.trim() === "wrap") return [];
621
+ if (prop === "overflow" && decl.value.kind === "static") {
622
+ const tokens = decl.valueRaw.trim().split(/\s+/);
623
+ if (tokens.length === 2) return [{
624
+ prop: "overflowX",
625
+ value: {
626
+ kind: "static",
627
+ value: tokens[0]
628
+ }
629
+ }, {
630
+ prop: "overflowY",
631
+ value: {
632
+ kind: "static",
633
+ value: tokens[1]
634
+ }
635
+ }];
636
+ }
485
637
  if (prop === "animation" && decl.value.kind === "static" && decl.valueRaw.trim() === "none") return [{
486
638
  prop: "animationName",
487
639
  value: decl.value
@@ -729,6 +881,87 @@ function classifyBorderTokens(tokens) {
729
881
  color
730
882
  };
731
883
  }
884
+ const BACKGROUND_REPEAT_KEYWORDS = new Set([
885
+ "repeat",
886
+ "repeat-x",
887
+ "repeat-y",
888
+ "no-repeat",
889
+ "space",
890
+ "round"
891
+ ]);
892
+ const BACKGROUND_ATTACHMENT_KEYWORDS = new Set([
893
+ "scroll",
894
+ "fixed",
895
+ "local"
896
+ ]);
897
+ const BACKGROUND_BOX_KEYWORDS = new Set([
898
+ "border-box",
899
+ "padding-box",
900
+ "content-box"
901
+ ]);
902
+ const BACKGROUND_POSITION_KEYWORDS = new Set([
903
+ "left",
904
+ "right",
905
+ "top",
906
+ "bottom",
907
+ "center"
908
+ ]);
909
+ const CSS_COLOR_FUNCTION_RE = /^(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color)\(/i;
910
+ const CSS_NAMED_COLORS = new Set("aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen transparent currentcolor".split(" "));
911
+ function isCssColorToken(token) {
912
+ if (token.startsWith("#")) return /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(token);
913
+ if (CSS_COLOR_FUNCTION_RE.test(token)) return true;
914
+ return CSS_NAMED_COLORS.has(token.toLowerCase());
915
+ }
916
+ function isBackgroundPositionToken(token) {
917
+ return BACKGROUND_POSITION_KEYWORDS.has(token.toLowerCase()) || /^-?\d*\.?\d+(?:[a-z%]*)$/i.test(token) || /^calc\(/i.test(token);
918
+ }
919
+ function isBackgroundSizeToken(token) {
920
+ const lower = token.toLowerCase();
921
+ return lower === "cover" || lower === "contain" || lower === "auto" || /^-?\d*\.?\d+(?:[a-z%]*)$/i.test(token) || /^calc\(/i.test(token);
922
+ }
923
+ /**
924
+ * Splits a background shorthand value into top-level tokens, keeping function
925
+ * calls intact and emitting `/` (position/size separator) as its own token.
926
+ * Returns null for values containing placeholders or unbalanced parens.
927
+ */
928
+ function tokenizeBackgroundValue(value) {
929
+ if (value.includes("__SC_EXPR_")) return null;
930
+ const tokens = [];
931
+ let current = "";
932
+ let depth = 0;
933
+ const flush = () => {
934
+ if (current) {
935
+ tokens.push(current);
936
+ current = "";
937
+ }
938
+ };
939
+ for (const c of value) {
940
+ if (c === "(") {
941
+ depth++;
942
+ current += c;
943
+ continue;
944
+ }
945
+ if (c === ")") {
946
+ depth = Math.max(0, depth - 1);
947
+ current += c;
948
+ continue;
949
+ }
950
+ if (depth === 0 && /\s/.test(c)) {
951
+ flush();
952
+ continue;
953
+ }
954
+ if (depth === 0 && c === "/") {
955
+ flush();
956
+ tokens.push("/");
957
+ continue;
958
+ }
959
+ current += c;
960
+ }
961
+ if (depth !== 0) return null;
962
+ flush();
963
+ return tokens.length ? tokens : null;
964
+ }
732
965
  //#endregion
733
966
  //#region src/internal/transform-parse-expr.ts
734
967
  function parseExpr(api, exprSource) {
@@ -1741,6 +1974,11 @@ function toSuffixFromProp(propName) {
1741
1974
  return suffixes.join("");
1742
1975
  }
1743
1976
  }
1977
+ const defaultTruthyMatch = trimmed.match(/^(.+?) === undefined \|\| (.+)$/);
1978
+ if (defaultTruthyMatch) {
1979
+ const lhs = defaultTruthyMatch[1].replace(/^\$/, "");
1980
+ if (lhs === defaultTruthyMatch[2].replace(/^\$/, "")) return toSuffixFromProp(lhs);
1981
+ }
1744
1982
  if (trimmed.includes(" || ")) {
1745
1983
  const parts = trimmed.split(" || ").map((s) => s.trim()).filter(Boolean);
1746
1984
  if (parts.length) {
@@ -3498,7 +3736,7 @@ function specifierExportedName(spec) {
3498
3736
  * - Disjunction: `"a || b"` → `a || b`
3499
3737
  * - Grouped negation: `"!(a || b)"` → `!(a || b)`
3500
3738
  */
3501
- function parseVariantWhenToAst(j, when, booleanProps, knownProps) {
3739
+ function parseVariantWhenToAst(j, when, booleanProps, knownProps, nonPropRoots) {
3502
3740
  const buildMemberExpr = (raw) => {
3503
3741
  if (!raw.includes(".")) return null;
3504
3742
  const parts = raw.split(".").map((part) => part.trim()).filter(Boolean);
@@ -3533,7 +3771,7 @@ function parseVariantWhenToAst(j, when, booleanProps, knownProps) {
3533
3771
  }
3534
3772
  const memberExpr = buildMemberExpr(trimmedRaw);
3535
3773
  if (memberExpr) return {
3536
- propName: root && root !== "theme" && isConditionPropIdentifier(root) ? root : null,
3774
+ propName: root && root !== "theme" && !nonPropRoots?.has(root) && isConditionPropIdentifier(root) ? root : null,
3537
3775
  expr: memberExpr
3538
3776
  };
3539
3777
  return {
@@ -3542,7 +3780,7 @@ function parseVariantWhenToAst(j, when, booleanProps, knownProps) {
3542
3780
  };
3543
3781
  }
3544
3782
  return {
3545
- propName: trimmedRaw === "theme" || !isConditionPropIdentifier(trimmedRaw) ? null : trimmedRaw,
3783
+ propName: trimmedRaw === "theme" || nonPropRoots?.has(trimmedRaw) || !isConditionPropIdentifier(trimmedRaw) ? null : trimmedRaw,
3546
3784
  expr: j.identifier(trimmedRaw)
3547
3785
  };
3548
3786
  };
@@ -3553,7 +3791,7 @@ function parseVariantWhenToAst(j, when, booleanProps, knownProps) {
3553
3791
  isBoolean: true
3554
3792
  };
3555
3793
  if (trimmed.startsWith("!(") && trimmed.endsWith(")")) {
3556
- const innerParsed = parseVariantWhenToAst(j, trimmed.slice(2, -1).trim(), booleanProps, knownProps);
3794
+ const innerParsed = parseVariantWhenToAst(j, trimmed.slice(2, -1).trim(), booleanProps, knownProps, nonPropRoots);
3557
3795
  return {
3558
3796
  cond: j.unaryExpression("!", innerParsed.cond),
3559
3797
  props: innerParsed.props,
@@ -3561,7 +3799,7 @@ function parseVariantWhenToAst(j, when, booleanProps, knownProps) {
3561
3799
  };
3562
3800
  }
3563
3801
  if (trimmed.includes("&&")) {
3564
- const parsed = trimmed.split("&&").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p, booleanProps, knownProps));
3802
+ const parsed = trimmed.split("&&").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p, booleanProps, knownProps, nonPropRoots));
3565
3803
  const firstParsed = parsed[0];
3566
3804
  if (!firstParsed) return {
3567
3805
  cond: j.identifier("true"),
@@ -3575,7 +3813,7 @@ function parseVariantWhenToAst(j, when, booleanProps, knownProps) {
3575
3813
  };
3576
3814
  }
3577
3815
  if (trimmed.includes(" || ")) {
3578
- const parsed = trimmed.split(" || ").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p, booleanProps, knownProps));
3816
+ const parsed = trimmed.split(" || ").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p, booleanProps, knownProps, nonPropRoots));
3579
3817
  const firstParsedOr = parsed[0];
3580
3818
  if (!firstParsedOr) return {
3581
3819
  cond: j.identifier("true"),
@@ -3589,7 +3827,7 @@ function parseVariantWhenToAst(j, when, booleanProps, knownProps) {
3589
3827
  };
3590
3828
  }
3591
3829
  if (trimmed.startsWith("!")) {
3592
- const innerParsed = parseVariantWhenToAst(j, trimmed.slice(1).trim(), booleanProps, knownProps);
3830
+ const innerParsed = parseVariantWhenToAst(j, trimmed.slice(1).trim(), booleanProps, knownProps, nonPropRoots);
3593
3831
  return {
3594
3832
  cond: j.unaryExpression("!", innerParsed.cond),
3595
3833
  props: innerParsed.props,
@@ -3718,8 +3956,8 @@ function isConditionPropIdentifier(name, knownProps) {
3718
3956
  * function's parameter destructuring.
3719
3957
  */
3720
3958
  function collectConditionProps(j, args) {
3721
- const { when, destructureProps, booleanProps, knownProps } = args;
3722
- const parsed = parseVariantWhenToAst(j, when, booleanProps, knownProps);
3959
+ const { when, destructureProps, booleanProps, knownProps, nonPropRoots } = args;
3960
+ const parsed = parseVariantWhenToAst(j, when, booleanProps, knownProps, nonPropRoots);
3723
3961
  if (destructureProps) {
3724
3962
  for (const p of parsed.props) if (p && !destructureProps.includes(p)) destructureProps.push(p);
3725
3963
  }
@@ -4627,6 +4865,19 @@ function getPropertyNameFromKey(key) {
4627
4865
  * Core concepts: deep merge semantics, AST node detection, and media query resolution.
4628
4866
  */
4629
4867
  /**
4868
+ * Marks an emitted AST value node as proven to be a single CSS token (e.g. a
4869
+ * numeric stylex const that had a unit suffix). Shorthand properties holding
4870
+ * such values cannot expand to multiple tokens, so the opaque-shorthand
4871
+ * warning does not apply to them.
4872
+ */
4873
+ function markProvenSingleTokenValue(node) {
4874
+ if (node && typeof node === "object") provenSingleTokenValues.add(node);
4875
+ }
4876
+ function isProvenSingleTokenValue(node) {
4877
+ return !!node && typeof node === "object" && provenSingleTokenValues.has(node);
4878
+ }
4879
+ const provenSingleTokenValues = /* @__PURE__ */ new WeakSet();
4880
+ /**
4630
4881
  * Tries to resolve a CallExpression via the adapter's `resolveCall`.
4631
4882
  * Shared by both css-helper and process-rules slot resolution.
4632
4883
  *
@@ -5284,6 +5535,10 @@ function staticStringValue(value) {
5284
5535
  function invertWhen(when) {
5285
5536
  if (when.startsWith("!(") && when.endsWith(")")) return when.slice(2, -1);
5286
5537
  if (when.startsWith("!")) return when.slice(1);
5538
+ if (when.includes(" || ") || when.includes(" && ")) {
5539
+ if (when.includes(" || ") && when.includes(" && ")) return null;
5540
+ return `!(${when})`;
5541
+ }
5287
5542
  const match = when.match(/^(.+)\s+(===|!==)\s+(.+)$/);
5288
5543
  if (match) {
5289
5544
  const [, propName, op, rhs] = match;
@@ -5336,31 +5591,46 @@ const createPropTestHelpers = (bindings) => {
5336
5591
  }
5337
5592
  return null;
5338
5593
  };
5594
+ const applyDefaultToTruthyWhen = (whenName, propName) => {
5595
+ if (bindings.kind !== "destructured" || !bindings.defaults?.has(propName)) return whenName;
5596
+ if (whenName !== propName) return null;
5597
+ const defaultValue = literalToStaticValue(bindings.defaults.get(propName));
5598
+ if (defaultValue === null) return null;
5599
+ return defaultValue ? `${whenName} === undefined || ${whenName}` : whenName;
5600
+ };
5339
5601
  const parseTestInfo = (test) => {
5340
5602
  if (!test || typeof test !== "object") return null;
5341
5603
  if (test.type === "Identifier" && bindings.kind === "destructured") {
5342
5604
  const propAccess = readPropAccess(test);
5343
- return propAccess ? {
5344
- when: propAccess.whenName,
5605
+ if (!propAccess) return null;
5606
+ const when = applyDefaultToTruthyWhen(propAccess.whenName, propAccess.propName);
5607
+ return when === null ? null : {
5608
+ when,
5345
5609
  propName: propAccess.propName
5346
- } : null;
5610
+ };
5347
5611
  }
5348
5612
  if (isMemberExpression(test)) {
5349
5613
  const propAccess = readPropAccess(test);
5350
- return propAccess ? {
5351
- when: propAccess.whenName,
5614
+ if (!propAccess) return null;
5615
+ const when = applyDefaultToTruthyWhen(propAccess.whenName, propAccess.propName);
5616
+ return when === null ? null : {
5617
+ when,
5352
5618
  propName: propAccess.propName
5353
- } : null;
5619
+ };
5354
5620
  }
5355
5621
  if (test.type === "UnaryExpression" && test.operator === "!" && test.argument) {
5356
5622
  const propAccess = readPropAccess(test.argument);
5357
- return propAccess ? {
5358
- when: `!${propAccess.whenName}`,
5623
+ if (!propAccess) return null;
5624
+ const truthyWhen = applyDefaultToTruthyWhen(propAccess.whenName, propAccess.propName);
5625
+ if (truthyWhen === null) return null;
5626
+ return {
5627
+ when: truthyWhen === propAccess.whenName ? `!${truthyWhen}` : `!(${truthyWhen})`,
5359
5628
  propName: propAccess.propName
5360
- } : null;
5629
+ };
5361
5630
  }
5362
5631
  if (test.type === "BinaryExpression" && (test.operator === "===" || test.operator === "!==")) {
5363
5632
  const left = test.left;
5633
+ const operator = test.operator;
5364
5634
  const getRhsValue = () => {
5365
5635
  const rhsTyped = test.right;
5366
5636
  if (rhsTyped.type === "Identifier" && rhsTyped.name === "undefined") return "undefined";
@@ -5368,12 +5638,28 @@ const createPropTestHelpers = (bindings) => {
5368
5638
  if (rhs === null) return getMemberExpressionSource(test.right);
5369
5639
  return JSON.stringify(rhs);
5370
5640
  };
5641
+ const buildBinaryWhen = (propAccess, rhsValue) => {
5642
+ const base = `${propAccess.whenName} ${operator} ${rhsValue}`;
5643
+ if (bindings.kind !== "destructured" || !bindings.defaults?.has(propAccess.propName)) return base;
5644
+ if (propAccess.whenName !== propAccess.propName || rhsValue === "undefined") return null;
5645
+ const defaultValue = literalToStaticValue(bindings.defaults.get(propAccess.propName));
5646
+ if (defaultValue === null) return null;
5647
+ let rhsStatic;
5648
+ try {
5649
+ rhsStatic = JSON.parse(rhsValue);
5650
+ } catch {
5651
+ return null;
5652
+ }
5653
+ if (defaultValue !== rhsStatic) return base;
5654
+ return operator === "===" ? `${propAccess.whenName} === undefined || ${base}` : `${propAccess.whenName} !== undefined && ${base}`;
5655
+ };
5371
5656
  if (bindings.kind === "destructured" && left.type === "Identifier") {
5372
5657
  const propAccess = readPropAccess(left);
5373
5658
  const rhsValue = getRhsValue();
5374
5659
  if (!propAccess || rhsValue === null) return null;
5375
- return {
5376
- when: `${propAccess.whenName} ${test.operator} ${rhsValue}`,
5660
+ const when = buildBinaryWhen(propAccess, rhsValue);
5661
+ return when === null ? null : {
5662
+ when,
5377
5663
  propName: propAccess.propName
5378
5664
  };
5379
5665
  }
@@ -5381,8 +5667,9 @@ const createPropTestHelpers = (bindings) => {
5381
5667
  const propAccess = readPropAccess(left);
5382
5668
  const rhsValue = getRhsValue();
5383
5669
  if (!propAccess || rhsValue === null) return null;
5384
- return {
5385
- when: `${propAccess.whenName} ${test.operator} ${rhsValue}`,
5670
+ const when = buildBinaryWhen(propAccess, rhsValue);
5671
+ return when === null ? null : {
5672
+ when,
5386
5673
  propName: propAccess.propName
5387
5674
  };
5388
5675
  }
@@ -8513,10 +8800,10 @@ function collectDeclPropNames(root, j, decl, filter) {
8513
8800
  const addIfMatch = (name) => {
8514
8801
  if (filter(name)) result.add(name);
8515
8802
  };
8516
- for (const when of Object.keys(decl.variantStyleKeys ?? {})) for (const p of parseVariantWhenToAst(j, when).props) addIfMatch(p);
8803
+ for (const when of Object.keys(decl.variantStyleKeys ?? {})) for (const p of parseVariantWhenToAst(j, when, void 0, void 0, decl.nonPropConditionRoots).props) addIfMatch(p);
8517
8804
  for (const sf of decl.styleFnFromProps ?? []) {
8518
8805
  addIfMatch(sf.jsxProp);
8519
- if (sf.conditionWhen) for (const p of parseVariantWhenToAst(j, sf.conditionWhen).props) addIfMatch(p);
8806
+ if (sf.conditionWhen) for (const p of parseVariantWhenToAst(j, sf.conditionWhen, void 0, void 0, decl.nonPropConditionRoots).props) addIfMatch(p);
8520
8807
  }
8521
8808
  for (const isp of decl.inlineStyleProps ?? []) addIfMatch(isp.jsxProp ?? isp.prop);
8522
8809
  for (const cv of decl.compoundVariants ?? []) if (cv.kind === "3branch" || cv.kind === "4branch") {
@@ -14521,10 +14808,13 @@ function tryHandleAnimation(args) {
14521
14808
  for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex++) {
14522
14809
  const tokens = segments[segmentIndex];
14523
14810
  if (!tokens.length) return false;
14524
- const m = tokens.shift().match(/^__SC_EXPR_(\d+)__$/);
14525
- if (!m) return false;
14811
+ const nameIdx = tokens.findIndex((t) => {
14812
+ const slotMatch = t.match(/^__SC_EXPR_(\d+)__$/);
14813
+ return slotMatch ? getKeyframeFromSlot(Number(slotMatch[1])) !== null : false;
14814
+ });
14815
+ if (nameIdx === -1) return false;
14816
+ const m = tokens.splice(nameIdx, 1)[0].match(/^__SC_EXPR_(\d+)__$/);
14526
14817
  const kf = getKeyframeFromSlot(Number(m[1]));
14527
- if (!kf) return false;
14528
14818
  animNames.push({
14529
14819
  kind: "ident",
14530
14820
  name: kf
@@ -20653,7 +20943,8 @@ function buildVariantStyleExprs(opts) {
20653
20943
  when: positiveWhen,
20654
20944
  destructureProps,
20655
20945
  booleanProps,
20656
- ...knownProps ? { knownProps } : {}
20946
+ ...knownProps ? { knownProps } : {},
20947
+ nonPropRoots: d.nonPropConditionRoots
20657
20948
  });
20658
20949
  if (onNewDestructureProp && prevLengthRef && destructureProps) for (let i = prevLengthRef.value; i < destructureProps.length; i++) onNewDestructureProp(destructureProps[i]);
20659
20950
  const isCurrentPositive = areEquivalentWhen(when, positiveWhen);
@@ -20668,7 +20959,8 @@ function buildVariantStyleExprs(opts) {
20668
20959
  when,
20669
20960
  destructureProps,
20670
20961
  booleanProps,
20671
- ...knownProps ? { knownProps } : {}
20962
+ ...knownProps ? { knownProps } : {},
20963
+ nonPropRoots: d.nonPropConditionRoots
20672
20964
  });
20673
20965
  if (onNewDestructureProp && prevLengthRef && destructureProps) for (let i = prevLengthRef.value; i < destructureProps.length; i++) onNewDestructureProp(destructureProps[i]);
20674
20966
  const styleExpr = styleRef(j, stylesIdentifier, variantKey);
@@ -20684,7 +20976,8 @@ function buildVariantStyleExprs(opts) {
20684
20976
  when,
20685
20977
  destructureProps,
20686
20978
  booleanProps,
20687
- ...knownProps ? { knownProps } : {}
20979
+ ...knownProps ? { knownProps } : {},
20980
+ nonPropRoots: d.nonPropConditionRoots
20688
20981
  });
20689
20982
  const styleExpr = styleRef(j, stylesIdentifier, variantKey);
20690
20983
  pushExpr(emitter.makeConditionalStyleExpr({
@@ -24569,7 +24862,8 @@ function emitShouldForwardPropWrappers(ctx) {
24569
24862
  for (const when of Object.keys(d.variantStyleKeys ?? {})) {
24570
24863
  const { props } = emitter.collectConditionProps({
24571
24864
  when,
24572
- ...knownConditionProps ? { knownProps: knownConditionProps } : {}
24865
+ ...knownConditionProps ? { knownProps: knownConditionProps } : {},
24866
+ nonPropRoots: d.nonPropConditionRoots
24573
24867
  });
24574
24868
  for (const p of props) if (p) extraProps.add(p);
24575
24869
  }
@@ -24715,7 +25009,8 @@ function emitShouldForwardPropWrappers(ctx) {
24715
25009
  if (compoundVariantKeys.has(when)) continue;
24716
25010
  const { props } = emitter.collectConditionProps({
24717
25011
  when,
24718
- ...knownProps ? { knownProps } : {}
25012
+ ...knownProps ? { knownProps } : {},
25013
+ nonPropRoots: d.nonPropConditionRoots
24719
25014
  });
24720
25015
  for (const p of props) if (p && !dropProps.includes(p)) dropProps.push(p);
24721
25016
  }
@@ -24726,7 +25021,8 @@ function emitShouldForwardPropWrappers(ctx) {
24726
25021
  for (const extra of d.extraStylexPropsArgs) if (extra.when) {
24727
25022
  const { props } = emitter.collectConditionProps({
24728
25023
  when: extra.when,
24729
- ...knownProps ? { knownProps } : {}
25024
+ ...knownProps ? { knownProps } : {},
25025
+ nonPropRoots: d.nonPropConditionRoots
24730
25026
  });
24731
25027
  for (const p of props) if (p && !destructureParts.includes(p)) destructureParts.push(p);
24732
25028
  }
@@ -24760,7 +25056,8 @@ function emitShouldForwardPropWrappers(ctx) {
24760
25056
  when: p.conditionWhen,
24761
25057
  destructureProps: destructureParts,
24762
25058
  booleanProps,
24763
- ...knownProps ? { knownProps } : {}
25059
+ ...knownProps ? { knownProps } : {},
25060
+ nonPropRoots: d.nonPropConditionRoots
24764
25061
  });
24765
25062
  expr = emitter.makeConditionalStyleExpr({
24766
25063
  cond,
@@ -25059,7 +25356,8 @@ function emitSimpleWithConfigWrappers(ctx) {
25059
25356
  if (d.variantStyleKeys) for (const [when] of Object.entries(d.variantStyleKeys)) {
25060
25357
  const { props } = emitter.collectConditionProps({
25061
25358
  when,
25062
- ...knownProps ? { knownProps } : {}
25359
+ ...knownProps ? { knownProps } : {},
25360
+ nonPropRoots: d.nonPropConditionRoots
25063
25361
  });
25064
25362
  for (const p of props) if (p) variantProps.add(p);
25065
25363
  }
@@ -25073,7 +25371,8 @@ function emitSimpleWithConfigWrappers(ctx) {
25073
25371
  if (!extra.when) continue;
25074
25372
  const { props } = emitter.collectConditionProps({
25075
25373
  when: extra.when,
25076
- ...knownProps ? { knownProps } : {}
25374
+ ...knownProps ? { knownProps } : {},
25375
+ nonPropRoots: d.nonPropConditionRoots
25077
25376
  });
25078
25377
  for (const p of props) if (p) extraProps.add(p);
25079
25378
  }
@@ -25668,7 +25967,7 @@ function collectPropsUsedOutsideExtraStyleConditionals(j, d, knownProps) {
25668
25967
  if (name) used.add(name);
25669
25968
  };
25670
25969
  for (const [when] of Object.entries(d.variantStyleKeys ?? {})) {
25671
- const parsed = parseVariantWhenToAst(j, when, void 0, knownProps);
25970
+ const parsed = parseVariantWhenToAst(j, when, void 0, knownProps, d.nonPropConditionRoots);
25672
25971
  for (const prop of parsed.props) add(prop);
25673
25972
  }
25674
25973
  for (const dim of d.variantDimensions ?? []) {
@@ -27268,43 +27567,47 @@ function tryResolveConditionalValue(node, ctx) {
27268
27567
  }
27269
27568
  if (info?.kind === "themeBinding" && test.type === "Identifier" && typeof test.name === "string" && isDestructuredFromParam(expr, test.name)) {
27270
27569
  const destructuredProp = test.name;
27271
- const cons = getBranch(consequent);
27272
- const alt = getBranch(alternate);
27273
- if (cons && alt) {
27274
- if (new Set([cons.usage, alt.usage]).size === 1) {
27275
- const usage = cons.usage;
27276
- const variants = [{
27277
- nameHint: "truthy",
27278
- when: destructuredProp,
27279
- expr: cons.expr,
27280
- imports: cons.imports
27281
- }, {
27282
- nameHint: "falsy",
27283
- when: `!${destructuredProp}`,
27284
- expr: alt.expr,
27285
- imports: alt.imports
27286
- }];
27287
- return usage === "props" ? {
27288
- type: "splitVariantsResolvedStyles",
27289
- variants
27290
- } : {
27291
- type: "splitVariantsResolvedValue",
27292
- variants
27293
- };
27570
+ const whens = destructuredBooleanWhens(destructuredProp, getArrowFnParamBindings(expr));
27571
+ if (whens) {
27572
+ const cons = getBranch(consequent);
27573
+ const alt = getBranch(alternate);
27574
+ if (cons && alt) {
27575
+ if (new Set([cons.usage, alt.usage]).size === 1) {
27576
+ const usage = cons.usage;
27577
+ const variants = [{
27578
+ nameHint: "truthy",
27579
+ when: whens.truthy,
27580
+ expr: cons.expr,
27581
+ imports: cons.imports
27582
+ }, {
27583
+ nameHint: "falsy",
27584
+ when: whens.falsy,
27585
+ expr: alt.expr,
27586
+ imports: alt.imports
27587
+ }];
27588
+ return usage === "props" ? {
27589
+ type: "splitVariantsResolvedStyles",
27590
+ variants
27591
+ } : {
27592
+ type: "splitVariantsResolvedValue",
27593
+ variants
27594
+ };
27595
+ }
27294
27596
  }
27597
+ const oneSided = buildOneSidedVariantResult({
27598
+ cons,
27599
+ alt,
27600
+ alternate,
27601
+ truthyWhen: whens.truthy
27602
+ });
27603
+ if (oneSided) return oneSided;
27604
+ if (!cons || !alt) return buildRuntimeCallResult();
27295
27605
  }
27296
- const oneSided = buildOneSidedVariantResult({
27297
- cons,
27298
- alt,
27299
- alternate,
27300
- truthyWhen: destructuredProp
27301
- });
27302
- if (oneSided) return oneSided;
27303
- if (!cons || !alt) return buildRuntimeCallResult();
27304
27606
  }
27305
27607
  if (paramBindings?.kind === "destructured" && test.type === "Identifier" && typeof test.name === "string") {
27306
27608
  const resolvedProp = resolveIdentifierToPropName(test, paramBindings);
27307
- if (resolvedProp) {
27609
+ const resolvedWhens = resolvedProp ? destructuredBooleanWhens(resolvedProp, paramBindings) : null;
27610
+ if (resolvedProp && resolvedWhens) {
27308
27611
  const cons = getBranch(consequent);
27309
27612
  const alt = getBranch(alternate);
27310
27613
  if (cons && alt) {
@@ -27312,12 +27615,12 @@ function tryResolveConditionalValue(node, ctx) {
27312
27615
  const usage = cons.usage;
27313
27616
  const variants = [{
27314
27617
  nameHint: "truthy",
27315
- when: resolvedProp,
27618
+ when: resolvedWhens.truthy,
27316
27619
  expr: cons.expr,
27317
27620
  imports: cons.imports
27318
27621
  }, {
27319
27622
  nameHint: "falsy",
27320
- when: `!${resolvedProp}`,
27623
+ when: resolvedWhens.falsy,
27321
27624
  expr: alt.expr,
27322
27625
  imports: alt.imports
27323
27626
  }];
@@ -27342,7 +27645,7 @@ function tryResolveConditionalValue(node, ctx) {
27342
27645
  cons,
27343
27646
  alt,
27344
27647
  alternate,
27345
- truthyWhen: resolvedProp
27648
+ truthyWhen: resolvedWhens.truthy
27346
27649
  });
27347
27650
  if (oneSided) return oneSided;
27348
27651
  if (!cons || !alt) return buildRuntimeCallResult();
@@ -27419,6 +27722,8 @@ function tryResolveConditionalCssBlock(node, ctx) {
27419
27722
  const { left, right } = body;
27420
27723
  const testProp = paramName ? getSinglePropFromMemberExpr(left, paramName) : bindings?.kind === "destructured" ? resolveIdentifierToPropName(left, bindings) : null;
27421
27724
  if (!testProp) return null;
27725
+ const whens = destructuredBooleanWhens(testProp, bindings);
27726
+ if (!whens) return null;
27422
27727
  const cssText = literalToString(right);
27423
27728
  if (cssText !== null && cssText !== void 0) {
27424
27729
  const style = parseCssDeclarationBlock(cssText);
@@ -27427,7 +27732,7 @@ function tryResolveConditionalCssBlock(node, ctx) {
27427
27732
  type: "splitVariants",
27428
27733
  variants: [{
27429
27734
  nameHint: "truthy",
27430
- when: testProp,
27735
+ when: whens.truthy,
27431
27736
  style
27432
27737
  }]
27433
27738
  };
@@ -27435,7 +27740,7 @@ function tryResolveConditionalCssBlock(node, ctx) {
27435
27740
  if (!paramName) return null;
27436
27741
  return resolveThemeTemplateToCssVariant(right, paramName, ctx, {
27437
27742
  nameHint: "truthy",
27438
- when: testProp
27743
+ when: whens.truthy
27439
27744
  });
27440
27745
  }
27441
27746
  function tryResolveConditionalCssBlockTernary(node, ctx) {
@@ -27887,6 +28192,35 @@ function replaceThemeRefsWithHookVar(expr, paramName, info) {
27887
28192
  };
27888
28193
  return replace(cloned);
27889
28194
  }
28195
+ /**
28196
+ * Builds truthy/falsy `when` condition strings for a destructured boolean-ish prop,
28197
+ * accounting for destructuring defaults. A default applies only when the prop is
28198
+ * `undefined`, so a statically-truthy default means the truthy branch must also
28199
+ * apply when the prop is unset (`prop === undefined || prop`).
28200
+ *
28201
+ * Returns null when the prop has a default whose truthiness cannot be determined
28202
+ * statically — callers should fall back to other handlers instead of emitting a
28203
+ * condition that ignores the default.
28204
+ */
28205
+ function destructuredBooleanWhens(propName, bindings) {
28206
+ if (bindings?.kind !== "destructured" || !bindings.defaults?.has(propName)) return {
28207
+ truthy: propName,
28208
+ falsy: `!${propName}`
28209
+ };
28210
+ const defaultValue = literalToStaticValue(bindings.defaults.get(propName));
28211
+ if (defaultValue === null) return null;
28212
+ if (defaultValue) {
28213
+ const truthy = `${propName} === undefined || ${propName}`;
28214
+ return {
28215
+ truthy,
28216
+ falsy: `!(${truthy})`
28217
+ };
28218
+ }
28219
+ return {
28220
+ truthy: propName,
28221
+ falsy: `!${propName}`
28222
+ };
28223
+ }
27890
28224
  //#endregion
27891
28225
  //#region src/internal/builtin-handlers.ts
27892
28226
  /**
@@ -29386,7 +29720,9 @@ function tryHandleInterpolatedStringValue(args) {
29386
29720
  const outputs = cssDeclarationToStylexDeclarations(d);
29387
29721
  for (let i = 0; i < outputs.length; i++) {
29388
29722
  const out = outputs[i];
29389
- setValue(out.prop, maybeOmitPxUnitFromStylexValue(j, tl, out.prop, d.important, { numericIdentifiers: args.numericIdentifiers }));
29723
+ const emitted = maybeOmitPxUnitFromStylexValue(j, tl, out.prop, d.important, { numericIdentifiers: args.numericIdentifiers });
29724
+ if (emitted !== tl && hasSingleSlotUnitSuffix(d.value)) markProvenSingleTokenValue(emitted);
29725
+ setValue(out.prop, emitted);
29390
29726
  if (i === 0 && (d.leadingComment || d.leadingLineComment)) addPropComments(styleObj, out.prop, {
29391
29727
  leading: d.leadingComment,
29392
29728
  leadingLine: d.leadingLineComment
@@ -33494,7 +33830,12 @@ const createValuePatternHandlers = (ctx) => {
33494
33830
  callArg,
33495
33831
  condition: "always"
33496
33832
  });
33497
- } else styleFnFromProps.push({
33833
+ } else if (expr.body.operator === "||") styleFnFromProps.push({
33834
+ fnKey,
33835
+ jsxProp,
33836
+ condition: "truthy"
33837
+ });
33838
+ else styleFnFromProps.push({
33498
33839
  fnKey,
33499
33840
  jsxProp
33500
33841
  });
@@ -34201,6 +34542,89 @@ function createDeclProcessingState(state, decl) {
34201
34542
  };
34202
34543
  }
34203
34544
  //#endregion
34545
+ //#region src/internal/lower-rules/validate-decl-conflicts.ts
34546
+ /**
34547
+ * Returns the name of an imported runtime-condition root (recorded in
34548
+ * `nonPropConditionRoots`) that also appears as a genuine component prop. Such a
34549
+ * collision is unrepresentable: the decl-wide non-prop marking suppresses
34550
+ * destructuring of the prop, so a prop-based variant would silently read the
34551
+ * imported binding instead. Covers both typed props and props inferred from
34552
+ * `props.<name>` usage in styling.
34553
+ *
34554
+ * `variantWhens` must be sourced from the in-progress finalize state, since
34555
+ * `decl.variantStyleKeys` is not assigned until later in finalize.
34556
+ */
34557
+ function findImportedRootPropCollision(decl, variantWhens) {
34558
+ const roots = decl.nonPropConditionRoots;
34559
+ if (!roots || roots.size === 0) return null;
34560
+ const directProps = collectDirectPropReferences(decl);
34561
+ for (const root of roots) {
34562
+ if (directProps.has(root)) return root;
34563
+ const bareRef = new RegExp(`(?:^|[^\\w.])${escapeRegex(root)}(?:$|[^\\w.])`);
34564
+ if (variantWhens.some((when) => bareRef.test(when))) return root;
34565
+ }
34566
+ return null;
34567
+ }
34568
+ /**
34569
+ * Returns true when the component declares both a logical scroll longhand
34570
+ * (e.g. `scroll-padding-inline-start`) and a physical scroll side
34571
+ * (e.g. `scroll-padding-left`) in the same scroll family. StyleX's
34572
+ * logical/physical conflict normalization resolves these to physical sides
34573
+ * assuming horizontal-tb LTR, but the logical-to-physical mapping depends on
34574
+ * `writing-mode`/`direction` (e.g. `inline-start` is the right side in RTL),
34575
+ * so any such mix may silently preserve or override the wrong side — bail.
34576
+ */
34577
+ function hasConflictingLogicalPhysicalScrollProps(decl) {
34578
+ for (const family of SCROLL_FAMILIES) {
34579
+ let hasLogical = false;
34580
+ let hasPhysical = false;
34581
+ for (const rule of decl.rules) for (const declaration of rule.declarations) {
34582
+ const prop = declaration.property?.trim();
34583
+ if (!prop) continue;
34584
+ const camel = kebabToCamel(prop);
34585
+ if (!camel.startsWith(family)) continue;
34586
+ const side = camel.slice(family.length);
34587
+ if (/^(?:Inline|Block)/.test(side)) hasLogical = true;
34588
+ else if (side === "" || /^(?:Top|Right|Bottom|Left)$/.test(side)) hasPhysical = true;
34589
+ }
34590
+ if (hasLogical && hasPhysical) return true;
34591
+ }
34592
+ return false;
34593
+ }
34594
+ const SCROLL_FAMILIES = ["scrollMargin", "scrollPadding"];
34595
+ function collectDirectPropReferences(decl) {
34596
+ const props = /* @__PURE__ */ new Set();
34597
+ const add = (name) => {
34598
+ if (name && !name.startsWith("__")) props.add(name);
34599
+ };
34600
+ for (const name of decl.typeScriptExplicitPropNames ?? []) add(name);
34601
+ for (const entry of decl.styleFnFromProps ?? []) {
34602
+ add(entry.jsxProp);
34603
+ for (const extra of entry.extraCallArgs ?? []) add(extra.jsxProp);
34604
+ }
34605
+ for (const entry of decl.inlineStyleProps ?? []) add(entry.jsxProp);
34606
+ for (const dim of decl.variantDimensions ?? []) {
34607
+ add(dim.propName);
34608
+ add(dim.namespaceBooleanProp);
34609
+ }
34610
+ for (const cv of decl.compoundVariants ?? []) {
34611
+ add(cv.outerProp);
34612
+ add(cv.innerProp);
34613
+ }
34614
+ const attrs = decl.attrsInfo;
34615
+ if (attrs) {
34616
+ for (const entry of attrs.defaultAttrs ?? []) add(entry.jsxProp);
34617
+ for (const entry of attrs.dynamicAttrs ?? []) add(entry.jsxProp);
34618
+ for (const entry of attrs.conditionalAttrs ?? []) add(entry.jsxProp);
34619
+ for (const entry of attrs.invertedBoolAttrs ?? []) add(entry.jsxProp);
34620
+ for (const entry of attrs.attrsDynamicStyles ?? []) add(entry.jsxProp);
34621
+ }
34622
+ return props;
34623
+ }
34624
+ function kebabToCamel(prop) {
34625
+ return prop.replace(/-([a-z])/g, (_, ch) => ch.toUpperCase());
34626
+ }
34627
+ //#endregion
34204
34628
  //#region src/internal/lower-rules/finalize-decl.ts
34205
34629
  /**
34206
34630
  * Finalizes per-declaration style objects after rule processing.
@@ -34209,6 +34633,14 @@ function createDeclProcessingState(state, decl) {
34209
34633
  function finalizeDeclProcessing(ctx) {
34210
34634
  const { state, decl, styleObj, perPropPseudo, perPropMedia, perPropComputedMedia, nestedSelectors, variantBuckets, variantStyleKeys, variantSourceOrder, extraStyleObjects, styleFnFromProps, styleFnDecls, attrBuckets, observedVariantFallbackFns, inlineStyleProps, localVarValues, cssHelperPropValues } = ctx;
34211
34635
  const { rewriteCssVarsInStyleObject, rewriteCssVarsInAstNode, relationOverridePseudoBuckets, relationOverrides, ancestorSelectorParents, resolvedStyleObjects, warnings } = state;
34636
+ if (findImportedRootPropCollision(decl, Object.keys(variantStyleKeys ?? {}))) {
34637
+ state.bailUnsupported(decl, "Imported runtime condition root collides with a component prop of the same name");
34638
+ return;
34639
+ }
34640
+ if (hasConflictingLogicalPhysicalScrollProps(decl)) {
34641
+ state.bailUnsupported(decl, "Mixed logical and physical scroll properties cannot be normalized without a known writing-mode");
34642
+ return;
34643
+ }
34212
34644
  mergeConditionBucket(styleObj, perPropPseudo);
34213
34645
  mergeConditionBucket(styleObj, perPropMedia);
34214
34646
  for (const [prop, entry] of perPropComputedMedia) {
@@ -36309,7 +36741,7 @@ const OPAQUE_SHORTHAND_PROPS = new Set([
36309
36741
  function warnOpaqueShorthands(styleObj, decl, warnings) {
36310
36742
  for (const prop of OPAQUE_SHORTHAND_PROPS) {
36311
36743
  const val = styleObj[prop];
36312
- if (val !== void 0 && isAstNode$1(val)) warnings.push({
36744
+ if (val !== void 0 && isAstNode$1(val) && !isProvenSingleTokenValue(val)) warnings.push({
36313
36745
  severity: "warning",
36314
36746
  type: "Shorthand property has an opaque value that StyleX will expand to longhands — use `directional` in resolveValue to return separate longhand tokens",
36315
36747
  loc: decl.loc,
@@ -36985,6 +37417,11 @@ function tryHandleInterpolatedBorder(ctx, args) {
36985
37417
  if (width) applyResolvedPropValue(widthProp, width);
36986
37418
  if (style) applyResolvedPropValue(styleProp, style);
36987
37419
  if (color) applyResolvedPropValue(colorProp, color);
37420
+ const stripConsumedStaticParts = () => {
37421
+ const value = d.value;
37422
+ if (!staticRaw || value?.kind !== "interpolated" || !Array.isArray(value.parts)) return;
37423
+ for (const part of value.parts) if (part?.kind === "static") part.value = "";
37424
+ };
36988
37425
  const hasStaticWidthOrStyle = Boolean(width || style);
36989
37426
  const targetProp = interpolationTarget === "color" ? colorProp : interpolationTarget === "width" ? widthProp : styleProp;
36990
37427
  const targetCssProperty = interpolationTarget === "width" ? directionLower ? `border-${directionLower}-width` : "border-width" : interpolationTarget === "style" ? directionLower ? `border-${directionLower}-style` : "border-style" : directionLower ? `border-${directionLower}-color` : "border-color";
@@ -37046,6 +37483,7 @@ function tryHandleInterpolatedBorder(ctx, args) {
37046
37483
  const alt = expr.body.alternate;
37047
37484
  if (isMemberExpression(cons) || isMemberExpression(alt)) {
37048
37485
  d.property = targetCssProperty;
37486
+ stripConsumedStaticParts();
37049
37487
  return false;
37050
37488
  }
37051
37489
  }
@@ -37242,6 +37680,7 @@ function tryHandleInterpolatedBorder(ctx, args) {
37242
37680
  if (expr?.type === "ArrowFunctionExpression") {
37243
37681
  if (expr.body?.type === "MemberExpression") {
37244
37682
  d.property = targetCssProperty;
37683
+ stripConsumedStaticParts();
37245
37684
  return false;
37246
37685
  }
37247
37686
  }
@@ -37435,7 +37874,39 @@ function handleSplitVariantsResolvedValue(ctx) {
37435
37874
  target[prop] = map;
37436
37875
  return map;
37437
37876
  };
37438
- const applyWithContext = (prop, valueAst) => {
37877
+ const withImportant = (valueAst) => {
37878
+ if (!d.important || !valueAst || typeof valueAst !== "object") return valueAst;
37879
+ const node = valueAst;
37880
+ if (node.type === "StringLiteral" || node.type === "Literal" || node.type === "NumericLiteral") {
37881
+ if (typeof node.value === "string") return node.value.includes("!important") ? valueAst : j.literal(`${node.value} !important`);
37882
+ if (typeof node.value === "number") return j.literal(`${node.value} !important`);
37883
+ return valueAst;
37884
+ }
37885
+ if (node.type === "TemplateLiteral" && Array.isArray(node.quasis)) {
37886
+ const quasis = node.quasis;
37887
+ const lastIndex = quasis.length - 1;
37888
+ const last = quasis[lastIndex];
37889
+ const lastText = last?.value?.cooked ?? last?.value?.raw ?? "";
37890
+ if (lastText.includes("!important")) return valueAst;
37891
+ const newQuasis = quasis.map((q, i) => i === lastIndex ? j.templateElement({
37892
+ raw: `${lastText} !important`,
37893
+ cooked: `${lastText} !important`
37894
+ }, true) : j.templateElement({
37895
+ raw: q?.value?.raw ?? q?.value?.cooked ?? "",
37896
+ cooked: q?.value?.cooked ?? q?.value?.raw ?? ""
37897
+ }, false));
37898
+ return j.templateLiteral(newQuasis, node.expressions ?? []);
37899
+ }
37900
+ return j.templateLiteral([j.templateElement({
37901
+ raw: "",
37902
+ cooked: ""
37903
+ }, false), j.templateElement({
37904
+ raw: " !important",
37905
+ cooked: " !important"
37906
+ }, true)], [valueAst]);
37907
+ };
37908
+ const applyWithContext = (prop, valueAstRaw) => {
37909
+ const valueAst = withImportant(valueAstRaw);
37439
37910
  if (media) {
37440
37911
  const map = getOrCreateConditionMap(prop);
37441
37912
  map[media] = valueAst;
@@ -38786,9 +39257,10 @@ function handleInterpolatedDeclaration(args) {
38786
39257
  };
38787
39258
  const resolveValueResult = resolveValue(resolveValueContext);
38788
39259
  if (!resolveValueResult) {
39260
+ const isPlainOwnedConstant = info.path.length === 0 && imp.source.kind === "absolutePath" && !isStylexImportSource(imp.source.value);
38789
39261
  warnings.push({
38790
39262
  severity: "error",
38791
- type: "Adapter resolveValue returned undefined for imported value",
39263
+ type: isPlainOwnedConstant ? "Imported constant cannot be referenced inside stylex.create() — move it into a `.stylex` defineConsts/defineVars group (or map it via adapter.resolveValue)" : "Adapter resolveValue returned undefined for imported value",
38792
39264
  loc: getNodeLocStart(expr) ?? decl.loc,
38793
39265
  context: {
38794
39266
  localName: decl.localName,
@@ -38888,7 +39360,8 @@ function handleInterpolatedDeclaration(args) {
38888
39360
  attrTarget,
38889
39361
  resolvedSelectorMedia
38890
39362
  })) continue;
38891
- if (isImportedShorthandUnitValue(d, decl, importMap)) {
39363
+ const numericIdentifiers = getNumericImportedStylexIdentifiers(j, filePath, importMap, resolverImports);
39364
+ if (isImportedShorthandUnitValue(d, decl, importMap, numericIdentifiers)) {
38892
39365
  bailUnsupportedLocal(decl, "Unsupported interpolation: call expression");
38893
39366
  continue;
38894
39367
  }
@@ -38901,7 +39374,7 @@ function handleInterpolatedDeclaration(args) {
38901
39374
  addImport,
38902
39375
  resolveImportedValueExpr,
38903
39376
  resolveThemeValue,
38904
- numericIdentifiers: getNumericImportedStylexIdentifiers(j, filePath, importMap, resolverImports),
39377
+ numericIdentifiers,
38905
39378
  setStyleValue: (prop, value) => applyResolvedPropValue(prop, value, null)
38906
39379
  })) continue;
38907
39380
  if (bail) break;
@@ -38910,9 +39383,31 @@ function handleInterpolatedDeclaration(args) {
38910
39383
  if (slot) {
38911
39384
  const expr = decl.templateExpressions[slot.slotId];
38912
39385
  if (tryEmitObservedCssBlockVariantBuckets(expr)) continue;
39386
+ const bailOnPropDependentCssHelper = (helperDecl) => {
39387
+ for (const helperExpr of helperDecl.templateExpressions ?? []) {
39388
+ if (!helperExpr || helperExpr.type !== "ArrowFunctionExpression" && helperExpr.type !== "FunctionExpression") continue;
39389
+ const propsUsed = new Set([...collectPropsFromArrowFn(helperExpr), ...collectPropsFromArrowFnDestructured(helperExpr)]);
39390
+ propsUsed.delete("theme");
39391
+ if (propsUsed.size > 0) {
39392
+ warnings.push({
39393
+ severity: "warning",
39394
+ type: "css helper with prop-based interpolation cannot be reused as a mixin",
39395
+ loc: decl.loc,
39396
+ context: {
39397
+ localName: decl.localName,
39398
+ mixin: helperDecl.localName
39399
+ }
39400
+ });
39401
+ bail = true;
39402
+ return true;
39403
+ }
39404
+ }
39405
+ return false;
39406
+ };
38913
39407
  if (expr?.type === "Identifier" && cssHelperNames.has(expr.name)) {
38914
39408
  const helperDecl = declByLocalName.get(expr.name);
38915
39409
  if (helperDecl) {
39410
+ if (bailOnPropDependentCssHelper(helperDecl)) break;
38916
39411
  applyCssHelperMixin(decl, helperDecl, cssHelperPropValues, inlineStyleProps);
38917
39412
  continue;
38918
39413
  }
@@ -38921,6 +39416,7 @@ function handleInterpolatedDeclaration(args) {
38921
39416
  const calleeName = expr.callee.name;
38922
39417
  const helperDecl = declByLocalName.get(calleeName);
38923
39418
  if (helperDecl?.isCssHelper) {
39419
+ if (bailOnPropDependentCssHelper(helperDecl)) break;
38924
39420
  applyCssHelperMixin(decl, helperDecl, cssHelperPropValues, inlineStyleProps);
38925
39421
  continue;
38926
39422
  }
@@ -39066,7 +39562,24 @@ function handleInterpolatedDeclaration(args) {
39066
39562
  })) continue;
39067
39563
  if (d.property && d.value.kind === "interpolated" && tryHandleMultiSlotTernary(ctx, d)) continue;
39068
39564
  if (tryHandleMultiSlotRuntimeValue(resolveImportedValueExpr)) continue;
39069
- const slotPart = d.value.parts.find((p) => p.kind === "slot");
39565
+ const remainingSlotParts = d.value.parts.filter((p) => p.kind === "slot");
39566
+ if (remainingSlotParts.filter((p) => {
39567
+ const slotExpr = decl.templateExpressions[p.slotId];
39568
+ return slotExpr?.type === "ArrowFunctionExpression" || slotExpr?.type === "FunctionExpression";
39569
+ }).length > 1) {
39570
+ warnings.push({
39571
+ severity: "error",
39572
+ type: "Unsupported interpolation: multiple dynamic slots in one declaration",
39573
+ loc: decl.loc,
39574
+ context: {
39575
+ localName: decl.localName,
39576
+ property: d.property
39577
+ }
39578
+ });
39579
+ bail = true;
39580
+ break;
39581
+ }
39582
+ const slotPart = remainingSlotParts[0];
39070
39583
  const slotId = slotPart && slotPart.kind === "slot" ? slotPart.slotId : 0;
39071
39584
  const expr = decl.templateExpressions[slotId];
39072
39585
  const loc = getNodeLocStart(expr);
@@ -39592,11 +40105,11 @@ function handleInterpolatedDeclaration(args) {
39592
40105
  const scalarDynamic = shouldUseScalarDynamicArgs(d.property, d.valueRaw) && dynamicProps.length > 0 ? scalarizePropsObjectDynamicValue({
39593
40106
  j,
39594
40107
  valueExpr: dynamicValueExpr,
39595
- paramName: "props",
40108
+ paramName,
39596
40109
  propNames: dynamicProps
39597
40110
  }) : null;
39598
40111
  const dynamicStyleValueExpr = scalarDynamic?.valueExpr ?? dynamicValueExpr;
39599
- const dynamicStyleParams = scalarDynamic ? scalarDynamic.paramNames.map((propName) => j.identifier(propName)) : [j.identifier("props")];
40112
+ const dynamicStyleParams = scalarDynamic ? scalarDynamic.paramNames.map((propName) => j.identifier(propName)) : [j.identifier(paramName)];
39600
40113
  if (scalarDynamic) annotateScalarParams(dynamicStyleParams, scalarDynamic.paramNames);
39601
40114
  const callArg = j.objectExpression(dynamicProps.map((name) => {
39602
40115
  const prop = j.property("init", j.identifier(name), j.identifier(name));
@@ -39884,17 +40397,22 @@ function handleInterpolatedDeclaration(args) {
39884
40397
  const callArg = resolvedCallArg ?? scalarCallArg;
39885
40398
  const hasExplicitType = !!decl.propsType;
39886
40399
  const isOptional = ctx.isJsxPropOptional(jsxProp);
40400
+ const foldsStaticBaseIntoPseudoDefault = !media && !!pseudos?.length && staticBaseValueWouldFold(styleObj[out.prop]);
39887
40401
  styleFnFromProps.push({
39888
40402
  fnKey,
39889
40403
  jsxProp,
39890
40404
  ...callArg ? { callArg } : {},
39891
- ...hasExplicitType && !isOptional ? { condition: "always" } : {}
40405
+ ...hasExplicitType && !isOptional || foldsStaticBaseIntoPseudoDefault ? { condition: "always" } : {}
39892
40406
  });
39893
40407
  if (!styleFnDecls.has(fnKey)) {
39894
40408
  const param = j.identifier(outParamName);
39895
40409
  const valueId = j.identifier(outParamName);
39896
40410
  if (jsxProp !== "__props") annotateParamFromJsxProp(param, jsxProp);
39897
40411
  if (resolvedCallArg && /\.(ts|tsx)$/.test(filePath)) param.typeAnnotation = j.tsTypeAnnotation(j.tsStringKeyword());
40412
+ if (foldsStaticBaseIntoPseudoDefault && isOptional && jsxProp !== "__props" && /\.(ts|tsx)$/.test(filePath)) {
40413
+ const baseTypeNode = param.typeAnnotation?.typeAnnotation ?? j.tsStringKeyword();
40414
+ param.typeAnnotation = j.tsTypeAnnotation(j.tsUnionType([baseTypeNode, j.tsUndefinedKeyword()]));
40415
+ }
39898
40416
  if (jsxProp?.startsWith?.("$")) ensureShouldForwardPropDrop(decl, jsxProp);
39899
40417
  const buildValueExpr = () => {
39900
40418
  const transformed = (() => {
@@ -39959,7 +40477,28 @@ function handleInterpolatedDeclaration(args) {
39959
40477
  };
39960
40478
  const valueExpr = buildValueExpr();
39961
40479
  const getPropValue = () => {
40480
+ if (!media && !pseudos?.length) return valueExpr;
40481
+ if (!media && pseudos?.length) {
40482
+ const existingStatic = styleObj[out.prop];
40483
+ let defaultValue = j.literal(null);
40484
+ if (existingStatic !== void 0 && existingStatic !== null) if (typeof existingStatic === "object") {
40485
+ if ("type" in existingStatic) {
40486
+ defaultValue = cloneAstNode(existingStatic);
40487
+ delete styleObj[out.prop];
40488
+ }
40489
+ } else {
40490
+ defaultValue = staticValueToLiteral(j, existingStatic);
40491
+ delete styleObj[out.prop];
40492
+ }
40493
+ return j.objectExpression([j.property("init", j.identifier("default"), defaultValue), ...pseudos.map((ps) => j.property("init", j.literal(ps), valueExpr))]);
40494
+ }
39962
40495
  if (!media) return valueExpr;
40496
+ if (pseudos?.length) return buildPseudoMediaPropValue({
40497
+ j,
40498
+ valueExpr,
40499
+ pseudos,
40500
+ media
40501
+ });
39963
40502
  const existingFn = styleFnDecls.get(fnKey);
39964
40503
  let existingValue = null;
39965
40504
  if (existingFn?.type === "ArrowFunctionExpression") {
@@ -40215,6 +40754,17 @@ function resolveDerivedLocalVariable(j, fnBody, fnParamName, localName, jsxProp)
40215
40754
  function isPseudoElementSelector(pseudoElement) {
40216
40755
  return pseudoElement === "::before" || pseudoElement === "::after" || pseudoElement === "::placeholder";
40217
40756
  }
40757
+ /**
40758
+ * Whether a base style value for a property would be folded into a pseudo-gated
40759
+ * dynamic style function's `default` (mirrors the fold logic in getPropValue):
40760
+ * plain primitives and AST-node values fold; existing pseudo/media condition
40761
+ * buckets (plain objects without a `type` discriminator) do not.
40762
+ */
40763
+ function staticBaseValueWouldFold(existingStatic) {
40764
+ if (existingStatic === void 0 || existingStatic === null) return false;
40765
+ if (typeof existingStatic === "object") return "type" in existingStatic;
40766
+ return true;
40767
+ }
40218
40768
  function tryHandleLocalCustomPropertyDefinition(args) {
40219
40769
  const { j, d, decl, expr, getOrCreateLocalStylexVar, inlineStyleProps } = args;
40220
40770
  if (!expr || typeof expr !== "object") return false;
@@ -40269,16 +40819,45 @@ function tryHandleRuntimeConditionalStaticBranches(ctx, args) {
40269
40819
  state.bailUnsupported(decl, "Unsupported interpolation: call expression");
40270
40820
  return true;
40271
40821
  }
40272
- if (hasLaterDeclarationOverlap(rule, allRules, d, new Set(Object.keys(consequentStyle)))) {
40822
+ if (!subtractLaterStaticOverrides({
40823
+ rule,
40824
+ allRules,
40825
+ currentDecl: d,
40826
+ branchStyles: [consequentStyle, alternateStyle]
40827
+ })) {
40273
40828
  state.bailUnsupported(decl, "Unsupported interpolation: call expression");
40274
40829
  return true;
40275
40830
  }
40831
+ if (!Object.keys(consequentStyle).length && !Object.keys(alternateStyle).length) return true;
40276
40832
  const target = getBaseStyleTarget();
40277
40833
  for (const [prop, value] of Object.entries(alternateStyle)) target[prop] = value;
40278
40834
  applyVariant({ when }, consequentStyle);
40279
40835
  decl.needsWrapperComponent = true;
40836
+ recordNonPropConditionRoots(decl, expr.test);
40280
40837
  return true;
40281
40838
  }
40839
+ /**
40840
+ * Records the root identifiers of an imported runtime condition on the decl so
40841
+ * wrapper emission treats them as module-scope bindings rather than component
40842
+ * props (which matters for lowercase roots like `browser.isTouchDevice`).
40843
+ */
40844
+ function recordNonPropConditionRoots(decl, test) {
40845
+ const roots = decl.nonPropConditionRoots ??= /* @__PURE__ */ new Set();
40846
+ const visit = (expr) => {
40847
+ if (expr.type === "LogicalExpression") {
40848
+ visit(expr.left);
40849
+ visit(expr.right);
40850
+ return;
40851
+ }
40852
+ if (expr.type === "UnaryExpression") {
40853
+ visit(expr.argument);
40854
+ return;
40855
+ }
40856
+ const info = extractRootAndPath(expr);
40857
+ if (info && info.path.length > 0) roots.add(info.rootName);
40858
+ };
40859
+ visit(test);
40860
+ }
40282
40861
  function buildStaticBranchStyle(d, rawValue) {
40283
40862
  if (d.property === "background" && isUnsupportedBackgroundShorthandValue(rawValue)) return null;
40284
40863
  const staticDecl = {
@@ -40303,24 +40882,120 @@ function sameStyleProps(left, right) {
40303
40882
  const rightKeys = new Set(Object.keys(right));
40304
40883
  return leftKeys.length === rightKeys.size && leftKeys.every((key) => rightKeys.has(key));
40305
40884
  }
40306
- function hasLaterDeclarationOverlap(rule, allRules, currentDecl, stylexProps) {
40885
+ /**
40886
+ * Removes branch properties that are unconditionally overridden by later static
40887
+ * declarations in the same selector context, so the runtime variant cannot
40888
+ * invert the original CSS cascade. Partially overridden directional props are
40889
+ * narrowed to the longhands that survive the override (e.g. `marginBlock`
40890
+ * overridden by a later `margin-top` becomes `marginBlockEnd`).
40891
+ *
40892
+ * Returns false when a later overlapping declaration cannot be subtracted
40893
+ * safely (conditional at-rule context, dynamic value, property-less helper, or
40894
+ * a multi-token branch value that cannot be split per longhand) — the caller
40895
+ * must bail in that case.
40896
+ */
40897
+ function subtractLaterStaticOverrides(args) {
40898
+ const { rule, allRules, currentDecl, branchStyles } = args;
40307
40899
  const currentIndex = rule.declarations.indexOf(currentDecl);
40308
- if (currentIndex === -1) return false;
40309
- if (declarationsOverlap(rule.declarations.slice(currentIndex + 1), stylexProps)) return true;
40900
+ if (currentIndex === -1) return true;
40901
+ const laterContexts = [{
40902
+ declarations: rule.declarations.slice(currentIndex + 1),
40903
+ unconditional: true
40904
+ }];
40310
40905
  const currentRuleIndex = allRules.indexOf(rule);
40311
- if (currentRuleIndex === -1) return false;
40312
- for (const laterRule of allRules.slice(currentRuleIndex + 1)) {
40313
- if (rule.selector !== laterRule.selector) continue;
40314
- if (declarationsOverlap(laterRule.declarations, stylexProps)) return true;
40906
+ if (currentRuleIndex !== -1) for (const laterRule of allRules.slice(currentRuleIndex + 1)) {
40907
+ if (laterRule.selector !== rule.selector) continue;
40908
+ laterContexts.push({
40909
+ declarations: laterRule.declarations,
40910
+ unconditional: sameAtRuleStack(laterRule.atRuleStack, rule.atRuleStack)
40911
+ });
40315
40912
  }
40316
- return false;
40913
+ const branchProps = () => [...new Set(branchStyles.flatMap((style) => Object.keys(style)))];
40914
+ for (const context of laterContexts) for (const laterDecl of context.declarations) {
40915
+ if (!laterDecl.property) {
40916
+ if (branchProps().length) return false;
40917
+ continue;
40918
+ }
40919
+ if (isBorderShorthandProperty(laterDecl.property)) {
40920
+ const borderProps = new Set(cssDeclarationToStylexDeclarations(laterDecl).map((out) => out.prop));
40921
+ if (branchProps().some((prop) => [...borderProps].some((borderProp) => stylexPropsOverlap(prop, borderProp)))) return false;
40922
+ continue;
40923
+ }
40924
+ for (const out of cssDeclarationToStylexDeclarations(laterDecl)) {
40925
+ const overrideProp = out.prop;
40926
+ const overlapped = branchProps().filter((prop) => stylexPropsOverlap(prop, overrideProp));
40927
+ if (!overlapped.length) continue;
40928
+ if (currentDecl.important && !laterDecl.important) return false;
40929
+ if (!context.unconditional || laterDecl.value.kind !== "static") return false;
40930
+ for (const branch of branchStyles) if (!subtractOverrideFromBranch(branch, overlapped, overrideProp)) return false;
40931
+ }
40932
+ }
40933
+ return true;
40317
40934
  }
40318
- function declarationsOverlap(declarations, stylexProps) {
40319
- for (const laterDecl of declarations) {
40320
- if (!laterDecl.property) return true;
40321
- for (const out of cssDeclarationToStylexDeclarations(laterDecl)) if ([...stylexProps].some((prop) => stylexPropsOverlap(prop, out.prop))) return true;
40935
+ function sameAtRuleStack(left, right) {
40936
+ return left.length === right.length && left.every((entry, i) => entry === right[i]);
40937
+ }
40938
+ /** True for the `border` / `border-<side>` shorthands (not the longhands). */
40939
+ function isBorderShorthandProperty(property) {
40940
+ return /^border(?:-(?:top|right|bottom|left))?$/.test(property.trim());
40941
+ }
40942
+ function subtractOverrideFromBranch(branch, overlappedProps, overrideProp) {
40943
+ const overridePhysical = new Set(physicalLonghandExpansion(overrideProp));
40944
+ const overrideIsLogical = isLogicalDirectionalProp(overrideProp);
40945
+ for (const branchProp of overlappedProps) {
40946
+ if (!(branchProp in branch)) continue;
40947
+ const branchPhysical = physicalLonghandExpansion(branchProp);
40948
+ const remainder = branchPhysical.filter((prop) => !overridePhysical.has(prop));
40949
+ if (remainder.length === branchPhysical.length) continue;
40950
+ if (isLogicalDirectionalProp(branchProp) !== overrideIsLogical) return false;
40951
+ const value = branch[branchProp];
40952
+ delete branch[branchProp];
40953
+ if (!remainder.length) continue;
40954
+ if (!isSingleCssToken(value)) return false;
40955
+ for (const physical of remainder) {
40956
+ const name = overrideIsLogical && LOGICAL_TO_PHYSICAL[branchProp] ? logicalFormForPhysical(branchProp, physical) : physical;
40957
+ if (!name) return false;
40958
+ branch[name] = value;
40959
+ }
40322
40960
  }
40323
- return false;
40961
+ return true;
40962
+ }
40963
+ /**
40964
+ * True for a logical directional longhand whose physical side(s) depend on the
40965
+ * writing mode — `marginInline`, `paddingBlockEnd`, `scrollMarginInlineStart`,
40966
+ * etc. The physical-neutral full shorthands (`margin`, `padding`) and physical
40967
+ * sides (`marginTop`) are not logical.
40968
+ */
40969
+ function isLogicalDirectionalProp(prop) {
40970
+ return LOGICAL_TO_PHYSICAL[prop] !== void 0;
40971
+ }
40972
+ /** Physical longhands covered by a StyleX directional/border property. */
40973
+ function physicalLonghandExpansion(prop) {
40974
+ const group = SHORTHAND_LONGHANDS[prop];
40975
+ if (group) return [...group.physical];
40976
+ const logical = LOGICAL_TO_PHYSICAL[prop];
40977
+ if (logical) return [...logical];
40978
+ const borderMatch = prop.match(/^border(Top|Right|Bottom|Left)?(Width|Style|Color)$/);
40979
+ if (borderMatch) {
40980
+ const side = borderMatch[1];
40981
+ const kind = borderMatch[2];
40982
+ return side ? [prop] : [
40983
+ "Top",
40984
+ "Right",
40985
+ "Bottom",
40986
+ "Left"
40987
+ ].map((s) => `border${s}${kind}`);
40988
+ }
40989
+ return [prop];
40990
+ }
40991
+ /** Maps a physical longhand back to the Start/End form of a logical branch prop. */
40992
+ function logicalFormForPhysical(logicalProp, physical) {
40993
+ for (const [name, physicalProps] of Object.entries(LOGICAL_TO_PHYSICAL)) if (physicalProps.length === 1 && physicalProps[0] === physical && name.startsWith(logicalProp)) return name;
40994
+ return null;
40995
+ }
40996
+ function isSingleCssToken(value) {
40997
+ if (typeof value === "number") return true;
40998
+ return typeof value === "string" && value.trim() !== "" && !/\s/.test(value.trim());
40324
40999
  }
40325
41000
  function stylexPropsOverlap(left, right) {
40326
41001
  const leftRelated = relatedDirectionalProps(left);
@@ -40356,21 +41031,20 @@ function addRelatedBorderLonghands(prop, related) {
40356
41031
  }
40357
41032
  function isImportedRuntimeCondition(expr, importMap) {
40358
41033
  const info = extractRootAndPath(expr);
40359
- if (info && info.path.length > 0 && importMap.has(info.rootName) && isRuntimeConditionImportRoot(info.rootName)) return true;
41034
+ if (info && info.path.length > 0 && importMap.has(info.rootName)) return true;
40360
41035
  if (expr.type === "LogicalExpression" && expr.operator === "&&") return isImportedRuntimeCondition(expr.left, importMap) && isImportedRuntimeCondition(expr.right, importMap);
40361
41036
  if (expr.type === "UnaryExpression" && expr.operator === "!") return isImportedRuntimeCondition(expr.argument, importMap);
40362
41037
  return false;
40363
41038
  }
40364
- function isRuntimeConditionImportRoot(rootName) {
40365
- return /^[A-Z]/.test(rootName);
40366
- }
40367
- function isImportedShorthandUnitValue(d, decl, importMap) {
41039
+ function isImportedShorthandUnitValue(d, decl, importMap, numericIdentifiers) {
40368
41040
  if (!d.property || !isCssShorthandProperty(d.property)) return false;
40369
41041
  const staticParts = getSingleSlotStaticParts(d, decl);
40370
41042
  if (!staticParts || !/^[a-zA-Z%]/.test(staticParts.suffix)) return false;
40371
41043
  const slotPart = d.value.kind === "interpolated" ? d.value.parts.find((part) => part.kind === "slot") : null;
40372
41044
  const info = extractRootAndPath(slotPart && slotPart.kind === "slot" ? decl.templateExpressions[slotPart.slotId] : void 0);
40373
- return !!info && importMap.has(info.rootName);
41045
+ if (!info || !importMap.has(info.rootName)) return false;
41046
+ if ((d.property === "margin" || d.property === "padding") && staticParts.prefix.trim() === "" && /^[a-zA-Z%]+$/.test(staticParts.suffix.trim()) && numericIdentifiers.has(info.rootName)) return false;
41047
+ return true;
40374
41048
  }
40375
41049
  function expressionToSource(j, expr) {
40376
41050
  try {
@@ -42122,6 +42796,12 @@ function processRuleDeclarations(args) {
42122
42796
  d.property = resolvedProperty;
42123
42797
  }
42124
42798
  if (d.value.kind === "interpolated") {
42799
+ if (d.property && isLogicalScrollAxisShorthand(d.property)) {
42800
+ state.bailUnsupported(ctx.decl, "Dynamic logical scroll shorthand cannot be expanded — bind a specific longhand (e.g. scroll-padding-inline-start) instead");
42801
+ if (state.currentDecl === ctx.decl) continue;
42802
+ state.bail = true;
42803
+ break;
42804
+ }
42125
42805
  handleInterpolatedDeclaration({
42126
42806
  ctx,
42127
42807
  rule,
@@ -42167,6 +42847,20 @@ function processRuleDeclarations(args) {
42167
42847
  }
42168
42848
  }
42169
42849
  if (d.property === "background" && isUnsupportedBackgroundShorthandValue(d.valueRaw)) {
42850
+ const expanded = d.value.kind === "static" && !d.important && !hasOtherBackgroundDeclaration(allRules, d) ? expandBackgroundShorthandComponents(d.valueRaw) : null;
42851
+ if (expanded) {
42852
+ const commentSource = {
42853
+ leading: d.leadingComment,
42854
+ leadingLine: d.leadingLineComment,
42855
+ trailingLine: d.trailingLineComment
42856
+ };
42857
+ let isFirst = true;
42858
+ for (const { prop, value } of expanded) {
42859
+ applyResolvedPropValue(prop, value, isFirst ? commentSource : null, d.property);
42860
+ isFirst = false;
42861
+ }
42862
+ continue;
42863
+ }
42170
42864
  state.bailUnsupported(ctx.decl, "Unsupported background shorthand: multiple components cannot be mapped to a single StyleX longhand");
42171
42865
  if (state.currentDecl === ctx.decl) break;
42172
42866
  state.bail = true;
@@ -42221,6 +42915,19 @@ function resolveInterpolatedPropertyName(property, ctx) {
42221
42915
  if (!resolved.startsWith("--")) return null;
42222
42916
  return resolved;
42223
42917
  }
42918
+ /**
42919
+ * True when any declaration other than `current` (across all of the component's
42920
+ * rules) targets a `background`/`background-*` property. A multi-component
42921
+ * background expansion only reproduces reset semantics in isolation, so the
42922
+ * presence of a sibling background declaration makes the expansion unsafe.
42923
+ */
42924
+ function hasOtherBackgroundDeclaration(allRules, current) {
42925
+ for (const rule of allRules) for (const declaration of rule.declarations) {
42926
+ if (declaration === current) continue;
42927
+ if (declaration.property && /^background(-|$)/.test(declaration.property)) return true;
42928
+ }
42929
+ return false;
42930
+ }
42224
42931
  //#endregion
42225
42932
  //#region src/internal/lower-rules/process-rules.ts
42226
42933
  function processDeclRules(ctx) {
@@ -42864,6 +43571,40 @@ function processDeclRules(ctx) {
42864
43571
  for (const bucket of variantBuckets.values()) if (Object.hasOwn(bucket, prop)) wrapStyleConditionValue(bucket, prop, conditionKey, conditionValue);
42865
43572
  patchStyleFnConditionValue(prop, conditionKey, conditionValue);
42866
43573
  };
43574
+ /**
43575
+ * A base-scope declaration later in the CSS overrides any earlier conditional
43576
+ * assignment of the same property — within one styled template the generated
43577
+ * class contains both declarations, and the last one wins regardless of the
43578
+ * runtime condition. Drop the dead base-scope value from earlier variant
43579
+ * buckets so the emitted variant (applied after the base style in
43580
+ * stylex.props) cannot incorrectly win. Condition-scoped values (pseudo/media
43581
+ * maps) only lose their `default` layer.
43582
+ *
43583
+ * Exception: an earlier `!important` conditional value still wins over a later
43584
+ * non-important base declaration in the CSS cascade, so it must be preserved.
43585
+ * (When the later base is itself `!important`, source order decides and the
43586
+ * later one wins, so clearing is correct.)
43587
+ */
43588
+ const clearEarlierVariantBaseValues = (prop, laterBaseValue) => {
43589
+ const laterBaseIsImportant = cssValueIsImportant(laterBaseValue);
43590
+ for (const [when, bucket] of variantBuckets) {
43591
+ if (!Object.hasOwn(bucket, prop)) continue;
43592
+ const bucketValue = bucket[prop];
43593
+ if (bucketValue !== null && typeof bucketValue === "object" && !isAstNode$1(bucketValue) && "default" in bucketValue) {
43594
+ const conditionDefault = bucketValue.default;
43595
+ if (!laterBaseIsImportant && cssValueIsImportant(conditionDefault)) continue;
43596
+ bucketValue.default = null;
43597
+ continue;
43598
+ }
43599
+ if (!laterBaseIsImportant && cssValueIsImportant(bucketValue)) continue;
43600
+ delete bucket[prop];
43601
+ if (Object.keys(bucket).length === 0) {
43602
+ variantBuckets.delete(when);
43603
+ delete ctx.variantStyleKeys[when];
43604
+ delete ctx.variantSourceOrder[when];
43605
+ }
43606
+ }
43607
+ };
42867
43608
  const applyResolvedPropValue = (prop, value, commentSource, sourceCssProperty) => {
42868
43609
  const noteSourceCssProperty = (target) => {
42869
43610
  if (sourceCssProperty) {
@@ -43078,6 +43819,7 @@ function processDeclRules(ctx) {
43078
43819
  }
43079
43820
  return;
43080
43821
  }
43822
+ clearEarlierVariantBaseValues(prop, value);
43081
43823
  const target = ctx.getBaseStyleTarget();
43082
43824
  setStyleObjectValue(target, prop, value);
43083
43825
  noteSourceCssProperty(target);
@@ -43606,9 +44348,23 @@ function recoverStandaloneInterpolationsInPseudoBlock(rule, decl) {
43606
44348
  cssProps
43607
44349
  };
43608
44350
  }
44351
+ /**
44352
+ * Whether a lowered StyleX value carries `!important`. Values are stored either
44353
+ * as plain strings (e.g. `"red !important"`) or as AST string/template literals.
44354
+ */
44355
+ function cssValueIsImportant(value) {
44356
+ if (typeof value === "string") return value.includes("!important");
44357
+ if (!value || typeof value !== "object") return false;
44358
+ const node = value;
44359
+ if ((node.type === "StringLiteral" || node.type === "Literal") && typeof node.value === "string") return node.value.includes("!important");
44360
+ if (node.type === "TemplateLiteral" && Array.isArray(node.quasis)) return node.quasis.some((q) => (q?.value?.cooked ?? q?.value?.raw ?? "").includes("!important"));
44361
+ return false;
44362
+ }
43609
44363
  /** Negates a `when` condition string (e.g. `$active` → `!$active`, `!$x` → `$x`). */
43610
44364
  function negateWhen(when) {
44365
+ if (when.startsWith("!(") && when.endsWith(")")) return when.slice(2, -1);
43611
44366
  if (when.startsWith("!")) return when.slice(1);
44367
+ if (when.includes(" || ") || when.includes(" && ")) return `!(${when})`;
43612
44368
  if (when.includes(" === ")) return when.replace(" === ", " !== ");
43613
44369
  if (when.includes(" !== ")) return when.replace(" !== ", " === ");
43614
44370
  return `!${when}`;
@@ -46095,6 +46851,10 @@ function wrappedComponentAcceptsSxProp(ctx, componentLocalName, visiting = /* @_
46095
46851
  visiting.delete(componentLocalName);
46096
46852
  return true;
46097
46853
  }
46854
+ if (localStyledDecl?.needsWrapperComponent && !localStyledDecl.skipTransform && localStyledDecl.supportsExternalStyles) {
46855
+ visiting.delete(componentLocalName);
46856
+ return true;
46857
+ }
46098
46858
  const componentInterface = wrappedComponentInterfaceFor(ctx, componentLocalName);
46099
46859
  if (componentInterface !== void 0) {
46100
46860
  visiting.delete(componentLocalName);
@@ -46342,8 +47102,9 @@ function rewriteJsxStep(ctx) {
46342
47102
  const mergeArgs = matchedCombinedStyleKey ? void 0 : opening.__promotedMergeArgs;
46343
47103
  const baseMember = j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(baseStyleKey));
46344
47104
  const baseExpr = mergeArgs ? j.callExpression(baseMember, mergeArgs) : baseMember;
47105
+ const rendersLocalWrapperBase = decl.base.kind === "component" && decl.base.ident === finalTag && (ctx.styledDecls?.some((d) => d.localName === finalTag && d.needsWrapperComponent && !d.skipTransform) ?? false);
46345
47106
  const styleArgs = [
46346
- ...decl.extendsStyleKey ? [j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(decl.extendsStyleKey))] : [],
47107
+ ...decl.extendsStyleKey && !rendersLocalWrapperBase ? [j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(decl.extendsStyleKey))] : [],
46347
47108
  ...extraMixinArgs,
46348
47109
  ...decl.skipBaseStyleRef ? [] : [baseExpr],
46349
47110
  ...extraAfterBaseArgs