@percy/dom 1.32.3-beta.1 → 1.32.3-beta.3

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.
Files changed (2) hide show
  1. package/dist/bundle.js +31 -3
  2. package/package.json +2 -2
package/dist/bundle.js CHANGED
@@ -1030,10 +1030,26 @@
1030
1030
  return decls.join(' ');
1031
1031
  }
1032
1032
 
1033
+ // Resolve a nested rule's selector against its parent per CSS Nesting.
1034
+ // The CSSOM serializes nested selectors with the implicit nesting selector
1035
+ // made explicit, so every nested selector — including each item of a
1036
+ // selector list — carries its own `&`. Replacing every `&` with :is(parent)
1037
+ // therefore fully scopes the rule. Without this, a bare top-level `&`
1038
+ // resolves to :root on engines that support nesting, so component-scoped
1039
+ // interactive-state rules leak page-wide (PER-9775). :is(...) preserves the
1040
+ // parent's grouping/specificity.
1041
+ function resolveNestedSelector(selectorText, parentSelector) {
1042
+ if (!parentSelector) return selectorText;
1043
+ return selectorText.replace(/&/g, `:is(${parentSelector})`);
1044
+ }
1045
+
1033
1046
  // Walk a CSSRule list yielding every reachable style rule. Nested rules
1034
1047
  // inside @media/@layer/@supports are emitted with the at-rule prelude
1035
1048
  // preserved as a wrapper string; flat-emitting would drop the guard.
1036
- function walkCSSRules(ruleList) {
1049
+ // `parentSelector` carries the enclosing style rule's (already-resolved)
1050
+ // selector down through native CSS nesting so `&`-relative children are
1051
+ // resolved against it rather than leaking as a bare top-level `&`.
1052
+ function walkCSSRules(ruleList, parentSelector = null) {
1037
1053
  const result = [];
1038
1054
  for (let i = 0; i < ruleList.length; i++) {
1039
1055
  const rule = ruleList[i];
@@ -1042,7 +1058,19 @@
1042
1058
  var _rule$media;
1043
1059
  const conditionText = rule.conditionText || ((_rule$media = rule.media) === null || _rule$media === void 0 ? void 0 : _rule$media.mediaText);
1044
1060
  const atRulePrelude = conditionText && rule.cssText ? rule.cssText.split('{')[0].trim() : null;
1045
- for (const inner of walkCSSRules(rule.cssRules)) {
1061
+ // An at-rule (@media/@layer/@supports) keeps the current parent scope
1062
+ // and is re-applied as a wrapper. A style rule with nested children
1063
+ // (native CSS nesting) becomes the scope for its children — resolve
1064
+ // its own selector against any outer parent first so deeper nesting
1065
+ // composes correctly.
1066
+ // Default to the current scope (also covers at-rules and nested-decl
1067
+ // rules that have no selector of their own). A style rule with nested
1068
+ // children resolves its own selector first and becomes the new scope.
1069
+ let childParent = parentSelector;
1070
+ if (!atRulePrelude && rule.selectorText) {
1071
+ childParent = resolveNestedSelector(rule.selectorText, parentSelector);
1072
+ }
1073
+ for (const inner of walkCSSRules(rule.cssRules, childParent)) {
1046
1074
  if (atRulePrelude && inner.selectorText) {
1047
1075
  result.push({
1048
1076
  selectorText: inner.selectorText,
@@ -1058,7 +1086,7 @@
1058
1086
  // @charset, @counter-style, etc.) are skipped — they can't contain
1059
1087
  // interactive pseudos.
1060
1088
  result.push({
1061
- selectorText: rule.selectorText,
1089
+ selectorText: resolveNestedSelector(rule.selectorText, parentSelector),
1062
1090
  style: rule.style,
1063
1091
  wrapper: null
1064
1092
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/dom",
3
- "version": "1.32.3-beta.1",
3
+ "version": "1.32.3-beta.3",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,5 +35,5 @@
35
35
  "devDependencies": {
36
36
  "interactor.js": "^2.0.0-beta.10"
37
37
  },
38
- "gitHead": "92723d8feb00ce604c67dcd1e28c93b498c6996f"
38
+ "gitHead": "f2029c138431bb176dd41ea0d1df1f3a3120cc8d"
39
39
  }