@astryxdesign/core 0.5.0-canary.f4d494c → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/Avatar/Avatar.d.ts +1 -25
  2. package/dist/Avatar/Avatar.d.ts.map +1 -1
  3. package/dist/Avatar/Avatar.js +9 -43
  4. package/dist/Avatar/index.d.ts +2 -2
  5. package/dist/Avatar/index.d.ts.map +1 -1
  6. package/dist/Avatar/index.js +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +2 -9
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +2 -5
  10. package/dist/AvatarGroup/AvatarGroupContext.d.ts +1 -2
  11. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  13. package/dist/AvatarGroup/AvatarGroupOverflow.js +6 -10
  14. package/dist/DateInput/DateInput.d.ts +0 -43
  15. package/dist/DateInput/DateInput.d.ts.map +1 -1
  16. package/dist/DateInput/DateInput.js +0 -19
  17. package/dist/DateInput/index.d.ts +1 -1
  18. package/dist/DateInput/index.d.ts.map +1 -1
  19. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  20. package/dist/HoverCard/HoverCard.js +3 -52
  21. package/dist/HoverCard/useHoverCard.d.ts +2 -11
  22. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  23. package/dist/HoverCard/useHoverCard.js +0 -2
  24. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  25. package/dist/Indicator/CheckboxIndicator.js +2 -6
  26. package/dist/Kbd/Kbd.d.ts.map +1 -1
  27. package/dist/Kbd/Kbd.js +26 -2
  28. package/dist/astryx.css +0 -21
  29. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  30. package/dist/hooks/useHotkeys.js +25 -3
  31. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  32. package/dist/theme/derivedVarRegistry.js +0 -4
  33. package/dist/theme/syntax/tokens.d.ts +1 -1
  34. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  35. package/dist/theme/syntax/tokens.js +2 -5
  36. package/package.json +3 -3
  37. package/src/Avatar/Avatar.doc.mjs +2 -12
  38. package/src/Avatar/Avatar.test.tsx +0 -80
  39. package/src/Avatar/Avatar.tsx +14 -64
  40. package/src/Avatar/index.ts +2 -2
  41. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -8
  42. package/src/AvatarGroup/AvatarGroup.test.tsx +0 -24
  43. package/src/AvatarGroup/AvatarGroup.tsx +4 -12
  44. package/src/AvatarGroup/AvatarGroupContext.ts +3 -5
  45. package/src/AvatarGroup/AvatarGroupOverflow.tsx +4 -10
  46. package/src/DateInput/DateInput.doc.mjs +0 -16
  47. package/src/DateInput/DateInput.tsx +0 -52
  48. package/src/DateInput/DateInputTouch.test.tsx +9 -32
  49. package/src/DateInput/index.ts +0 -1
  50. package/src/HoverCard/HoverCard.test.tsx +4 -84
  51. package/src/HoverCard/HoverCard.tsx +3 -65
  52. package/src/HoverCard/useHoverCard.tsx +2 -15
  53. package/src/Indicator/CheckboxIndicator.tsx +8 -14
  54. package/src/Indicator/Indicator.doc.mjs +0 -2
  55. package/src/Kbd/Kbd.test.tsx +17 -0
  56. package/src/Kbd/Kbd.tsx +26 -2
  57. package/src/hooks/useHotkeys.test.ts +18 -0
  58. package/src/hooks/useHotkeys.ts +25 -3
  59. package/src/theme/derivedVarRegistry.test.ts +0 -1
  60. package/src/theme/derivedVarRegistry.ts +0 -1
  61. package/src/theme/syntax/tokens.ts +2 -5
  62. package/dist/DateInput/NativeDateField.d.ts +0 -10
  63. package/dist/DateInput/NativeDateField.d.ts.map +0 -1
  64. package/dist/DateInput/NativeDateField.js +0 -477
  65. package/dist/utils/isApplePlatform.d.ts +0 -18
  66. package/dist/utils/isApplePlatform.d.ts.map +0 -1
  67. package/dist/utils/isApplePlatform.js +0 -37
  68. package/src/DateInput/NativeDateField.test.tsx +0 -565
  69. package/src/DateInput/NativeDateField.tsx +0 -623
  70. package/src/utils/isApplePlatform.test.ts +0 -61
  71. package/src/utils/isApplePlatform.ts +0 -37
