@astryxdesign/core 0.7.2-canary.5c75a75 → 0.7.2-canary.81b8df4

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.
@@ -172,7 +172,7 @@ const styles = {
172
172
  kI3sdo: "x1a2a7pz",
173
173
  k7VYTy: "xpdipgo",
174
174
  kfSwDN: "x87ps6o",
175
- ks0D6T: "x1p5vr2b",
175
+ ks0D6T: "xc0b1yq",
176
176
  kskxy: "x1uh1i8a",
177
177
  "--_dropdown-menu-radius": "x1fcsqxe",
178
178
  "--_dropdown-menu-padding": "xgory14",
@@ -659,16 +659,16 @@ export function DropdownMenuSubMenu(props) {
659
659
  ...menuPress.menuProps,
660
660
  ...mergeProps(themeProps('dropdown-menu'), {
661
661
  0: {
662
- className: "x9f619 x78zum5 xdt5ytf x1lsbc85 x1p5vr2b x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o x1j4r261"
662
+ className: "x9f619 x78zum5 xdt5ytf x1lsbc85 xc0b1yq x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o x1j4r261"
663
663
  },
664
664
  2: {
665
- className: "x9f619 x78zum5 xdt5ytf x1lsbc85 x1p5vr2b x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o xge0x1e xish69e"
665
+ className: "x9f619 x78zum5 xdt5ytf x1lsbc85 xc0b1yq x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o xge0x1e xish69e"
666
666
  },
667
667
  1: {
668
- className: "x9f619 x78zum5 xdt5ytf x1lsbc85 x1p5vr2b x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o x1j4r261 x1odjw0f x6ikm8r xish69e"
668
+ className: "x9f619 x78zum5 xdt5ytf x1lsbc85 xc0b1yq x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o x1j4r261 x1odjw0f x6ikm8r xish69e"
669
669
  },
670
670
  3: {
671
- className: "x9f619 x78zum5 xdt5ytf x1lsbc85 x1p5vr2b x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o xge0x1e x1odjw0f x6ikm8r xish69e"
671
+ className: "x9f619 x78zum5 xdt5ytf x1lsbc85 xc0b1yq x1uh1i8a x1fcsqxe xgory14 x9epnlk x1n97fys x1prclbq x1i5ehqx x1hc1fzr x19991ni xuedmi6 xlr8y92 xpdipgo x87ps6o xge0x1e x1odjw0f x6ikm8r xish69e"
672
672
  }
673
673
  }[!!hasOverflow << 1 | !!hasOverflow << 0]),
674
674
  children: nestedContent
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * The viewport gutter and the caps derived from it. Every value has an
3
- * `env()`-less fallback for engines without safe-area support; pair them with
4
- * `stylex.firstThatWorks(cap, capFallback)`.
3
+ * `env()`-less fallback for engines without safe-area support; pair them as
4
+ * `stylex.firstThatWorks(`calc(${cap})`, `calc(${capFallback})`)`. Pass
5
+ * each inside a `calc()` (or another function), never as a bare reference: a
6
+ * constant reference compiles to a `var()`, and `firstThatWorks` folds two
7
+ * `var()` arguments into one `var(cap, capFallback)` whose constant is never
8
+ * inlined, so every engine would take the fallback.
5
9
  */
6
10
  export declare const layerViewportInset: {
7
11
  /** Gutter from the block-start viewport edge. */
@@ -1 +1 @@
1
- {"version":3,"file":"layerViewportInset.stylex.d.ts","sourceRoot":"","sources":["../../src/Layer/layerViewportInset.stylex.ts"],"names":[],"mappings":"AAwDA;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;IAC7B,iDAAiD;;IAEjD,+CAA+C;;IAE/C,+CAA+C;;IAE/C,gFAAgF;;;;;;IAMhF,iFAAiF;;;IAGjF,gFAAgF;;;CAGhF,CAAC"}
1
+ {"version":3,"file":"layerViewportInset.stylex.d.ts","sourceRoot":"","sources":["../../src/Layer/layerViewportInset.stylex.ts"],"names":[],"mappings":"AAwDA;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;IAC7B,iDAAiD;;IAEjD,+CAA+C;;IAE/C,+CAA+C;;IAE/C,gFAAgF;;;;;;IAMhF,iFAAiF;;;IAGjF,gFAAgF;;;CAGhF,CAAC"}
@@ -54,8 +54,12 @@ const GUTTER_BLOCK = `calc(max(${SPACING}, env(safe-area-inset-top, 0px), env(sa
54
54
 
55
55
  /**
56
56
  * The viewport gutter and the caps derived from it. Every value has an
57
- * `env()`-less fallback for engines without safe-area support; pair them with
58
- * `stylex.firstThatWorks(cap, capFallback)`.
57
+ * `env()`-less fallback for engines without safe-area support; pair them as
58
+ * `stylex.firstThatWorks(`calc(${cap})`, `calc(${capFallback})`)`. Pass
59
+ * each inside a `calc()` (or another function), never as a bare reference: a
60
+ * constant reference compiles to a `var()`, and `firstThatWorks` folds two
61
+ * `var()` arguments into one `var(cap, capFallback)` whose constant is never
62
+ * inlined, so every engine would take the fallback.
59
63
  */
60
64
  export const layerViewportInset = {
61
65
  gutterBlockStart: "calc(max(var(--spacing-4), env(safe-area-inset-top, 0px)) + var(--astryx-layer-inset-block-start, 0px))",
@@ -1 +1 @@
1
- {"version":3,"file":"useLayer.d.ts","sourceRoot":"","sources":["../../src/Layer/useLayer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAuHnD;;GAEG;AACH,UAAU,kBAAkB;IAC1B,aAAa,EAAE,KAAK,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;IACtD;;;OAGG;IACH,cAAc,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CACtD;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAClC;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV;;OAEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAC3D,IAAI,EAAE,SAAS,CAAC;IAChB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,GAAG,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAE9B;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,kBAAkB,KAAK,SAAS,CAAC;CACxE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,GAAG,EAAE,SAAS,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,KAAK,SAAS,CAAC;CACrE;AAED,UAAU,mBAAmB;IAC3B,gBAAgB,EAAE,MAAM,OAAO,CAAC;CACjC;AAED,KAAK,0BAA0B,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;AAC3E,KAAK,wBAAwB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAwHvE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,cAAwB,EACnC,SAAS,GAAE,cAAyB,GACnC,MAAM,CAiBR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,cAAc,EACzB,cAAc,EAAE,OAAO,GACtB,KAAK,CAAC,aAAa,CAQrB;AA4BD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,GACd,kBAAkB,CAoCpB;AAupBD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAAC;AAC3E,wBAAgB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAAC;AAWvE,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,mBAAmB,GAC3B,0BAA0B,CAAC;AAC9B,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,GACzB,wBAAwB,CAAC"}
1
+ {"version":3,"file":"useLayer.d.ts","sourceRoot":"","sources":["../../src/Layer/useLayer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAyHnD;;GAEG;AACH,UAAU,kBAAkB;IAC1B,aAAa,EAAE,KAAK,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;IACtD;;;OAGG;IACH,cAAc,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CACtD;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAClC;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV;;OAEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAC3D,IAAI,EAAE,SAAS,CAAC;IAChB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,GAAG,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAE9B;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,kBAAkB,KAAK,SAAS,CAAC;CACxE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,GAAG,EAAE,SAAS,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,KAAK,SAAS,CAAC;CACrE;AAED,UAAU,mBAAmB;IAC3B,gBAAgB,EAAE,MAAM,OAAO,CAAC;CACjC;AAED,KAAK,0BAA0B,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;AAC3E,KAAK,wBAAwB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAwHvE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,cAAwB,EACnC,SAAS,GAAE,cAAyB,GACnC,MAAM,CAiBR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,cAAc,EACzB,cAAc,EAAE,OAAO,GACtB,KAAK,CAAC,aAAa,CAQrB;AA4BD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,GACd,kBAAkB,CAoCpB;AAupBD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAAC;AAC3E,wBAAgB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAAC;AAWvE,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,mBAAmB,GAC3B,0BAA0B,CAAC;AAC9B,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,GACzB,wBAAwB,CAAC"}
@@ -104,24 +104,24 @@ const styles = {
104
104
  },
105
105
  viewportFit: {
106
106
  kB7OPa: "x9f619",
107
- ks0D6T: "x1p5vr2b",
108
- kskxy: "xv8yize",
107
+ ks0D6T: "xc0b1yq",
108
+ kskxy: "x3v4n1",
109
109
  $$css: true
110
110
  },
111
111
  gutterInlineEnd: {
112
- k71WvV: "x1m9hk3x",
112
+ k71WvV: "x144kxal",
113
113
  $$css: true
114
114
  },
115
115
  gutterInlineStart: {
116
- keTefX: "x1ey4id",
116
+ keTefX: "xpdk1p0",
117
117
  $$css: true
118
118
  },
119
119
  gutterBlockEnd: {
120
- k1K539: "x1chv945",
120
+ k1K539: "x3z50q0",
121
121
  $$css: true
122
122
  },
123
123
  gutterBlockStart: {
124
- keoZOQ: "x3390rn",
124
+ keoZOQ: "x1j6tnbc",
125
125
  $$css: true
126
126
  }
127
127
  };
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelector.d.ts","sourceRoot":"","sources":["../../src/MultiSelector/MultiSelector.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelector.d.ts","sourceRoot":"","sources":["../../src/MultiSelector/MultiSelector.tsx"],"names":[],"mappings":"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"}
@@ -29,13 +29,12 @@ import { PanelSearchInput } from "../Field/PanelSearchInput.js";
29
29
  import { CheckboxInput } from "../CheckboxInput/index.js";
30
30
  import { Badge } from "../Badge/index.js";
31
31
  import "../theme/tokens.stylex.js";
32
- import { colorVars, sizeVars, spacingVars, radiusVars, durationVars, easeVars, typographyVars, fontWeightVars, typeScaleVars } from "../theme/tokens.stylex.js";
32
+ import { colorVars, focusVars, sizeVars, spacingVars, radiusVars, durationVars, easeVars, typographyVars, fontWeightVars, typeScaleVars } from "../theme/tokens.stylex.js";
33
33
  import { isOptionData, isDivider, isSection, normalizeOption, getSelectableOptions } from "../Selector/utils.js";
34
34
  import { useMultiCombobox } from "./hooks.js";
35
35
  import { getInputARIA, isImeKeyEvent, mergeProps } from "../utils/index.js";
36
36
  import { devWarn, warnOnce } from "../utils/devWarning.js";
37
- import "../utils/focusOutline.stylex.js";
38
- import { FOCUS_OUTLINE_PARTS } from "../utils/focusOutline.stylex.js";
37
+ import { getInteractionModality, useInteractionModalityTracking } from "../utils/interactionModality.js";
39
38
  import { useAnnounce } from "../hooks/useAnnounce.js";
40
39
  import { FOCUSABLE_SELECTOR } from "../hooks/focusableSelector.js";
41
40
  import { useAnnounceRenderedText } from "../hooks/useAnnounceRenderedText.js";
@@ -894,8 +893,8 @@ export function MultiSelector({
894
893
  getActionCellId,
895
894
  activeDescendantId,
896
895
  onTriggerClick,
897
- onKeyDown,
898
- onItemMouseEnter
896
+ onKeyDown: onComboboxKeyDown,
897
+ onItemMouseEnter: onComboboxItemMouseEnter
899
898
  } = useMultiCombobox({
900
899
  selectableItems: sortedItems,
901
900
  isDisabled: isDisabled || isEffectivelyReadOnly,
@@ -918,6 +917,21 @@ export function MultiSelector({
918
917
  }, [getActionCellIdForActivate])
919
918
  });
920
919
 
920
+ // A hover moves the highlight too, but the focus ring follows the shared
921
+ // interaction modality (architecture:interaction-modality INV1): it is read
922
+ // whenever a key or the pointer moves the highlight, so an action cell
923
+ // highlighted after pointer input keeps the hover treatment and no ring.
924
+ useInteractionModalityTracking();
925
+ const [isKeyboardHighlight, setIsKeyboardHighlight] = useState(() => getInteractionModality() === 'keyboard');
926
+ const onKeyDown = useCallback(event => {
927
+ onComboboxKeyDown(event);
928
+ setIsKeyboardHighlight(getInteractionModality() === 'keyboard');
929
+ }, [onComboboxKeyDown]);
930
+ const onItemMouseEnter = useCallback((...args) => {
931
+ onComboboxItemMouseEnter(...args);
932
+ setIsKeyboardHighlight(getInteractionModality() === 'keyboard');
933
+ }, [onComboboxItemMouseEnter]);
934
+
921
935
  // The trigger button owns its click so a consumer `onClick`, which lands on
922
936
  // the button, composes after the built-in toggle. A press elsewhere in the
923
937
  // control box (padding, start icon, chevron) still opens the panel.
@@ -1179,14 +1193,14 @@ export function MultiSelector({
1179
1193
  className: "x78zum5 x6s0dn4 x2lah0s xh6dtrn"
1180
1194
  },
1181
1195
  1: {
1182
- className: "x78zum5 x6s0dn4 x2lah0s xh6dtrn x19wbafz xy741jp xb9bu2e xgg8d26"
1196
+ className: "x78zum5 x6s0dn4 x2lah0s xh6dtrn xak0ai5 x9ssot5 x19bwoy8 x1eqjuhu"
1183
1197
  }
1184
- }[!!(isHighlighted && highlightedCell === 'action') << 0],
1198
+ }[!!(isHighlighted && highlightedCell === 'action' && isKeyboardHighlight) << 0],
1185
1199
  children: isSelectAll || isCreate ? null : item.action
1186
1200
  })]
1187
1201
  }) : content
1188
1202
  }, item.value);
1189
- }, [isGrid, highlightedCell, getActionCellId, renderOption, indicatorPosition, highlightedIndex, optimisticValue, allEnabledSelected, selectAllState, selectAllLabel, getItemId, handleNavigableToggle, onItemMouseEnter, size, t]);
1203
+ }, [isGrid, highlightedCell, isKeyboardHighlight, getActionCellId, renderOption, indicatorPosition, highlightedIndex, optimisticValue, allEnabledSelected, selectAllState, selectAllLabel, getItemId, handleNavigableToggle, onItemMouseEnter, size, t]);
1190
1204
 
1191
1205
  // Render all options. Uses sortedItems as the single source of truth for
1192
1206
  // item order — no independent sorting here. Structural elements (dividers,
@@ -6,8 +6,10 @@
6
6
  * built Storybook.
7
7
  * @output Real-engine proof for option actions: the action is visible at rest
8
8
  * beside its row (`spec:AST-058` FR8), the keyboard reaches it through the
9
- * inline-end arrow in both directions and fires it with Enter, and the
10
- * highlight follows a real mouse across rows and actions.
9
+ * inline-end arrow in both directions and fires it with Enter, the active
10
+ * action cell draws the standard focus ring when the shared input modality
11
+ * is keyboard and not after pointer input, and the highlight follows a real
12
+ * mouse across rows and actions.
11
13
  * @position Run with `pnpm test:a11y-contract`; paint, pointer paths and the
12
14
  * active descendant as an engine exposes them are not jsdom claims.
13
15
  */
@@ -105,6 +107,102 @@ test('the keyboard reaches an action through the inline-end arrow and fires it w
105
107
  await search.press('ArrowLeft');
106
108
  await expect.poll(async () => activeDescendant(page)).toBe('row:Bug');
107
109
  });
110
+ test('the active action cell draws the standard focus ring', async ({
111
+ page
112
+ }) => {
113
+ await page.goto(`${storybook.origin}/iframe.html?id=core-multiselector--row-actions&viewMode=story`);
114
+ const search = page.getByRole('combobox');
115
+ await expect(search).toBeFocused();
116
+ await search.press('ArrowDown'); // select-all
117
+ await search.press('ArrowDown'); // Feature (no action)
118
+ await search.press('ArrowDown'); // Bug
119
+ await search.press('ArrowRight');
120
+ await expect.poll(async () => activeDescendant(page)).toBe('gridcell:Edit Bug');
121
+ const ring = await page.evaluate(() => {
122
+ const id = document.activeElement?.getAttribute('aria-activedescendant');
123
+ const cell = id ? document.getElementById(id) : null;
124
+ if (!cell) {
125
+ return null;
126
+ }
127
+ const cs = getComputedStyle(cell);
128
+ // The ring the focus tokens describe, resolved by the browser on a probe.
129
+ const probe = document.createElement('div');
130
+ probe.style.cssText = 'outline-width:var(--focus-outline-width);outline-style:var(--focus-outline-style);outline-color:var(--focus-outline-color);outline-offset:var(--focus-outline-offset)';
131
+ document.body.appendChild(probe);
132
+ const want = getComputedStyle(probe);
133
+ const result = {
134
+ style: cs.outlineStyle,
135
+ width: cs.outlineWidth,
136
+ color: cs.outlineColor,
137
+ offset: cs.outlineOffset,
138
+ wantStyle: want.outlineStyle,
139
+ wantWidth: want.outlineWidth,
140
+ wantColor: want.outlineColor,
141
+ wantOffset: want.outlineOffset
142
+ };
143
+ probe.remove();
144
+ return result;
145
+ });
146
+ expect(ring).not.toBeNull();
147
+ expect(ring.style).not.toBe('none');
148
+ expect(ring.width).not.toBe('0px');
149
+ expect(ring.style).toBe(ring.wantStyle);
150
+ expect(ring.width).toBe(ring.wantWidth);
151
+ expect(ring.color).toBe(ring.wantColor);
152
+ expect(ring.offset).toBe(ring.wantOffset);
153
+ // Back on the option cell, the action cell drops the ring.
154
+ await search.press('ArrowLeft');
155
+ await expect.poll(async () => activeDescendant(page)).toBe('row:Bug');
156
+ const after = await page.getByRole('row', {
157
+ name: 'Bug'
158
+ }).locator('[data-multi-selector-action-cell]').evaluate(cell => getComputedStyle(cell).outlineStyle);
159
+ expect(after).toBe('none');
160
+ });
161
+ test('the action-cell ring follows the shared input modality', async ({
162
+ page
163
+ }) => {
164
+ await page.goto(`${storybook.origin}/iframe.html?id=core-multiselector--row-actions&viewMode=story`);
165
+ const search = page.getByRole('combobox');
166
+ await expect(search).toBeFocused();
167
+ const actionCell = name => page.getByRole('row', {
168
+ name
169
+ }).locator('[data-multi-selector-action-cell]');
170
+ const outlineStyle = async name => actionCell(name).evaluate(cell => getComputedStyle(cell).outlineStyle);
171
+ const hover = async name => {
172
+ const cell = await box(actionCell(name));
173
+ await page.mouse.move(cell.x + cell.width / 2, cell.y + cell.height / 2, {
174
+ steps: 6
175
+ });
176
+ };
177
+
178
+ // Pointer input: a click into the search field, then a hover onto an
179
+ // action, moves the highlight without the ring, and leaves none behind.
180
+ await search.click();
181
+ await hover('Bug');
182
+ await expect.poll(async () => activeDescendant(page)).toBe('gridcell:Edit Bug');
183
+ expect(await outlineStyle('Bug')).toBe('none');
184
+ // A modifier chord is not keyboard navigation for the shared tracker.
185
+ await search.press('Control+ArrowRight');
186
+ expect(await outlineStyle('Bug')).toBe('none');
187
+ await page.mouse.move(1, 1, {
188
+ steps: 6
189
+ });
190
+ expect(await outlineStyle('Bug')).toBe('none');
191
+
192
+ // Keyboard input: back onto the same cell, the ring is drawn.
193
+ await search.press('ArrowLeft');
194
+ await expect.poll(async () => activeDescendant(page)).toBe('row:Bug');
195
+ await search.press('ArrowRight');
196
+ await expect.poll(async () => activeDescendant(page)).toBe('gridcell:Edit Bug');
197
+ expect(await outlineStyle('Bug')).not.toBe('none');
198
+
199
+ // Still keyboard-driven, a hover that moves the highlight to another
200
+ // row's action carries the ring with it.
201
+ await hover('P0');
202
+ await expect.poll(async () => activeDescendant(page)).toBe('gridcell:Edit P0');
203
+ expect(await outlineStyle('P0')).not.toBe('none');
204
+ expect(await outlineStyle('Bug')).toBe('none');
205
+ });
108
206
  test('under RTL the inline-end arrow is ArrowLeft', async ({
109
207
  page
110
208
  }) => {
@@ -60,8 +60,8 @@ function findTriggerButton(el) {
60
60
  const styles = {
61
61
  surfaceViewportFit: {
62
62
  kB7OPa: "x9f619",
63
- ks0D6T: "x1p5vr2b",
64
- kskxy: "xv8yize",
63
+ ks0D6T: "xc0b1yq",
64
+ kskxy: "x3v4n1",
65
65
  $$css: true
66
66
  },
67
67
  surfaceScrollable: {
package/dist/astryx.css CHANGED
@@ -1115,10 +1115,10 @@
1115
1115
  .xkd40ry:not(#\#):not(#\#):not(#\#){margin-inline-end:calc(-1 * var(--spacing-1))}
1116
1116
  .x3kzqx6:not(#\#):not(#\#):not(#\#){margin-inline-end:calc(-1 * var(--spacing-2))}
1117
1117
  .xmequkd:not(#\#):not(#\#):not(#\#){margin-inline-end:calc(var(--spacing-1) * -1)}
1118
+ .x144kxal:not(#\#):not(#\#):not(#\#){margin-inline-end:calc(calc(var(--spacing-4) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px))));margin-inline-end:calc(calc(max(var(--spacing-4), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px))))}
1118
1119
  .xnur1sd:not(#\#):not(#\#):not(#\#){margin-inline-end:var(--spacing-1)}
1119
1120
  .x1pezmd8:not(#\#):not(#\#):not(#\#){margin-inline-end:var(--spacing-2)}
1120
1121
  .x1c9tiao:not(#\#):not(#\#):not(#\#){margin-inline-end:var(--x-marginInlineEnd)}
1121
- .x1m9hk3x:not(#\#):not(#\#):not(#\#){margin-inline-end:var(--x3pzc91,calc(var(--spacing-4) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px))))}
1122
1122
  .xa3ewmy:not(#\#):not(#\#):not(#\#){margin-inline-end:calc(max(var(--spacing-4), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px)))}
1123
1123
  .x1qfufaz:not(#\#):not(#\#):not(#\#){margin-inline-start:-8px}
1124
1124
  .x1lziwak:not(#\#):not(#\#):not(#\#){margin-inline-start:0}
@@ -1134,6 +1134,7 @@
1134
1134
  .x1idpjx9:not(#\#):not(#\#):not(#\#){margin-inline-start:calc((40px - min(var(--spacing-6),40px)) / 2 - var(--spacing-4))}
1135
1135
  .x1xrm450:not(#\#):not(#\#):not(#\#){margin-inline-start:calc(var(--spacing-2) - var(--spacing-1) + 1px)}
1136
1136
  .x6w1cxl:not(#\#):not(#\#):not(#\#){margin-inline-start:calc(var(--spacing-3) + var(--border-width) + var(--spacing-4))}
1137
+ .xpdk1p0:not(#\#):not(#\#):not(#\#){margin-inline-start:calc(calc(var(--spacing-4) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px))));margin-inline-start:calc(calc(max(var(--spacing-4), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px))))}
1137
1138
  .x13hpdyo:not(#\#):not(#\#):not(#\#){margin-inline-start:var(--_avatar-group-overlap)}
1138
1139
  .x12vn87o:not(#\#):not(#\#):not(#\#){margin-inline-start:var(--_tree-indent,0px)}
1139
1140
  .xladpz9:not(#\#):not(#\#):not(#\#){margin-inline-start:var(--spacing-0-5)}
@@ -1142,7 +1143,6 @@
1142
1143
  .x6i7dpz:not(#\#):not(#\#):not(#\#){margin-inline-start:var(--spacing-3)}
1143
1144
  .x1l10yog:not(#\#):not(#\#):not(#\#){margin-inline-start:var(--spacing-6)}
1144
1145
  .x4lel18:not(#\#):not(#\#):not(#\#){margin-inline-start:var(--x-marginInlineStart)}
1145
- .x1ey4id:not(#\#):not(#\#):not(#\#){margin-inline-start:var(--x3pzc91,calc(var(--spacing-4) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px))))}
1146
1146
  .x1cytvie:not(#\#):not(#\#):not(#\#){margin-inline-start:calc(max(var(--spacing-4), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)) + max(var(--astryx-layer-inset-inline-start, 0px), var(--astryx-layer-inset-inline-end, 0px)))}
1147
1147
  .xkm2don:not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to bottom,transparent,black 24px)}
1148
1148
  .xw26lqc:not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to bottom,transparent,black 36px)}
@@ -1170,14 +1170,14 @@
1170
1170
  .x1i7u4iq:not(#\#):not(#\#):not(#\#){opacity:var(--_toast-swipe-opacity,1)}
1171
1171
  .x1clqncf:not(#\#):not(#\#):not(#\#){order:-1}
1172
1172
  .x1g77sc7:not(#\#):not(#\#):not(#\#){order:0}
1173
- .xb9bu2e:not(#\#):not(#\#):not(#\#){outline-color:var(--x18622n)}
1173
+ .x19bwoy8:not(#\#):not(#\#):not(#\#){outline-color:var(--focus-outline-color)}
1174
1174
  .x1wfwxd8:not(#\#):not(#\#):not(#\#){outline-offset:0}
1175
1175
  .x1kvmbwa:not(#\#):not(#\#):not(#\#){outline-offset:var(--_focus-outline-offset,0)}
1176
- .xgg8d26:not(#\#):not(#\#):not(#\#){outline-offset:var(--x8bm18s)}
1176
+ .x1eqjuhu:not(#\#):not(#\#):not(#\#){outline-offset:var(--focus-outline-offset)}
1177
1177
  .x1t137rt:not(#\#):not(#\#):not(#\#){outline-style:none}
1178
- .xy741jp:not(#\#):not(#\#):not(#\#){outline-style:var(--xy2juf7)}
1178
+ .x9ssot5:not(#\#):not(#\#):not(#\#){outline-style:var(--focus-outline-style)}
1179
1179
  .x1k57tk5:not(#\#):not(#\#):not(#\#){outline-width:0}
1180
- .x19wbafz:not(#\#):not(#\#):not(#\#){outline-width:var(--x1hdvljp)}
1180
+ .xak0ai5:not(#\#):not(#\#):not(#\#){outline-width:var(--focus-outline-width)}
1181
1181
  .x9y5y3v:not(#\#):not(#\#):not(#\#){overflow-clip-margin:1px}
1182
1182
  .xj0a0fe:not(#\#):not(#\#):not(#\#){overflow-wrap:anywhere}
1183
1183
  .x1mzt3pk:not(#\#):not(#\#):not(#\#){overflow-wrap:break-word}
@@ -1757,6 +1757,7 @@
1757
1757
  .x6h7pi7:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:calc(-1 * var(--container-padding-block-end,0px))}
1758
1758
  .x1cdrix3:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:calc(-1 * var(--radius-chat))}
1759
1759
  .xdrfirk:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:calc(-1 * var(--spacing-1-5))}
1760
+ .x3z50q0:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:calc(calc(var(--spacing-4) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px))));margin-bottom:calc(calc(max(var(--spacing-4), env(safe-area-inset-top, 0px), env(safe-area-inset-bottom, 0px)) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px))))}
1760
1761
  .xlstkdb:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:var(--spacing-0-5)}
1761
1762
  .x14cgwvg:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:var(--spacing-1)}
1762
1763
  .x1p37lm5:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:var(--spacing-2)}
@@ -1764,7 +1765,6 @@
1764
1765
  .xwh1t77:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:var(--spacing-4)}
1765
1766
  .x4d5cxa:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:var(--spacing-6)}
1766
1767
  .xlm3tn6:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:var(--x-marginBlockEnd)}
1767
- .x1chv945:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:var(--x1ot8se4,calc(var(--spacing-4) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px))))}
1768
1768
  .x119uzjn:not(#\#):not(#\#):not(#\#):not(#\#){margin-bottom:calc(max(var(--spacing-4), env(safe-area-inset-top, 0px), env(safe-area-inset-bottom, 0px)) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px)))}
1769
1769
  .xdj266r:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:0}
1770
1770
  .xr1yuqi:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:auto}
@@ -1778,6 +1778,7 @@
1778
1778
  .x1ajqlyv:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:calc(-1 * var(--spacing-2))}
1779
1779
  .x1oms9s6:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:calc((1em * var(--text-body-leading) - 20px) / 2)}
1780
1780
  .x1233pnv:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:calc((1em * var(--text-body-leading) - 6px) / 2)}
1781
+ .x1j6tnbc:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:calc(calc(var(--spacing-4) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px))));margin-top:calc(calc(max(var(--spacing-4), env(safe-area-inset-top, 0px), env(safe-area-inset-bottom, 0px)) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px))))}
1781
1782
  .xjc2qm6:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:var(--spacing-0-5)}
1782
1783
  .xcsaf9d:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:var(--spacing-1)}
1783
1784
  .xtbrsbv:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:var(--spacing-2)}
@@ -1785,7 +1786,6 @@
1785
1786
  .x1s1qpeq:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:var(--spacing-4)}
1786
1787
  .x13rv44w:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:var(--spacing-6)}
1787
1788
  .x1vhfslr:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:var(--x-marginBlockStart)}
1788
- .x3390rn:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:var(--x1ot8se4,calc(var(--spacing-4) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px))))}
1789
1789
  .x1lhixnp:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:calc(max(var(--spacing-4), env(safe-area-inset-top, 0px), env(safe-area-inset-bottom, 0px)) + max(var(--astryx-layer-inset-block-start, 0px), var(--astryx-layer-inset-block-end, 0px)))}
1790
1790
  .xmz0i5r:not(#\#):not(#\#):not(#\#):not(#\#){max-height:100%}
1791
1791
  .x1wj9ous:not(#\#):not(#\#):not(#\#):not(#\#){max-height:100dvh}
@@ -1794,13 +1794,13 @@
1794
1794
  .xuyqlj2:not(#\#):not(#\#):not(#\#):not(#\#){max-height:300px}
1795
1795
  .xbhvqqn:not(#\#):not(#\#):not(#\#):not(#\#){max-height:calc(100% - var(--spacing-3))}
1796
1796
  .x14bu9tk:not(#\#):not(#\#):not(#\#):not(#\#){max-height:calc(92dvh + 48px)}
1797
+ .x3v4n1:not(#\#):not(#\#):not(#\#):not(#\#){max-height:calc(calc(100vh - var(--spacing-4) - var(--spacing-4) - var(--astryx-layer-inset-block-start, 0px) - var(--astryx-layer-inset-block-end, 0px)));max-height:calc(calc(100dvb - calc(max(var(--spacing-4), env(safe-area-inset-top, 0px)) + var(--astryx-layer-inset-block-start, 0px)) - calc(max(var(--spacing-4), env(safe-area-inset-bottom, 0px)) + var(--astryx-layer-inset-block-end, 0px))))}
1797
1798
  .x1uh1i8a:not(#\#):not(#\#):not(#\#):not(#\#){max-height:min(300px,calc(100vh - var(--spacing-4) - var(--spacing-4) - var(--astryx-layer-inset-block-start, 0px) - var(--astryx-layer-inset-block-end, 0px)));max-height:min(300px,calc(100dvb - calc(max(var(--spacing-4), env(safe-area-inset-top, 0px)) + var(--astryx-layer-inset-block-start, 0px)) - calc(max(var(--spacing-4), env(safe-area-inset-bottom, 0px)) + var(--astryx-layer-inset-block-end, 0px))))}
1798
1799
  .xpnbb6b:not(#\#):not(#\#):not(#\#):not(#\#){max-height:min(480px,calc(100vh - 32px))}
1799
1800
  .x7ab17h:not(#\#):not(#\#):not(#\#):not(#\#){max-height:none}
1800
1801
  .xenllk4:not(#\#):not(#\#):not(#\#):not(#\#){max-height:var(--container-max-height,none)}
1801
1802
  .xbmfmqd:not(#\#):not(#\#):not(#\#):not(#\#){max-height:var(--x-maxBlockSize)}
1802
1803
  .x1jols5v:not(#\#):not(#\#):not(#\#):not(#\#){max-height:var(--x-maxHeight)}
1803
- .xv8yize:not(#\#):not(#\#):not(#\#):not(#\#){max-height:var(--x17ekfff,calc(100vh - var(--spacing-4) - var(--spacing-4) - var(--astryx-layer-inset-block-start, 0px) - var(--astryx-layer-inset-block-end, 0px)))}
1804
1804
  .x1m189uc:not(#\#):not(#\#):not(#\#):not(#\#){max-width:0}
1805
1805
  .x193iq5w:not(#\#):not(#\#):not(#\#):not(#\#){max-width:100%}
1806
1806
  .xlbgzzq:not(#\#):not(#\#):not(#\#):not(#\#){max-width:100dvw}
@@ -1815,13 +1815,13 @@
1815
1815
  .x1ap80js:not(#\#):not(#\#):not(#\#):not(#\#){max-width:800px}
1816
1816
  .xq8v1hd:not(#\#):not(#\#):not(#\#):not(#\#){max-width:90%}
1817
1817
  .x1rfgw7b:not(#\#):not(#\#):not(#\#):not(#\#){max-width:calc(100% + var(--container-padding-inline-start,0px) + var(--container-padding-inline-end,0px))}
1818
+ .xc0b1yq:not(#\#):not(#\#):not(#\#):not(#\#){max-width:calc(calc(100vw - var(--spacing-4) - var(--spacing-4) - var(--astryx-layer-inset-inline-start, 0px) - var(--astryx-layer-inset-inline-end, 0px)));max-width:calc(calc(100vi - calc(max(var(--spacing-4), env(safe-area-inset-left, 0px)) + var(--astryx-layer-inset-inline-start, 0px)) - calc(max(var(--spacing-4), env(safe-area-inset-right, 0px)) + var(--astryx-layer-inset-inline-end, 0px))))}
1818
1819
  .xl6temz:not(#\#):not(#\#):not(#\#):not(#\#){max-width:max(80%,280px)}
1819
1820
  .x1m1aaou:not(#\#):not(#\#):not(#\#):not(#\#){max-width:min(960px,calc(100dvw - var(--spacing-4)))}
1820
1821
  .x1x1rfll:not(#\#):not(#\#):not(#\#):not(#\#){max-width:none}
1821
1822
  .xjl2t3p:not(#\#):not(#\#):not(#\#):not(#\#){max-width:var(--layout-content-width,none)}
1822
1823
  .x1jys5uj:not(#\#):not(#\#):not(#\#):not(#\#){max-width:var(--size-element-md)}
1823
1824
  .xf68679:not(#\#):not(#\#):not(#\#):not(#\#){max-width:var(--x-maxWidth)}
1824
- .x1p5vr2b:not(#\#):not(#\#):not(#\#):not(#\#){max-width:var(--xdig2zt,calc(100vw - var(--spacing-4) - var(--spacing-4) - var(--astryx-layer-inset-inline-start, 0px) - var(--astryx-layer-inset-inline-end, 0px)))}
1825
1825
  .x2lwn1j:not(#\#):not(#\#):not(#\#):not(#\#){min-height:0}
1826
1826
  .x1us19tq:not(#\#):not(#\#):not(#\#):not(#\#){min-height:100%}
1827
1827
  .x1ov3xa9:not(#\#):not(#\#):not(#\#):not(#\#){min-height:100dvh}
@@ -44,6 +44,10 @@ export declare const FOCUS_OUTLINE: string;
44
44
  * independently overridable — and every part stays a var, so a theme's
45
45
  * `--focus-outline-*` overrides still flow through.
46
46
  *
47
+ * NOT usable inside another file's `stylex.create`, for the same reason as
48
+ * {@link FOCUS_OUTLINE}: StyleX compiles each member there to a CSS variable
49
+ * nothing defines. Compose the `focusVars` tokens instead.
50
+ *
47
51
  * Keys are camelCase to match `HTMLElement.style`, so this spreads straight
48
52
  * onto an element:
49
53
  *
@@ -1 +1 @@
1
- {"version":3,"file":"focusOutline.stylex.d.ts","sourceRoot":"","sources":["../../src/utils/focusOutline.stylex.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAS3C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,QAAyE,CAAC;AAgBpG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,mBAAmB;;;;;CAKtB,CAAC;AAEX,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB;;;;;CAK3B,CAAC;AAEX,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiG7B,CAAC;AAIH,KAAK,cAAc,GAAG,OAAO,CAAC;AAiB9B,eAAO,MAAM,iBAAiB;uCAdT,cAAc,EAAE;;;;;;;sCAAhB,cAAc,EAAE;;;;;;;gDAAhB,cAAc,EAAE;;;;;;;qCAAhB,cAAc,EAAE;;;;;;;kDAAhB,cAAc,EAAE;;;;;;;iDAAhB,cAAc,EAAE;;;;;;;CA2B3B,CAAC"}
1
+ {"version":3,"file":"focusOutline.stylex.d.ts","sourceRoot":"","sources":["../../src/utils/focusOutline.stylex.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAS3C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,QAAyE,CAAC;AAgBpG;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,mBAAmB;;;;;CAKtB,CAAC;AAEX,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB;;;;;CAK3B,CAAC;AAEX,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiG7B,CAAC;AAIH,KAAK,cAAc,GAAG,OAAO,CAAC;AAiB9B,eAAO,MAAM,iBAAiB;uCAdT,cAAc,EAAE;;;;;;;sCAAhB,cAAc,EAAE;;;;;;;gDAAhB,cAAc,EAAE;;;;;;;qCAAhB,cAAc,EAAE;;;;;;;kDAAhB,cAAc,EAAE;;;;;;;iDAAhB,cAAc,EAAE;;;;;;;CA2B3B,CAAC"}
@@ -69,6 +69,10 @@ const focusOutlineLonghands = {
69
69
  * independently overridable — and every part stays a var, so a theme's
70
70
  * `--focus-outline-*` overrides still flow through.
71
71
  *
72
+ * NOT usable inside another file's `stylex.create`, for the same reason as
73
+ * {@link FOCUS_OUTLINE}: StyleX compiles each member there to a CSS variable
74
+ * nothing defines. Compose the `focusVars` tokens instead.
75
+ *
72
76
  * Keys are camelCase to match `HTMLElement.style`, so this spreads straight
73
77
  * onto an element:
74
78
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.7.2-canary.5c75a75",
3
+ "version": "0.7.2-canary.81b8df4",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -787,8 +787,8 @@
787
787
  "react-dom": ">=19.0.0"
788
788
  },
789
789
  "devDependencies": {
790
- "@astryxdesign/a11y-spec": "0.7.2-canary.5c75a75",
791
- "@astryxdesign/cli": "0.7.2-canary.5c75a75",
790
+ "@astryxdesign/a11y-spec": "0.7.2-canary.81b8df4",
791
+ "@astryxdesign/cli": "0.7.2-canary.81b8df4",
792
792
  "@babel/cli": "^7.29.7",
793
793
  "@babel/core": "^7.29.7",
794
794
  "@babel/preset-react": "^7.29.7",
@@ -229,8 +229,8 @@ const styles = stylex.create({
229
229
  WebkitTouchCallout: 'none',
230
230
  userSelect: 'none',
231
231
  maxInlineSize: stylex.firstThatWorks(
232
- layerViewportInset.maxInlineSize,
233
- layerViewportInset.maxInlineSizeFallback,
232
+ `calc(${layerViewportInset.maxInlineSize})`,
233
+ `calc(${layerViewportInset.maxInlineSizeFallback})`,
234
234
  ),
235
235
  maxHeight: stylex.firstThatWorks(
236
236
  MENU_MAX_BLOCK_SIZE,
@@ -166,8 +166,8 @@ const flyoutStyles = stylex.create({
166
166
  flexDirection: 'column',
167
167
  gap: spacingVars['--spacing-0-5'],
168
168
  maxInlineSize: stylex.firstThatWorks(
169
- layerViewportInset.maxInlineSize,
170
- layerViewportInset.maxInlineSizeFallback,
169
+ `calc(${layerViewportInset.maxInlineSize})`,
170
+ `calc(${layerViewportInset.maxInlineSizeFallback})`,
171
171
  ),
172
172
  maxHeight: stylex.firstThatWorks(
173
173
  MENU_MAX_BLOCK_SIZE,
@@ -0,0 +1,90 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file layerViewportInset.source-build.test.mjs
5
+ * @input Uses Babel, the core StyleX build config, the StyleX rule processor,
6
+ * and the sources that cap or inset anchored layers
7
+ * @output Verifies the shipped stylesheet carries the dynamic-viewport,
8
+ * safe-area gutter and caps (spec:AST-059 FR1–FR3), not only the fallbacks
9
+ * @position Regression test for the compiled layer viewport inset
10
+ *
11
+ * `stylex.firstThatWorks(a, b)` turns two `var()` arguments into one nested
12
+ * `var(a, b)`. A `defineConsts` reference compiles to such a `var()`, and the
13
+ * rule processor inlines a constant only where its `var()` stands alone, so a
14
+ * pair of bare constant references shipped as `var(--<hash>, <fallback>)`
15
+ * with `--<hash>` defined nowhere: every engine took the fallback.
16
+ */
17
+
18
+ import fs from 'node:fs/promises';
19
+ import path from 'node:path';
20
+ import {fileURLToPath} from 'node:url';
21
+ import {transformAsync} from '@babel/core';
22
+ import stylexBabelPlugin from '@stylexjs/babel-plugin';
23
+ import {describe, expect, it} from 'vitest';
24
+
25
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
+ const SRC = path.resolve(__dirname, '..');
27
+ const CORE_ROOT = path.resolve(SRC, '..');
28
+
29
+ const SOURCES = [
30
+ 'Layer/layerViewportInset.stylex.ts',
31
+ 'Layer/useLayer.tsx',
32
+ 'Popover/Popover.tsx',
33
+ 'DropdownMenu/DropdownMenu.tsx',
34
+ 'DropdownMenu/DropdownMenuSubMenu.tsx',
35
+ ];
36
+
37
+ async function compileStylesheet() {
38
+ const rules = [];
39
+ for (const relative of SOURCES) {
40
+ const filename = path.join(SRC, relative);
41
+ const result = await transformAsync(await fs.readFile(filename, 'utf8'), {
42
+ babelrc: false,
43
+ configFile: path.join(CORE_ROOT, 'babel.config.json'),
44
+ filename,
45
+ });
46
+ rules.push(...(result?.metadata?.stylex ?? []));
47
+ }
48
+ return stylexBabelPlugin.processStylexRules(rules, false);
49
+ }
50
+
51
+ /** Every declaration of `property` in the compiled sheet. */
52
+ function declarations(css, property) {
53
+ return [...css.matchAll(new RegExp(`[{;]${property}:([^;}]+)`, 'g'))].map(
54
+ match => match[1],
55
+ );
56
+ }
57
+
58
+ describe('layer viewport inset stylesheet (spec:AST-059)', () => {
59
+ it('leaves no constant reference unresolved behind a fallback', async () => {
60
+ const css = await compileStylesheet();
61
+ const defined = new Set(
62
+ [...css.matchAll(/(--[\w-]+)\s*:/g)].map(match => match[1]),
63
+ );
64
+ const undefinedReferences = [
65
+ ...css.matchAll(/var\((--x[a-z0-9]+),/g),
66
+ ].flatMap(match => (defined.has(match[1]) ? [] : [match[0]]));
67
+ // Covers the caps and the four alignment-axis gutter margins.
68
+ expect(undefinedReferences).toEqual([]);
69
+ });
70
+
71
+ it('caps both axes against the dynamic viewport and the safe-area gutters', async () => {
72
+ const css = await compileStylesheet();
73
+ const blockCaps = declarations(css, 'max-height');
74
+ const inlineCaps = declarations(css, 'max-width');
75
+
76
+ expect(blockCaps).toContainEqual(
77
+ expect.stringMatching(/^calc\(calc\(100dvb - .*safe-area-inset-top/),
78
+ );
79
+ expect(inlineCaps).toContainEqual(
80
+ expect.stringMatching(/^calc\(calc\(100vi - .*safe-area-inset-left/),
81
+ );
82
+ // The older-engine fallbacks still ship, ahead of the preferred value.
83
+ expect(blockCaps).toContainEqual(
84
+ expect.stringMatching(/^calc\(calc\(100vh - /),
85
+ );
86
+ expect(inlineCaps).toContainEqual(
87
+ expect.stringMatching(/^calc\(calc\(100vw - /),
88
+ );
89
+ });
90
+ });
@@ -56,8 +56,12 @@ const GUTTER_BLOCK = `calc(max(${SPACING}, env(safe-area-inset-top, 0px), env(sa
56
56
 
57
57
  /**
58
58
  * The viewport gutter and the caps derived from it. Every value has an
59
- * `env()`-less fallback for engines without safe-area support; pair them with
60
- * `stylex.firstThatWorks(cap, capFallback)`.
59
+ * `env()`-less fallback for engines without safe-area support; pair them as
60
+ * `stylex.firstThatWorks(`calc(${cap})`, `calc(${capFallback})`)`. Pass
61
+ * each inside a `calc()` (or another function), never as a bare reference: a
62
+ * constant reference compiles to a `var()`, and `firstThatWorks` folds two
63
+ * `var()` arguments into one `var(cap, capFallback)` whose constant is never
64
+ * inlined, so every engine would take the fallback.
61
65
  */
62
66
  export const layerViewportInset = stylex.defineConsts({
63
67
  /** Gutter from the block-start viewport edge. */
@@ -102,15 +102,17 @@ const styles = stylex.create({
102
102
  // The cap is on the layer box itself: content wider than the viewport
103
103
  // overflows inside the layer, where the composing component decides
104
104
  // whether it scrolls or clips; the box never leaves the viewport.
105
+ // Each constant is wrapped in `calc()` so both declarations ship (see
106
+ // layerViewportInset).
105
107
  viewportFit: {
106
108
  boxSizing: 'border-box',
107
109
  maxInlineSize: stylex.firstThatWorks(
108
- layerViewportInset.maxInlineSize,
109
- layerViewportInset.maxInlineSizeFallback,
110
+ `calc(${layerViewportInset.maxInlineSize})`,
111
+ `calc(${layerViewportInset.maxInlineSizeFallback})`,
110
112
  ),
111
113
  maxBlockSize: stylex.firstThatWorks(
112
- layerViewportInset.maxBlockSize,
113
- layerViewportInset.maxBlockSizeFallback,
114
+ `calc(${layerViewportInset.maxBlockSize})`,
115
+ `calc(${layerViewportInset.maxBlockSizeFallback})`,
114
116
  ),
115
117
  },
116
118
  // Alignment-axis gutter (spec:AST-059 FR1): a margin on the far viewport
@@ -120,26 +122,26 @@ const styles = stylex.create({
120
122
  // (see layerSlideRules.ts).
121
123
  gutterInlineEnd: {
122
124
  marginInlineEnd: stylex.firstThatWorks(
123
- layerViewportInset.gutterInline,
124
- layerViewportInset.gutterInlineFallback,
125
+ `calc(${layerViewportInset.gutterInline})`,
126
+ `calc(${layerViewportInset.gutterInlineFallback})`,
125
127
  ),
126
128
  },
127
129
  gutterInlineStart: {
128
130
  marginInlineStart: stylex.firstThatWorks(
129
- layerViewportInset.gutterInline,
130
- layerViewportInset.gutterInlineFallback,
131
+ `calc(${layerViewportInset.gutterInline})`,
132
+ `calc(${layerViewportInset.gutterInlineFallback})`,
131
133
  ),
132
134
  },
133
135
  gutterBlockEnd: {
134
136
  marginBlockEnd: stylex.firstThatWorks(
135
- layerViewportInset.gutterBlock,
136
- layerViewportInset.gutterBlockFallback,
137
+ `calc(${layerViewportInset.gutterBlock})`,
138
+ `calc(${layerViewportInset.gutterBlockFallback})`,
137
139
  ),
138
140
  },
139
141
  gutterBlockStart: {
140
142
  marginBlockStart: stylex.firstThatWorks(
141
- layerViewportInset.gutterBlock,
142
- layerViewportInset.gutterBlockFallback,
143
+ `calc(${layerViewportInset.gutterBlock})`,
144
+ `calc(${layerViewportInset.gutterBlockFallback})`,
143
145
  ),
144
146
  },
145
147
  });
@@ -35,6 +35,7 @@ import {generateThemeCSS} from '../theme/generateThemeRules';
35
35
  import {selectorPresentationStyles} from '../Selector/selectorPresentation.stylex';
36
36
  import {groupStyles} from '../InputGroup/groupStyles';
37
37
  import {InputGroup, InputGroupText} from '../InputGroup';
38
+ import {focusVars} from '../theme/tokens.stylex';
38
39
 
39
40
  function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
40
41
  const {prose, component} = generateThemeCSS(theme);
@@ -4083,6 +4084,164 @@ describe('MultiSelector option actions (grid)', () => {
4083
4084
  expect(onChange).not.toHaveBeenCalled();
4084
4085
  });
4085
4086
 
4087
+ it('draws the standard focus ring on the active action cell, from the focus tokens', async () => {
4088
+ // jsdom resolves no custom property, so the ring is read from StyleX
4089
+ // classes: a class name hashes one property and its value, and the same
4090
+ // declaration yields the same class wherever it is written.
4091
+ const ring = stylex.create({
4092
+ active: {
4093
+ outlineWidth: focusVars['--focus-outline-width'],
4094
+ outlineStyle: focusVars['--focus-outline-style'],
4095
+ outlineColor: focusVars['--focus-outline-color'],
4096
+ outlineOffset: focusVars['--focus-outline-offset'],
4097
+ },
4098
+ });
4099
+ const ringClasses = stylex
4100
+ .props(ring.active)
4101
+ .className!.split(' ')
4102
+ .filter(token => /^x[a-z0-9]+$/.test(token));
4103
+ expect(ringClasses).toHaveLength(4);
4104
+
4105
+ const user = userEvent.setup();
4106
+ render(
4107
+ <MultiSelector
4108
+ label="Labels"
4109
+ options={OPTIONS}
4110
+ value={[]}
4111
+ onChange={() => {}}
4112
+ />,
4113
+ );
4114
+ const trigger = screen.getByRole('combobox', {name: 'Labels'});
4115
+ await user.click(trigger);
4116
+ const bug = screen.getByRole('row', {name: 'Bug', ...h});
4117
+ const actionCell = bug.children[1] as HTMLElement;
4118
+ for (const token of ringClasses) {
4119
+ expect(actionCell).not.toHaveClass(token);
4120
+ }
4121
+ fireEvent.keyDown(trigger, {key: 'ArrowRight'});
4122
+ expect(trigger).toHaveAttribute('aria-activedescendant', actionCell.id);
4123
+ for (const token of ringClasses) {
4124
+ expect(actionCell).toHaveClass(token);
4125
+ }
4126
+ });
4127
+
4128
+ it('draws no focus ring on an action cell highlighted after pointer input, until a key moves the highlight', async () => {
4129
+ const ring = stylex.create({
4130
+ active: {
4131
+ outlineWidth: focusVars['--focus-outline-width'],
4132
+ outlineStyle: focusVars['--focus-outline-style'],
4133
+ outlineColor: focusVars['--focus-outline-color'],
4134
+ outlineOffset: focusVars['--focus-outline-offset'],
4135
+ },
4136
+ });
4137
+ const ringClasses = stylex
4138
+ .props(ring.active)
4139
+ .className!.split(' ')
4140
+ .filter(token => /^x[a-z0-9]+$/.test(token));
4141
+ expect(ringClasses).toHaveLength(4);
4142
+
4143
+ const user = userEvent.setup();
4144
+ render(
4145
+ <MultiSelector
4146
+ label="Labels"
4147
+ options={OPTIONS}
4148
+ value={[]}
4149
+ onChange={() => {}}
4150
+ />,
4151
+ );
4152
+ const trigger = screen.getByRole('combobox', {name: 'Labels'});
4153
+ await user.click(trigger);
4154
+ const bug = screen.getByRole('row', {name: 'Bug', ...h});
4155
+ const actionCell = bug.children[1] as HTMLElement;
4156
+ fireEvent.mouseEnter(actionCell);
4157
+ // The hover moved the highlight onto the action cell, without the ring.
4158
+ expect(trigger).toHaveAttribute('aria-activedescendant', actionCell.id);
4159
+ for (const token of ringClasses) {
4160
+ expect(actionCell).not.toHaveClass(token);
4161
+ }
4162
+ // Keyboard navigation back onto the cell draws it.
4163
+ fireEvent.keyDown(trigger, {key: 'ArrowLeft'});
4164
+ fireEvent.keyDown(trigger, {key: 'ArrowRight'});
4165
+ expect(trigger).toHaveAttribute('aria-activedescendant', actionCell.id);
4166
+ for (const token of ringClasses) {
4167
+ expect(actionCell).toHaveClass(token);
4168
+ }
4169
+ });
4170
+
4171
+ describe('ring under mixed input', () => {
4172
+ const ringClasses = () => {
4173
+ const ring = stylex.create({
4174
+ active: {
4175
+ outlineWidth: focusVars['--focus-outline-width'],
4176
+ outlineStyle: focusVars['--focus-outline-style'],
4177
+ outlineColor: focusVars['--focus-outline-color'],
4178
+ outlineOffset: focusVars['--focus-outline-offset'],
4179
+ },
4180
+ });
4181
+ return stylex
4182
+ .props(ring.active)
4183
+ .className!.split(' ')
4184
+ .filter(token => /^x[a-z0-9]+$/.test(token));
4185
+ };
4186
+ const hasRing = (cell: HTMLElement) =>
4187
+ ringClasses().every(token => cell.classList.contains(token));
4188
+
4189
+ async function openWithKeyboardOnBugAction() {
4190
+ const user = userEvent.setup();
4191
+ render(
4192
+ <MultiSelector
4193
+ label="Labels"
4194
+ options={OPTIONS}
4195
+ value={[]}
4196
+ onChange={() => {}}
4197
+ />,
4198
+ );
4199
+ const trigger = screen.getByRole('combobox', {name: 'Labels'});
4200
+ await user.click(trigger);
4201
+ const row = (name: string) => screen.getByRole('row', {name, ...h});
4202
+ fireEvent.keyDown(trigger, {key: 'ArrowRight'});
4203
+ const bugAction = row('Bug').children[1] as HTMLElement;
4204
+ expect(trigger).toHaveAttribute('aria-activedescendant', bugAction.id);
4205
+ expect(hasRing(bugAction)).toBe(true);
4206
+ return {trigger, row, bugAction};
4207
+ }
4208
+
4209
+ it('keeps the ring when the pointer crosses a disabled option the highlight ignores', async () => {
4210
+ const {trigger, row, bugAction} = await openWithKeyboardOnBugAction();
4211
+ fireEvent.mouseEnter(row('Docs').children[0]);
4212
+ expect(trigger).toHaveAttribute('aria-activedescendant', bugAction.id);
4213
+ expect(hasRing(bugAction)).toBe(true);
4214
+ });
4215
+
4216
+ it('follows the keyboard modality when a hover moves the highlight to another action', async () => {
4217
+ const {trigger, row} = await openWithKeyboardOnBugAction();
4218
+ const docsAction = row('Docs').children[1] as HTMLElement;
4219
+ fireEvent.mouseEnter(docsAction);
4220
+ expect(trigger).toHaveAttribute('aria-activedescendant', docsAction.id);
4221
+ expect(hasRing(docsAction)).toBe(true);
4222
+ });
4223
+
4224
+ it('draws no ring for a modifier chord after pointer input', async () => {
4225
+ const user = userEvent.setup();
4226
+ render(
4227
+ <MultiSelector
4228
+ label="Labels"
4229
+ options={OPTIONS}
4230
+ value={[]}
4231
+ onChange={() => {}}
4232
+ />,
4233
+ );
4234
+ const trigger = screen.getByRole('combobox', {name: 'Labels'});
4235
+ await user.click(trigger);
4236
+ const bugAction = screen.getByRole('row', {name: 'Bug', ...h})
4237
+ .children[1] as HTMLElement;
4238
+ fireEvent.mouseEnter(bugAction);
4239
+ expect(hasRing(bugAction)).toBe(false);
4240
+ fireEvent.keyDown(trigger, {key: 'ArrowRight', ctrlKey: true});
4241
+ expect(hasRing(bugAction)).toBe(false);
4242
+ });
4243
+ });
4244
+
4086
4245
  it('the inline-end arrow reaches the action cell, Enter fires it, inline-start returns', async () => {
4087
4246
  const user = userEvent.setup();
4088
4247
  const onChange = vi.fn();
@@ -50,6 +50,7 @@ import type {IndicatorPosition} from '../Indicator';
50
50
  import {Badge} from '../Badge';
51
51
  import {
52
52
  colorVars,
53
+ focusVars,
53
54
  sizeVars,
54
55
  spacingVars,
55
56
  radiusVars,
@@ -74,7 +75,10 @@ import {
74
75
  import {useMultiCombobox} from './hooks';
75
76
  import {getInputARIA, isImeKeyEvent, mergeProps} from '../utils';
76
77
  import {devWarn, warnOnce} from '../utils/devWarning';
77
- import {FOCUS_OUTLINE_PARTS} from '../utils/focusOutline.stylex';
78
+ import {
79
+ getInteractionModality,
80
+ useInteractionModalityTracking,
81
+ } from '../utils/interactionModality';
78
82
  import {useAnnounce} from '../hooks/useAnnounce';
79
83
  import {FOCUSABLE_SELECTOR} from '../hooks/focusableSelector';
80
84
  import {useAnnounceRenderedText} from '../hooks/useAnnounceRenderedText';
@@ -436,11 +440,15 @@ const styles = stylex.create({
436
440
  flexShrink: 0,
437
441
  borderRadius: radiusVars['--radius-element'],
438
442
  },
443
+ // The standard ring, from the focus tokens themselves: inside
444
+ // `stylex.create` StyleX resolves an imported `defineVars` group, and a
445
+ // plain object such as `FOCUS_OUTLINE_PARTS` compiles to variables nothing
446
+ // defines.
439
447
  actionCellActive: {
440
- outlineWidth: FOCUS_OUTLINE_PARTS.outlineWidth,
441
- outlineStyle: FOCUS_OUTLINE_PARTS.outlineStyle,
442
- outlineColor: FOCUS_OUTLINE_PARTS.outlineColor,
443
- outlineOffset: FOCUS_OUTLINE_PARTS.outlineOffset,
448
+ outlineWidth: focusVars['--focus-outline-width'],
449
+ outlineStyle: focusVars['--focus-outline-style'],
450
+ outlineColor: focusVars['--focus-outline-color'],
451
+ outlineOffset: focusVars['--focus-outline-offset'],
444
452
  },
445
453
 
446
454
  // Empty state
@@ -1752,8 +1760,8 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1752
1760
  getActionCellId,
1753
1761
  activeDescendantId,
1754
1762
  onTriggerClick,
1755
- onKeyDown,
1756
- onItemMouseEnter,
1763
+ onKeyDown: onComboboxKeyDown,
1764
+ onItemMouseEnter: onComboboxItemMouseEnter,
1757
1765
  } = useMultiCombobox({
1758
1766
  selectableItems: sortedItems,
1759
1767
  isDisabled: isDisabled || isEffectivelyReadOnly,
@@ -1784,6 +1792,29 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1784
1792
  ),
1785
1793
  });
1786
1794
 
1795
+ // A hover moves the highlight too, but the focus ring follows the shared
1796
+ // interaction modality (architecture:interaction-modality INV1): it is read
1797
+ // whenever a key or the pointer moves the highlight, so an action cell
1798
+ // highlighted after pointer input keeps the hover treatment and no ring.
1799
+ useInteractionModalityTracking();
1800
+ const [isKeyboardHighlight, setIsKeyboardHighlight] = useState(
1801
+ () => getInteractionModality() === 'keyboard',
1802
+ );
1803
+ const onKeyDown = useCallback(
1804
+ (event: React.KeyboardEvent<HTMLElement>) => {
1805
+ onComboboxKeyDown(event);
1806
+ setIsKeyboardHighlight(getInteractionModality() === 'keyboard');
1807
+ },
1808
+ [onComboboxKeyDown],
1809
+ );
1810
+ const onItemMouseEnter = useCallback(
1811
+ (...args: Parameters<typeof onComboboxItemMouseEnter>) => {
1812
+ onComboboxItemMouseEnter(...args);
1813
+ setIsKeyboardHighlight(getInteractionModality() === 'keyboard');
1814
+ },
1815
+ [onComboboxItemMouseEnter],
1816
+ );
1817
+
1787
1818
  // The trigger button owns its click so a consumer `onClick`, which lands on
1788
1819
  // the button, composes after the built-in toggle. A press elsewhere in the
1789
1820
  // control box (padding, start icon, chevron) still opens the panel.
@@ -2117,6 +2148,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
2117
2148
  styles.actionCell,
2118
2149
  isHighlighted &&
2119
2150
  highlightedCell === 'action' &&
2151
+ isKeyboardHighlight &&
2120
2152
  styles.actionCellActive,
2121
2153
  )}>
2122
2154
  {isSelectAll || isCreate ? null : item.action}
@@ -2131,6 +2163,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
2131
2163
  [
2132
2164
  isGrid,
2133
2165
  highlightedCell,
2166
+ isKeyboardHighlight,
2134
2167
  getActionCellId,
2135
2168
  renderOption,
2136
2169
  indicatorPosition,
@@ -6,8 +6,10 @@
6
6
  * built Storybook.
7
7
  * @output Real-engine proof for option actions: the action is visible at rest
8
8
  * beside its row (`spec:AST-058` FR8), the keyboard reaches it through the
9
- * inline-end arrow in both directions and fires it with Enter, and the
10
- * highlight follows a real mouse across rows and actions.
9
+ * inline-end arrow in both directions and fires it with Enter, the active
10
+ * action cell draws the standard focus ring when the shared input modality
11
+ * is keyboard and not after pointer input, and the highlight follows a real
12
+ * mouse across rows and actions.
11
13
  * @position Run with `pnpm test:a11y-contract`; paint, pointer paths and the
12
14
  * active descendant as an engine exposes them are not jsdom claims.
13
15
  */
@@ -143,6 +145,114 @@ test('the keyboard reaches an action through the inline-end arrow and fires it w
143
145
  await expect.poll(async () => activeDescendant(page)).toBe('row:Bug');
144
146
  });
145
147
 
148
+ test('the active action cell draws the standard focus ring', async ({page}) => {
149
+ await page.goto(
150
+ `${storybook.origin}/iframe.html?id=core-multiselector--row-actions&viewMode=story`,
151
+ );
152
+ const search = page.getByRole('combobox');
153
+ await expect(search).toBeFocused();
154
+ await search.press('ArrowDown'); // select-all
155
+ await search.press('ArrowDown'); // Feature (no action)
156
+ await search.press('ArrowDown'); // Bug
157
+ await search.press('ArrowRight');
158
+ await expect
159
+ .poll(async () => activeDescendant(page))
160
+ .toBe('gridcell:Edit Bug');
161
+ const ring = await page.evaluate(() => {
162
+ const id = document.activeElement?.getAttribute('aria-activedescendant');
163
+ const cell = id ? document.getElementById(id) : null;
164
+ if (!cell) {
165
+ return null;
166
+ }
167
+ const cs = getComputedStyle(cell);
168
+ // The ring the focus tokens describe, resolved by the browser on a probe.
169
+ const probe = document.createElement('div');
170
+ probe.style.cssText =
171
+ 'outline-width:var(--focus-outline-width);outline-style:var(--focus-outline-style);outline-color:var(--focus-outline-color);outline-offset:var(--focus-outline-offset)';
172
+ document.body.appendChild(probe);
173
+ const want = getComputedStyle(probe);
174
+ const result = {
175
+ style: cs.outlineStyle,
176
+ width: cs.outlineWidth,
177
+ color: cs.outlineColor,
178
+ offset: cs.outlineOffset,
179
+ wantStyle: want.outlineStyle,
180
+ wantWidth: want.outlineWidth,
181
+ wantColor: want.outlineColor,
182
+ wantOffset: want.outlineOffset,
183
+ };
184
+ probe.remove();
185
+ return result;
186
+ });
187
+ expect(ring).not.toBeNull();
188
+ expect(ring!.style).not.toBe('none');
189
+ expect(ring!.width).not.toBe('0px');
190
+ expect(ring!.style).toBe(ring!.wantStyle);
191
+ expect(ring!.width).toBe(ring!.wantWidth);
192
+ expect(ring!.color).toBe(ring!.wantColor);
193
+ expect(ring!.offset).toBe(ring!.wantOffset);
194
+ // Back on the option cell, the action cell drops the ring.
195
+ await search.press('ArrowLeft');
196
+ await expect.poll(async () => activeDescendant(page)).toBe('row:Bug');
197
+ const after = await page
198
+ .getByRole('row', {name: 'Bug'})
199
+ .locator('[data-multi-selector-action-cell]')
200
+ .evaluate(cell => getComputedStyle(cell).outlineStyle);
201
+ expect(after).toBe('none');
202
+ });
203
+
204
+ test('the action-cell ring follows the shared input modality', async ({
205
+ page,
206
+ }) => {
207
+ await page.goto(
208
+ `${storybook.origin}/iframe.html?id=core-multiselector--row-actions&viewMode=story`,
209
+ );
210
+ const search = page.getByRole('combobox');
211
+ await expect(search).toBeFocused();
212
+ const actionCell = (name: string) =>
213
+ page.getByRole('row', {name}).locator('[data-multi-selector-action-cell]');
214
+ const outlineStyle = async (name: string) =>
215
+ actionCell(name).evaluate(cell => getComputedStyle(cell).outlineStyle);
216
+ const hover = async (name: string) => {
217
+ const cell = await box(actionCell(name));
218
+ await page.mouse.move(cell.x + cell.width / 2, cell.y + cell.height / 2, {
219
+ steps: 6,
220
+ });
221
+ };
222
+
223
+ // Pointer input: a click into the search field, then a hover onto an
224
+ // action, moves the highlight without the ring, and leaves none behind.
225
+ await search.click();
226
+ await hover('Bug');
227
+ await expect
228
+ .poll(async () => activeDescendant(page))
229
+ .toBe('gridcell:Edit Bug');
230
+ expect(await outlineStyle('Bug')).toBe('none');
231
+ // A modifier chord is not keyboard navigation for the shared tracker.
232
+ await search.press('Control+ArrowRight');
233
+ expect(await outlineStyle('Bug')).toBe('none');
234
+ await page.mouse.move(1, 1, {steps: 6});
235
+ expect(await outlineStyle('Bug')).toBe('none');
236
+
237
+ // Keyboard input: back onto the same cell, the ring is drawn.
238
+ await search.press('ArrowLeft');
239
+ await expect.poll(async () => activeDescendant(page)).toBe('row:Bug');
240
+ await search.press('ArrowRight');
241
+ await expect
242
+ .poll(async () => activeDescendant(page))
243
+ .toBe('gridcell:Edit Bug');
244
+ expect(await outlineStyle('Bug')).not.toBe('none');
245
+
246
+ // Still keyboard-driven, a hover that moves the highlight to another
247
+ // row's action carries the ring with it.
248
+ await hover('P0');
249
+ await expect
250
+ .poll(async () => activeDescendant(page))
251
+ .toBe('gridcell:Edit P0');
252
+ expect(await outlineStyle('P0')).not.toBe('none');
253
+ expect(await outlineStyle('Bug')).toBe('none');
254
+ });
255
+
146
256
  test('under RTL the inline-end arrow is ArrowLeft', async ({page}) => {
147
257
  await page.goto(
148
258
  `${storybook.origin}/iframe.html?id=core-multiselector--row-actions-rtl&viewMode=story`,
@@ -280,10 +280,10 @@ describe('Popover', () => {
280
280
  'utf8',
281
281
  );
282
282
  expect(popoverSource).toMatch(
283
- /surfaceViewportFit:[\s\S]*?maxInlineSize: stylex\.firstThatWorks\(\s*layerViewportInset\.maxInlineSize/,
283
+ /surfaceViewportFit:[\s\S]*?maxInlineSize: stylex\.firstThatWorks\(\s*`calc\(\$\{layerViewportInset\.maxInlineSize\}\)`/,
284
284
  );
285
285
  expect(popoverSource).toMatch(
286
- /surfaceViewportFit:[\s\S]*?maxBlockSize: stylex\.firstThatWorks\(\s*layerViewportInset\.maxBlockSize/,
286
+ /surfaceViewportFit:[\s\S]*?maxBlockSize: stylex\.firstThatWorks\(\s*`calc\(\$\{layerViewportInset\.maxBlockSize\}\)`/,
287
287
  );
288
288
  expect(popoverSource).not.toMatch(/safe-area-inset|calc\(100%/);
289
289
  expect(surface?.className).not.toContain(
@@ -272,12 +272,12 @@ const styles = stylex.create({
272
272
  surfaceViewportFit: {
273
273
  boxSizing: 'border-box',
274
274
  maxInlineSize: stylex.firstThatWorks(
275
- layerViewportInset.maxInlineSize,
276
- layerViewportInset.maxInlineSizeFallback,
275
+ `calc(${layerViewportInset.maxInlineSize})`,
276
+ `calc(${layerViewportInset.maxInlineSizeFallback})`,
277
277
  ),
278
278
  maxBlockSize: stylex.firstThatWorks(
279
- layerViewportInset.maxBlockSize,
280
- layerViewportInset.maxBlockSizeFallback,
279
+ `calc(${layerViewportInset.maxBlockSize})`,
280
+ `calc(${layerViewportInset.maxBlockSizeFallback})`,
281
281
  ),
282
282
  },
283
283
  surfaceScrollable: {
@@ -70,6 +70,10 @@ const focusOutlineLonghands = {
70
70
  * independently overridable — and every part stays a var, so a theme's
71
71
  * `--focus-outline-*` overrides still flow through.
72
72
  *
73
+ * NOT usable inside another file's `stylex.create`, for the same reason as
74
+ * {@link FOCUS_OUTLINE}: StyleX compiles each member there to a CSS variable
75
+ * nothing defines. Compose the `focusVars` tokens instead.
76
+ *
73
77
  * Keys are camelCase to match `HTMLElement.style`, so this spreads straight
74
78
  * onto an element:
75
79
  *