@@ -155,9 +155,7 @@ export function CheckboxIndicator({
155
155
  children: isRenderable(children) ? children : /*#__PURE__*/_jsxs(_Fragment, {
156
156
  children: [/*#__PURE__*/_jsx("svg", {
157
157
  viewBox: "0 0 10 10",
158
- ...mergeProps(themeProps('checkbox-indicator-check', {
159
- size
160
- }), stylex.props(styles.checkmark, checkmarkSizeStyles[size], isChecked && styles.checkmarkVisible)),
158
+ ...stylex.props(styles.checkmark, checkmarkSizeStyles[size], isChecked && styles.checkmarkVisible),
161
159
  children: /*#__PURE__*/_jsx("path", {
162
160
  d: "M8.5 2.5L4 7.5L1.5 5",
163
161
  stroke: "currentColor",
@@ -167,9 +165,7 @@ export function CheckboxIndicator({
167
165
  strokeLinejoin: "round"
168
166
  })
169
167
  }), /*#__PURE__*/_jsx("span", {
170
- ...mergeProps(themeProps('checkbox-indicator-dash', {
171
- size
172
- }), stylex.props(styles.indeterminateMark, indeterminateSizeStyles[size], isIndeterminate && styles.indeterminateMarkVisible))
168
+ ...stylex.props(styles.indeterminateMark, indeterminateSizeStyles[size], isIndeterminate && styles.indeterminateMarkVisible)
173
169
  })]
174
170
  })
175
171
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Kbd.d.ts","sourceRoot":"","sources":["../../src/Kbd/Kbd.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAA6B,MAAM,OAAO,CAAC;AAIlD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA0G5C,MAAM,WAAW,QAAS,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC1D,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,GAAG,CAAC,EAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAC,EAAE,QAAQ,qBAgC3E;yBAhCe,GAAG"}
1
+ {"version":3,"file":"Kbd.d.ts","sourceRoot":"","sources":["../../src/Kbd/Kbd.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAA6B,MAAM,OAAO,CAAC;AAGlD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAmI5C,MAAM,WAAW,QAAS,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC1D,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,GAAG,CAAC,EAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAC,EAAE,QAAQ,qBAgC3E;yBAhCe,GAAG"}
package/dist/Kbd/Kbd.js CHANGED
@@ -15,7 +15,6 @@
15
15
  import React, { useSyncExternalStore } from 'react';
16
16
  import * as stylex from '@stylexjs/stylex';
17
17
  import { mergeProps } from "../utils/index.js";
18
- import { isApplePlatform } from "../utils/isApplePlatform.js";
19
18
  import { themeProps } from "../utils/themeProps.js";
20
19
  import "../theme/tokens.stylex.js";
21
20
  import { colorVars, spacingVars, radiusVars, typographyVars, fontWeightVars, typeScaleVars } from "../theme/tokens.stylex.js";
@@ -99,6 +98,31 @@ function subscribeToPlatformChanges() {
99
98
  function getServerPlatformSnapshot() {
100
99
  return false;
101
100
  }
101
+
102
+ /**
103
+ * Detects whether the current platform is macOS/iOS.
104
+ * Prefers the User-Agent Client Hints API when it names a platform (modern
105
+ * Chrome/Edge), falls back to navigator.platform (deprecated but universally
106
+ * supported) when it is absent or blank.
107
+ */
108
+ function detectMac() {
109
+ if (typeof navigator === 'undefined') {
110
+ return false;
111
+ }
112
+ // Prefer User-Agent Client Hints API (not deprecated)
113
+ const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
114
+ if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
115
+ const uaPlatform = uaData.platform;
116
+ // A blank platform is no answer, not a negative one. Builds that rewrite
117
+ // their client-hints identity ship '', so fall through rather than
118
+ // reading it as "not Apple".
119
+ if (typeof uaPlatform === 'string' && uaPlatform.trim() !== '') {
120
+ return /mac/i.test(uaPlatform);
121
+ }
122
+ }
123
+ // Fallback: navigator.platform (deprecated but still shipped everywhere)
124
+ return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
125
+ }
102
126
  /**
103
127
  * Displays a keyboard shortcut as styled <kbd> elements.
104
128
  *
@@ -122,7 +146,7 @@ export function Kbd({
122
146
  style,
123
147
  ...rest
124
148
  }) {
125
- const isMac = useSyncExternalStore(subscribeToPlatformChanges, isApplePlatform, getServerPlatformSnapshot);
149
+ const isMac = useSyncExternalStore(subscribeToPlatformChanges, detectMac, getServerPlatformSnapshot);
126
150
  const parts = keys.split('+').map(key => key.trim().toLowerCase());
127
151
 
128
152
  // Screen-reader name: the joined spoken labels (e.g. "Command + K"), since
package/dist/astryx.css CHANGED
@@ -79,9 +79,6 @@
79
79
  :root, .x1kvdh9l{--spacing-0:0px;--spacing-0-5:2px;--spacing-1:4px;--spacing-1-5:6px;--spacing-2:8px;--spacing-3:12px;--spacing-4:16px;--spacing-5:20px;--spacing-6:24px;--spacing-7:28px;--spacing-8:32px;--spacing-9:36px;--spacing-10:40px;--spacing-11:44px;--spacing-12:48px;}
80
80
  :root, .x1etlgq0{--text-heading-1-size:var(--font-size-2xl);--text-heading-1-weight:var(--font-weight-semibold);--text-heading-1-leading:1.3333;--text-heading-2-size:var(--font-size-xl);--text-heading-2-weight:var(--font-weight-semibold);--text-heading-2-leading:1.4;--text-heading-3-size:var(--font-size-lg);--text-heading-3-weight:var(--font-weight-semibold);--text-heading-3-leading:1.4118;--text-heading-4-size:var(--font-size-base);--text-heading-4-weight:var(--font-weight-semibold);--text-heading-4-leading:1.4286;--text-heading-5-size:var(--font-size-sm);--text-heading-5-weight:var(--font-weight-semibold);--text-heading-5-leading:1.6667;--text-heading-6-size:var(--font-size-xs);--text-heading-6-weight:var(--font-weight-semibold);--text-heading-6-leading:1.6;--text-body-size:var(--font-size-base);--text-body-weight:var(--font-weight-normal);--text-body-leading:1.4286;--text-large-size:var(--font-size-lg);--text-large-weight:var(--font-weight-semibold);--text-large-leading:1.4118;--text-label-size:var(--font-size-base);--text-label-weight:var(--font-weight-medium);--text-label-leading:1.4286;--text-code-size:var(--font-size-base);--text-code-weight:var(--font-weight-normal);--text-code-leading:1.4286;--text-supporting-size:var(--font-size-sm);--text-supporting-weight:var(--font-weight-normal);--text-supporting-leading:1.6667;--text-display-1-size:var(--font-size-5xl);--text-display-1-weight:var(--font-weight-normal);--text-display-1-leading:1.2381;--text-display-2-size:var(--font-size-4xl);--text-display-2-weight:var(--font-weight-normal);--text-display-2-leading:1.2571;--text-display-3-size:var(--font-size-3xl);--text-display-3-weight:var(--font-weight-normal);--text-display-3-leading:1.2414;}
81
81
  .xlz5hwt{--_avatar-group-overlap:var(--x---_avatar-group-overlap)}
82
- .xdarwi0{--_avatar-radius:var(--radius-element)}
83
- .xeincmr{--_avatar-radius:var(--radius-full)}
84
- .x877142{--_avatar-radius:var(--radius-none)}
85
82
  .xh8ntw8{--_button-radius:calc(var(--radius-element) - var(--spacing-1))}
86
83
  .xcw7vlg{--_button-radius:max(var(--radius-element),calc(var(--_chat-composer-radius) - var(--_chat-composer-padding)))}
87
84
  .x1rjw8qi{--_button-radius:var(--radius-full)}
@@ -404,7 +401,6 @@
404
401
  .x1pjcqnp:not(#\#):not(#\#){border-radius:inherit}
405
402
  .x1ws5lxm:not(#\#):not(#\#){border-radius:max(0px,calc(var(--_dropdown-menu-radius,var(--spacing-2)) - var(--_dropdown-menu-padding,var(--spacing-1))))}
406
403
  .xc910v0:not(#\#):not(#\#){border-radius:max(0px,calc(var(--_segmented-control-radius) - var(--_segmented-control-padding)))}
407
- .xbc78cj:not(#\#):not(#\#){border-radius:var(--_avatar-radius)}
408
404
  .x1vf2byn:not(#\#):not(#\#){border-radius:var(--_banner-radius,var(--radius-container))}
409
405
  .x1jxw6zd:not(#\#):not(#\#){border-radius:var(--_button-radius,var(--radius-element))}
410
406
  .xy0xnkn:not(#\#):not(#\#){border-radius:var(--_button-radius,var(--radius-full))}
@@ -438,7 +434,6 @@
438
434
  .x98rzlu:not(#\#):not(#\#){flex:1}
439
435
  .xxhr3t:not(#\#):not(#\#){gap:0}
440
436
  .x1jnr06f:not(#\#):not(#\#){gap:4px}
441
- .x167g77z:not(#\#):not(#\#){gap:8px}
442
437
  .x14kbac9:not(#\#):not(#\#){gap:inherit}
443
438
  .x1lsbc85:not(#\#):not(#\#){gap:var(--spacing-0-5)}
444
439
  .xsn7fz1:not(#\#):not(#\#){gap:var(--spacing-0)}
@@ -563,7 +558,6 @@
563
558
  @media (hover: hover){.x61roa7.x61roa7.x61roa7:where(.x1odsvnm:hover *):not(#\#):not(#\#){border-color:var(--color-border)}}
564
559
  @media (hover: hover){.x1xwh4np.x1xwh4np.x1xwh4np:hover:where(:not(:disabled,[aria-disabled="true"])):not(#\#):not(#\#){border-color:var(--color-border-emphasized)}}
565
560
  @media (hover: hover){.x13nosk6.x13nosk6.x13nosk6:hover:where(:not(:disabled,[aria-disabled="true"])):not(#\#):not(#\#){text-decoration:underline}}
566
- .x1lugfcp:not(#\#):not(#\#):not(#\#){-webkit-appearance:none}
567
561
  .x67t5t6:not(#\#):not(#\#):not(#\#){-webkit-backdrop-filter:blur(12px)}
568
562
  .x1ua5tub:not(#\#):not(#\#):not(#\#){-webkit-box-orient:vertical}
569
563
  .x1yhjpo9:not(#\#):not(#\#):not(#\#){-webkit-line-clamp:var(--x-WebkitLineClamp)}
@@ -573,7 +567,6 @@
573
567
  .xt2iwb0:not(#\#):not(#\#):not(#\#){-webkit-mask-image:linear-gradient(transparent,transparent)}
574
568
  .x5lxg6s:not(#\#):not(#\#):not(#\#){-webkit-overflow-scrolling:touch}
575
569
  .x1i10hfl:not(#\#):not(#\#):not(#\#){-webkit-tap-highlight-color:transparent}
576
- .xg7jpbn:not(#\#):not(#\#):not(#\#){-webkit-text-fill-color:transparent}
577
570
  .xpdipgo:not(#\#):not(#\#):not(#\#){-webkit-touch-callout:none}
578
571
  .x8gbvx8:not(#\#):not(#\#):not(#\#){align-content:flex-start}
579
572
  .x1pha0wt:not(#\#):not(#\#):not(#\#){align-items:baseline}
@@ -1153,7 +1146,6 @@
1153
1146
  .x3oybdh:not(#\#):not(#\#):not(#\#){transform:scale(1)}
1154
1147
  .x11lhmoz:not(#\#):not(#\#):not(#\#){transform:translate(-50%,-50%)}
1155
1148
  .x1m9mm8y:not(#\#):not(#\#):not(#\#){transform:translate(-50%,50%)}
1156
- .xv8yid9:not(#\#):not(#\#):not(#\#){transform:translate(25%,25%)}
1157
1149
  .x4v1no5:not(#\#):not(#\#):not(#\#){transform:translate(50%,50%)}
1158
1150
  .x5i6ehr:not(#\#):not(#\#):not(#\#){transform:translateX(-100%)}
1159
1151
  .xuuh30:not(#\#):not(#\#):not(#\#){transform:translateX(-50%)}
@@ -1303,7 +1295,6 @@
1303
1295
  .xaafvuh:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform:scaleX(-1) rotate(0deg)}
1304
1296
  .x1nxqau:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform:scaleX(-1) rotate(90deg)}
1305
1297
  .xgtlewx:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform:scaleX(-1)}
1306
- .x10fxb7p:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform:translate(-25%,25%)}
1307
1298
  .xqgnvz5:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform:translate(-50%,50%)}
1308
1299
  .xoffwj3:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform:translate(50%,-50%)}
1309
1300
  .x1df3fe5:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform:translateX(-100%)}
@@ -1472,7 +1463,6 @@
1472
1463
  .xs02d24:not(#\#):not(#\#):not(#\#):not(#\#){height:calc(100% + 1px)}
1473
1464
  .x12qplqi:not(#\#):not(#\#):not(#\#):not(#\#){height:calc(100% + var(--container-padding-block-start,0px) + var(--container-padding-block-end,0px))}
1474
1465
  .xpa73km:not(#\#):not(#\#):not(#\#):not(#\#){height:calc(100dvh - var(--_app-shell-header-height,0px))}
1475
- .x1oglpa6:not(#\#):not(#\#):not(#\#):not(#\#){height:calc(max(1rem,var(--text-body-size)) * var(--text-body-leading));height:1lh}
1476
1466
  .x1tpcejd:not(#\#):not(#\#):not(#\#):not(#\#){height:calc(var(--_sheet-budget) + 48px)}
1477
1467
  .x1uiybsj:not(#\#):not(#\#):not(#\#):not(#\#){height:calc(var(--size-element-lg) - 4px)}
1478
1468
  .x102lyln:not(#\#):not(#\#):not(#\#):not(#\#){height:calc(var(--size-element-lg) - 8px)}
@@ -1776,13 +1766,7 @@
1776
1766
  .xxyica1:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{border-radius:inherit}
1777
1767
  .x17gn2l9:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{grid-column:1}
1778
1768
  .xd54j2p:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{inset-block:0}
1779
- .xpsyfx0:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-date-and-time-value{margin-block:0}
1780
- .x1qqcexc:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-date-and-time-value{margin-inline:0}
1781
- .x1x4c3m6:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-date-and-time-value{padding-block:0}
1782
- .xtbxizx:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-datetime-edit{padding-block:0}
1783
1769
  @starting-style{.x5rzir7.x5rzir7:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#){padding-bottom:0}}
1784
- .xkqr7wz:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-date-and-time-value{padding-inline:0}
1785
- .xslb4at:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-datetime-edit{padding-inline:0}
1786
1770
  .xvtkv6q:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{transition:background-color .15s ease,width .15s ease}
1787
1771
  .x17s1k9h:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{transition:opacity .15s ease}
1788
1772
  .x19ds33d:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{align-self:start}
@@ -1806,15 +1790,12 @@
1806
1790
  .xc2ndz5:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{content:counter(astryx-list) "."}
1807
1791
  .xh3pasq:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{content:var(--_input-clear-hit-content)}
1808
1792
  .xhkezso:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{display:block}
1809
- .xolhmmf:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-calendar-picker-indicator{display:none}
1810
1793
  .x3nzp0f:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{font-family:var(--font-family-code)}
1811
1794
  .xekda60:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{inset-inline-end:-2px}
1812
1795
  .x1unh1gc:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{inset-inline-end:0}
1813
1796
  .xvw6utc:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{inset-inline-start:-2px}
1814
1797
  .x1iygr5g:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{inset-inline-start:0}
1815
1798
  .x1km86hw:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{inset-inline-start:calc(var(--spacing-4) + var(--_codeblock-gutter-width) + var(--spacing-3))}
1816
- .x15bqym3:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-datetime-edit{line-height:inherit}
1817
- .x1f74mqm:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-date-and-time-value{line-height:inherit}
1818
1799
  .xph5o2a:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::backdrop{opacity:0}
1819
1800
  .xb3n6bw:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::backdrop{opacity:1}
1820
1801
  .xni466t:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::backdrop{opacity:var(--_sheet-scrim-opacity,1)}
@@ -1825,7 +1806,6 @@
1825
1806
  .x1hmns74:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{position:absolute}
1826
1807
  .x1j6awrg:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{position:absolute}
1827
1808
  .x1wnbjh8:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{text-align:end}
1828
- .x9rmy9g:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-date-and-time-value{text-align:start}
1829
1809
  .x1fgylwi:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{transform-origin:center top}
1830
1810
  .xgu12d:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{transform-origin:left center}
1831
1811
  .x1e8txe:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{transform:scaleX(0)}
@@ -1865,7 +1845,6 @@
1865
1845
  .xe80sof:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{bottom:-2px}
1866
1846
  .x1xrz1ek:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{bottom:0}
1867
1847
  .xu6fald:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{height:var(--_sheet-keyboard-inset,0px)}
1868
- .xec4aax:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::-webkit-date-and-time-value{min-height:0}
1869
1848
  .x51xajf:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{top:-2px}
1870
1849
  .x1m1drc7:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{top:0}
1871
1850
  .xkj6t0a:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{width:0}
@@ -1 +1 @@
1
- {"version":3,"file":"useHotkeys.d.ts","sourceRoot":"","sources":["../../src/hooks/useHotkeys.ts"],"names":[],"mappings":"AA+BA;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB;;;;;;;;;;;;;;;OAeG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA0ED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAkClD"}
1
+ {"version":3,"file":"useHotkeys.d.ts","sourceRoot":"","sources":["../../src/hooks/useHotkeys.ts"],"names":[],"mappings":"AA6BA;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB;;;;;;;;;;;;;;;OAeG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAkGD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAkClD"}
@@ -18,15 +18,13 @@
18
18
  * unless a hotkey opts in via `allowInInputs`.
19
19
  *
20
20
  * Platform-aware: `mod` maps to metaKey (⌘) on Apple platforms and
21
- * ctrlKey elsewhere, through the same isApplePlatform util Kbd reads, so
22
- * displayed and handled shortcuts cannot disagree.
21
+ * ctrlKey elsewhere — mirrors the detection used by Kbd.
23
22
  *
24
23
  * SYNC: When modified, update:
25
24
  * - /packages/core/src/hooks/index.ts
26
25
  * - /packages/core/src/hooks/useHotkeys.doc.mjs
27
26
  */
28
27
  import { useEffect, useRef } from 'react';
29
- import { isApplePlatform } from "../utils/isApplePlatform.js";
30
28
 
31
29
  /**
32
30
  * A single keyboard shortcut registration.
@@ -44,6 +42,30 @@ const KEY_ALIASES = {
44
42
  plus: '+'
45
43
  };
46
44
 
45
+ /**
46
+ * Detects whether the current platform is macOS/iOS.
47
+ * Prefers the User-Agent Client Hints API when it names a platform (modern
48
+ * Chrome/Edge), falls back to navigator.platform (deprecated but universally
49
+ * supported) when it is absent or blank.
50
+ * Mirrors the detection used by Kbd so displayed and handled shortcuts agree.
51
+ */
52
+ function isApplePlatform() {
53
+ if (typeof navigator === 'undefined') {
54
+ return false;
55
+ }
56
+ const uaData = 'userAgentData' in navigator ? navigator.userAgentData : null;
57
+ if (uaData && typeof uaData === 'object' && 'platform' in uaData) {
58
+ const uaPlatform = uaData.platform;
59
+ // A blank platform is no answer, not a negative one. Builds that rewrite
60
+ // their client-hints identity ship '', so fall through rather than
61
+ // reading it as "not Apple".
62
+ if (typeof uaPlatform === 'string' && uaPlatform.trim() !== '') {
63
+ return /mac/i.test(uaPlatform);
64
+ }
65
+ }
66
+ return /Mac|iPhone|iPad|iPod/.test(navigator.platform ?? '');
67
+ }
68
+
47
69
  /**
48
70
  * Whether the event target is a typing surface where global shortcuts
49
71
  * should stay silent by default.
@@ -1 +1 @@
1
- {"version":3,"file":"derivedVarRegistry.d.ts","sourceRoot":"","sources":["../../src/theme/derivedVarRegistry.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CA+ChE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,eAAe,EAAE,CAMnB"}
1
+ {"version":3,"file":"derivedVarRegistry.d.ts","sourceRoot":"","sources":["../../src/theme/derivedVarRegistry.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CA8ChE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,eAAe,EAAE,CAMnB"}
@@ -26,10 +26,6 @@
26
26
  * entries share the same property.
27
27
  */
28
28
  export const derivedVarRegistry = {
29
- avatar: [{
30
- property: 'borderRadius',
31
- vars: ['--_avatar-radius']
32
- }],
33
29
  banner: [{
34
30
  property: 'borderRadius',
35
31
  vars: ['--_banner-radius']
@@ -29,7 +29,7 @@ export declare const syntaxTokenDefaults: {
29
29
  readonly '--color-syntax-tag': "var(--color-text-red)";
30
30
  readonly '--color-syntax-attribute': "var(--color-text-teal)";
31
31
  readonly '--color-syntax-property': "var(--color-text-cyan)";
32
- readonly '--color-syntax-punctuation': "var(--color-text-secondary)";
32
+ readonly '--color-syntax-punctuation': "var(--color-text-disabled)";
33
33
  readonly '--color-syntax-background': "var(--color-background-muted)";
34
34
  };
35
35
  export type SyntaxTokenName = keyof typeof syntaxTokenDefaults;
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/theme/syntax/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;CAgCtB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,mBAAmB,CAAC"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/theme/syntax/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;CA6BtB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,mBAAmB,CAAC"}
@@ -44,11 +44,8 @@ export const syntaxTokenDefaults = {
44
44
  '--color-syntax-attribute': 'var(--color-text-teal)',
45
45
  // property -> cyan text (object properties)
46
46
  '--color-syntax-property': 'var(--color-text-cyan)',
47
- // punctuation -> secondary text (brackets, semicolons). Not disabled text:
48
- // disabled grey is calibrated to fail AA on purpose (WCAG exempts inactive
49
- // controls), and punctuation in a code sample is normal, always-active
50
- // text — see #5386.
51
- '--color-syntax-punctuation': 'var(--color-text-secondary)',
47
+ // punctuation -> disabled text (brackets, semicolons)
48
+ '--color-syntax-punctuation': 'var(--color-text-disabled)',
52
49
  // background -> muted surface
53
50
  '--color-syntax-background': 'var(--color-background-muted)'
54
51
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.5.0-canary.f4d494c",
3
+ "version": "0.5.0",
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",
@@ -661,7 +661,6 @@
661
661
  "react-dom": ">=19.0.0"
662
662
  },
663
663
  "devDependencies": {
664
- "@astryxdesign/cli": "0.5.0-canary.f4d494c",
665
664
  "@babel/cli": "^8.0.4",
666
665
  "@babel/core": "^7.29.7",
667
666
  "@babel/preset-react": "^8.0.1",
@@ -670,7 +669,8 @@
670
669
  "@testing-library/dom": "^10.0.0",
671
670
  "@testing-library/jest-dom": "^6.6.0",
672
671
  "@testing-library/react": "^16.3.2",
673
- "rimraf": "^6.0.1"
672
+ "rimraf": "^6.0.1",
673
+ "@astryxdesign/cli": "0.5.0"
674
674
  },
675
675
  "dependencies": {
676
676
  "intl-messageformat": "^11.2.9"
@@ -19,7 +19,7 @@ export const docs = {
19
19
  {guidance: true, description: 'Give every interactive avatar (href or onClick) a name or alt. It is the control\'s accessible name, and it warns in development when it is missing.'},
20
20
  {guidance: false, description: 'Rely on a status label to name an interactive avatar. "Online" says nothing about where the link goes.'},
21
21
  {guidance: false, description: 'Use Avatar for logos, product images, or anything that isn\'t a person or team. Use an image or icon instead.'},
22
- {guidance: false, description: 'Use xstyle or className to override shape. Use the shape prop instead so themes can control it globally.'},
22
+ {guidance: false, description: 'Force a square or custom shape. Avatars are always circular to stay consistent across the system.'},
23
23
  ],
24
24
  anatomy: [
25
25
  {name: 'Photo', required: false, description: 'The profile image, loaded from the src URL. Shown when available.'},
@@ -30,17 +30,13 @@ export const docs = {
30
30
  },
31
31
  theming: {
32
32
  targets: [
33
- {className: 'astryx-avatar', visualProps: ['size', 'shape']},
33
+ {className: 'astryx-avatar', visualProps: ['size']},
34
34
  {className: 'astryx-avatar-fallback', visualProps: ['size']},
35
35
  {className: 'astryx-avatar-status-dot', visualProps: ['variant']},
36
36
  {className: 'astryx-avatar-status-dot-glyph', visualProps: ['shape']},
37
37
  ],
38
38
  vars: [
39
39
  {name: '--_avatar-group-overlap', description: 'Negative inline offset applied to every avatar after the first when avatars are stacked in an AvatarGroup. Set from the group size; a more negative value tightens the stack.', default: 'set at runtime from the group avatar size (px)', private: true},
40
- {name: '--_avatar-radius', description: 'Border radius of the avatar wrapper, content, focus ring, and the AvatarGroupOverflow "+N" chip. Set per shape variant by shapeStyles.', default: 'var(--radius-full)', private: true},
41
- ],
42
- derived: [
43
- {property: 'borderRadius', vars: ['--_avatar-radius']},
44
40
  ],
45
41
  },
46
42
  description: 'Displays a user avatar with image, initials fallback, and optional status indicator.',
@@ -71,12 +67,6 @@ export const docs = {
71
67
  description: "Avatar size. Use a named size ('xsm' 20px, 'sm' 24px, 'md' 36px, 'lg' 48px, 'xl' 128px) or a numeric pixel value. Avatar shares Icon's abbreviated scale, but its tiers are larger because avatars align with media rather than glyphs. Inside an AvatarGroup the group's size wins and this prop is ignored.",
72
68
  default: "'md'",
73
69
  },
74
- {
75
- name: 'shape',
76
- type: "'circle' | 'rounded' | 'square'",
77
- description: "Shape variant of the avatar. 'circle' (default) stays fully round. 'rounded' uses the element radius token so it matches UI corner rounding and can be set globally via theme. 'square' has no radius. Status dot positioning adapts automatically: 4-o'clock on circle, bottom-right corner on rounded/square.",
78
- default: "'circle'",
79
- },
80
70
  {
81
71
  name: 'status',
82
72
  type: 'ReactNode',
@@ -6,7 +6,6 @@ import {act, render, screen, fireEvent} from '@testing-library/react';
6
6
  import userEvent from '@testing-library/user-event';
7
7
  import {Avatar} from './Avatar';
8
8
  import {AvatarStatusDot} from './AvatarStatusDot';
9
- import {AvatarGroup} from '../AvatarGroup';
10
9
 
11
10
  describe('Avatar', () => {
12
11
  it('exposes role="img" with the name as accessible name', () => {
@@ -417,85 +416,6 @@ describe('Avatar', () => {
417
416
  expect(img).toHaveAttribute('src', 'https://example.com/ada.jpg');
418
417
  });
419
418
 
420
- it('renders without error for all shape variants', () => {
421
- const shapes = ['circle', 'rounded', 'square'] as const;
422
- for (const shape of shapes) {
423
- const {unmount} = render(
424
- <Avatar
425
- name="Ana Thomas"
426
- shape={shape}
427
- data-testid={`avatar-${shape}`}
428
- />,
429
- );
430
- expect(screen.getByTestId(`avatar-${shape}`)).toBeInTheDocument();
431
- unmount();
432
- }
433
- });
434
-
435
- it('defaults to circle shape (no shape prop)', () => {
436
- render(<Avatar name="Lee" data-testid="avatar-default" />);
437
- // Should still render with role="img" using the name as the accessible label.
438
- expect(screen.getByRole('img', {name: 'Lee'})).toBeInTheDocument();
439
- });
440
-
441
- it('renders status dot for all shape variants', () => {
442
- const shapes = ['circle', 'rounded', 'square'] as const;
443
- const StatusDot = () => <span data-testid="status-dot" />;
444
- for (const shape of shapes) {
445
- const {unmount} = render(
446
- <Avatar
447
- name="Ana"
448
- shape={shape}
449
- status={<StatusDot />}
450
- data-testid={`avatar-${shape}-status`}
451
- />,
452
- );
453
- expect(screen.getByTestId('status-dot')).toBeInTheDocument();
454
- unmount();
455
- }
456
- });
457
-
458
- it('applies shapeStyles to wrapper when rendered inside AvatarGroup', () => {
459
- render(
460
- <AvatarGroup>
461
- <Avatar name="Alice" shape="rounded" data-testid="group-avatar" />
462
- </AvatarGroup>,
463
- );
464
- expect(screen.getByTestId('group-avatar')).toBeInTheDocument();
465
- });
466
-
467
- it("an AvatarGroup's shape overrides an individual Avatar's own shape prop, matching how size already works", () => {
468
- render(
469
- <AvatarGroup shape="square">
470
- <Avatar name="Alice" shape="rounded" data-testid="group-avatar" />
471
- </AvatarGroup>,
472
- );
473
- expect(screen.getByTestId('group-avatar')).toHaveAttribute(
474
- 'data-shape',
475
- 'square',
476
- );
477
- });
478
-
479
- it("an Avatar's own shape prop applies outside any AvatarGroup", () => {
480
- render(<Avatar name="Ana" shape="rounded" data-testid="solo-avatar" />);
481
- expect(screen.getByTestId('solo-avatar')).toHaveAttribute(
482
- 'data-shape',
483
- 'rounded',
484
- );
485
- });
486
-
487
- it('defaults to circle shape inside an AvatarGroup with no explicit shape', () => {
488
- render(
489
- <AvatarGroup>
490
- <Avatar name="Alice" shape="square" data-testid="group-avatar" />
491
- </AvatarGroup>,
492
- );
493
- expect(screen.getByTestId('group-avatar')).toHaveAttribute(
494
- 'data-shape',
495
- 'circle',
496
- );
497
- });
498
-
499
419
  // --- Name tooltip (tooltip?: string | boolean) ---
500
420
  describe('name tooltip', () => {
501
421
  const originalShowPopover = HTMLElement.prototype.showPopover;
@@ -97,11 +97,6 @@ type AvatarNumericSize =
97
97
  */
98
98
  export type AvatarSize = AvatarNamedSize | AvatarNumericSize;
99
99
 
100
- /**
101
- * Avatar shape options
102
- */
103
- export type AvatarShape = 'circle' | 'rounded' | 'square';
104
-
105
100
  /**
106
101
  * Resolves named sizes to their numeric pixel values
107
102
  */
@@ -123,25 +118,6 @@ export function resolveSize(size: AvatarSize): number {
123
118
  }
124
119
  }
125
120
 
126
- // Sets one local custom property rather than `borderRadius` directly, so
127
- // every clipping/ring surface that needs the shape's radius (the wrapper,
128
- // the interactive focus ring, the content div that actually clips via
129
- // `overflow: hidden`, and AvatarGroupOverflow's "+N" indicator) reads the
130
- // same value instead of each hardcoding its own borderRadius independently.
131
- // A theme override only has to reach this one property, not fight the
132
- // per-surface StyleX class order to change the shape everywhere at once.
133
- export const shapeStyles = stylex.create({
134
- circle: {
135
- '--_avatar-radius': radiusVars['--radius-full'],
136
- },
137
- rounded: {
138
- '--_avatar-radius': radiusVars['--radius-element'],
139
- },
140
- square: {
141
- '--_avatar-radius': radiusVars['--radius-none'],
142
- },
143
- });
144
-
145
121
  /**
146
122
  * Base styles for the avatar
147
123
  * Uses a wrapper/content structure so status isn't clipped by overflow:hidden
@@ -151,10 +127,10 @@ const styles = stylex.create({
151
127
  position: 'relative',
152
128
  display: 'inline-flex',
153
129
  flexShrink: 0,
154
- // Reads the shape variant's `--_avatar-radius` (set by `shapeStyles`
155
- // below) rather than a hardcoded value, so `shape` actually changes the
156
- // wrapper's own radius instead of only the content div's clip.
157
- borderRadius: 'var(--_avatar-radius)',
130
+ // The wrapper carries the avatar's box as well as its radius, so a theme
131
+ // rule on the `.astryx-avatar` target reaches both: the size the `size`
132
+ // visual prop selects on is set here, and the content below fills it.
133
+ borderRadius: radiusVars['--radius-full'],
158
134
  },
159
135
  content: {
160
136
  display: 'flex',
@@ -162,14 +138,9 @@ const styles = stylex.create({
162
138
  justifyContent: 'center',
163
139
  width: '100%',
164
140
  height: '100%',
141
+ borderRadius: radiusVars['--radius-full'],
165
142
  overflow: 'hidden',
166
143
  userSelect: 'none',
167
- // The content div is the one that actually clips (via overflow: hidden
168
- // above), so it needs its own border-radius matching the wrapper's.
169
- // It inherits `--_avatar-radius` from the wrapper, since custom
170
- // properties cascade to descendants even though border-radius itself
171
- // does not.
172
- borderRadius: 'var(--_avatar-radius)',
173
144
  },
174
145
  image: {
175
146
  width: '100%',
@@ -196,6 +167,9 @@ const styles = stylex.create({
196
167
  status: {
197
168
  position: 'absolute',
198
169
  },
170
+ // Visible focus ring for the name-tooltip tab stop, matching the repo-wide
171
+ // focus-visible outline treatment (see Timestamp, Token, Thumbnail). Only
172
+ // applied when a tooltip is active so keyboard users can reveal it.
199
173
  // Reset the intrinsic styling of the interactive element (<a>/<button>) so it
200
174
  // is a transparent, correctly-sized wrapper around the avatar visuals. The
201
175
  // element carries the focus-visible accent ring for keyboard users.
@@ -213,9 +187,8 @@ const styles = stylex.create({
213
187
  default: 'pointer',
214
188
  ':is(:disabled,[aria-disabled="true"])': 'default',
215
189
  },
216
- // Match the avatar's shape (via `--_avatar-radius`) so the focus ring
217
- // hugs it, whichever shape variant is in effect.
218
- borderRadius: 'var(--_avatar-radius)',
190
+ // Match the avatar's circular shape so the focus ring hugs it.
191
+ borderRadius: radiusVars['--radius-full'],
219
192
  },
220
193
  });
221
194
 
@@ -234,7 +207,7 @@ const dynamicStyles = stylex.create({
234
207
  fontSize: (size: number) => ({
235
208
  fontSize: `${size * INITIALS_FONT_SIZE_RATIO}px`,
236
209
  }),
237
- statusPositionCircle: (size: number) => ({
210
+ statusPosition: (size: number) => ({
238
211
  bottom: size * CIRCLE_EDGE_OFFSET_RATIO,
239
212
  insetInlineEnd: size * CIRCLE_EDGE_OFFSET_RATIO,
240
213
  // `insetInlineEnd` anchors to the right edge in LTR / left in RTL, so the
@@ -244,23 +217,13 @@ const dynamicStyles = stylex.create({
244
217
  ':is([dir="rtl"] *)': 'translate(-50%, 50%)',
245
218
  },
246
219
  }),
247
- statusPositionCorner: {
248
- bottom: 0,
249
- insetInlineEnd: 0,
250
- // Same RTL mirroring as statusPositionCircle above: insetInlineEnd
251
- // anchors to the right edge in LTR / left in RTL, so the outward push
252
- // must flip sign too.
253
- transform: {
254
- default: 'translate(25%, 25%)',
255
- ':is([dir="rtl"] *)': 'translate(-25%, 25%)',
256
- },
257
- },
258
220
  });
259
221
 
260
222
  const BORDER_WIDTH = 2;
261
223
 
262
224
  const groupStyles = stylex.create({
263
225
  ring: {
226
+ borderRadius: radiusVars['--radius-full'],
264
227
  borderWidth: BORDER_WIDTH,
265
228
  borderStyle: 'solid',
266
229
  borderColor: colorVars['--color-background-surface'],
@@ -313,14 +276,6 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
313
276
  * @default 'md'
314
277
  */
315
278
  size?: AvatarSize;
316
- /**
317
- * Shape variant of the avatar.
318
- * - 'circle': Full circle (default)
319
- * - 'rounded': Rounded square
320
- * - 'square': Sharp square
321
- * @default 'circle'
322
- */
323
- shape?: AvatarShape;
324
279
  /**
325
280
  * The primary image source for the avatar.
326
281
  */
@@ -524,7 +479,6 @@ export function Avatar({
524
479
  fallbackSrc,
525
480
  name,
526
481
  size = 'md',
527
- shape = 'circle',
528
482
  src,
529
483
  status,
530
484
  tooltip = true,
@@ -584,7 +538,6 @@ export function Avatar({
584
538
  : undefined;
585
539
  const avatarGroup = useAvatarGroup();
586
540
  const resolvedSize = avatarGroup?.size ?? size;
587
- const resolvedShape = avatarGroup?.shape ?? shape;
588
541
  const numericSize = useMemo(() => resolveSize(resolvedSize), [resolvedSize]);
589
542
 
590
543
  // Resolve the tooltip content:
@@ -770,9 +723,7 @@ export function Avatar({
770
723
  <div
771
724
  {...stylex.props(
772
725
  styles.status,
773
- resolvedShape === 'circle'
774
- ? dynamicStyles.statusPositionCircle(numericSize)
775
- : dynamicStyles.statusPositionCorner,
726
+ dynamicStyles.statusPosition(numericSize),
776
727
  )}>
777
728
  {status}
778
729
  </div>
@@ -785,7 +736,7 @@ export function Avatar({
785
736
  // tooltip tab-stop focus ring all live here so the interactive
786
737
  // `<a>`/`<button>` and the static `<div>` carry the exact same box.
787
738
  const rootStylexProps = mergeProps(
788
- themeProps('avatar', {size: resolvedSize, shape: resolvedShape}),
739
+ themeProps('avatar', {size: resolvedSize}),
789
740
  focusOutlineProps.focusVisible(
790
741
  styles.wrapper,
791
742
  dynamicStyles.size(numericSize),
@@ -793,7 +744,6 @@ export function Avatar({
793
744
  avatarGroup && groupStyles.ring,
794
745
  avatarGroup && groupStyles.overlap,
795
746
  avatarGroup && groupDynamicStyles.overlap(-avatarGroup.overlap),
796
- shapeStyles[resolvedShape],
797
747
  xstyle,
798
748
  ),
799
749
  className,