@astryxdesign/core 0.7.2-canary.fdd384f → 0.7.2

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 (132) hide show
  1. package/CHANGELOG.md +156 -2
  2. package/README.md +4 -4
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +8 -1
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  7. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  8. package/dist/Chat/ChatLayoutScrollButton.js +1 -0
  9. package/dist/CheckboxList/CheckboxList.d.ts +1 -1
  10. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  11. package/dist/CheckboxList/CheckboxList.js +6 -2
  12. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -1
  13. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  14. package/dist/DropdownMenu/DropdownMenu.js +38 -110
  15. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -1
  16. package/dist/DropdownMenu/DropdownMenuGroup.d.ts +6 -4
  17. package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
  18. package/dist/DropdownMenu/DropdownMenuGroup.js +4 -3
  19. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  20. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +24 -0
  21. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -0
  22. package/dist/DropdownMenu/lazyMenuBottomSheet.js +46 -0
  23. package/dist/DropdownMenu/menuRegionStops.d.ts +38 -0
  24. package/dist/DropdownMenu/menuRegionStops.d.ts.map +1 -0
  25. package/dist/DropdownMenu/menuRegionStops.js +131 -0
  26. package/dist/FormLayout/FormLayout.d.ts +5 -4
  27. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  28. package/dist/FormLayout/FormLayout.js +1 -1
  29. package/dist/FormLayout/FormLayoutContext.d.ts +3 -3
  30. package/dist/FormLayout/FormLayoutContext.js +3 -3
  31. package/dist/Grid/Grid.d.ts +8 -7
  32. package/dist/Grid/Grid.d.ts.map +1 -1
  33. package/dist/Grid/Grid.js +5 -4
  34. package/dist/Grid/GridSpan.d.ts +1 -1
  35. package/dist/Markdown/parser.d.ts.map +1 -1
  36. package/dist/Markdown/parser.js +8 -2
  37. package/dist/Markdown/plugins/headingLinks.d.ts.map +1 -1
  38. package/dist/Markdown/plugins/headingLinks.js +9 -1
  39. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  40. package/dist/PowerSearch/PowerSearch.js +5 -0
  41. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  42. package/dist/PowerSearch/PowerSearchValueEditor.js +45 -11
  43. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  44. package/dist/SideNav/SideNavItem.js +6 -2
  45. package/dist/Token/Token.js +1 -1
  46. package/dist/TopNav/TopNavMegaMenu.js +2 -0
  47. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  48. package/dist/TopNav/TopNavMenu.js +2 -0
  49. package/dist/astryx.css +1 -1
  50. package/dist/reset.a11y.chromium.spec.js +50 -1
  51. package/docs.mjs +4 -4
  52. package/package.json +4 -4
  53. package/scripts/agent-doc-state.mjs +66 -6
  54. package/scripts/postinstall.mjs +44 -3
  55. package/src/Avatar/Avatar.tsx +11 -2
  56. package/src/AvatarGroup/AvatarGroupOverflow.test.tsx +24 -1
  57. package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -0
  58. package/src/Chat/ChatLayoutScrollButton.test.tsx +13 -0
  59. package/src/Chat/ChatLayoutScrollButton.tsx +4 -2
  60. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +64 -19
  61. package/src/CheckboxList/CheckboxList.fr10.test.tsx +29 -2
  62. package/src/CheckboxList/CheckboxList.tsx +4 -1
  63. package/src/Code/Code.doc.mjs +1 -0
  64. package/src/CommandPalette/CommandPalette.doc.mjs +2 -0
  65. package/src/ContextMenu/ContextMenu.test.tsx +12 -2
  66. package/src/Drawer/Drawer.doc.mjs +1 -0
  67. package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +57 -0
  68. package/src/DropdownMenu/DropdownMenu.test.tsx +17 -5
  69. package/src/DropdownMenu/DropdownMenu.tsx +43 -142
  70. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +1 -1
  71. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -1
  72. package/src/DropdownMenu/DropdownMenuGroup.doc.mjs +169 -10
  73. package/src/DropdownMenu/DropdownMenuGroup.tsx +6 -4
  74. package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +126 -0
  75. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +1 -1
  76. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +1 -1
  77. package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +334 -0
  78. package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +43 -0
  79. package/src/DropdownMenu/lazyMenuBottomSheet.ts +71 -0
  80. package/src/DropdownMenu/menuRegionStops.ts +167 -0
  81. package/src/Field/FieldLabel.doc.mjs +133 -26
  82. package/src/Field/FieldLabel.spec.md +172 -0
  83. package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +296 -0
  84. package/src/FormLayout/FormLayout.doc.mjs +6 -3
  85. package/src/FormLayout/FormLayout.spec.md +192 -0
  86. package/src/FormLayout/FormLayout.test.tsx +15 -0
  87. package/src/FormLayout/FormLayout.tsx +7 -6
  88. package/src/FormLayout/FormLayoutContext.ts +3 -3
  89. package/src/Grid/Grid.doc.mjs +7 -1
  90. package/src/Grid/Grid.spec.md +93 -113
  91. package/src/Grid/Grid.test.tsx +22 -0
  92. package/src/Grid/Grid.tsx +10 -9
  93. package/src/Grid/GridSpan.doc.mjs +3 -3
  94. package/src/Grid/GridSpan.tsx +1 -1
  95. package/src/Icon/Icon.doc.mjs +36 -0
  96. package/src/Indicator/Indicator.doc.mjs +3 -3
  97. package/src/Layout/LayoutHeader.doc.mjs +5 -0
  98. package/src/Markdown/parser.ts +8 -2
  99. package/src/Markdown/plugins/headingLinks.ts +9 -1
  100. package/src/MetadataList/MetadataList.doc.mjs +1 -1
  101. package/src/MoreMenu/MoreMenu.test.tsx +5 -1
  102. package/src/PowerSearch/PowerSearch.test.tsx +62 -0
  103. package/src/PowerSearch/PowerSearch.tsx +9 -0
  104. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +71 -0
  105. package/src/PowerSearch/PowerSearchValueEditor.tsx +45 -0
  106. package/src/ProgressBar/ProgressBar.doc.mjs +1 -0
  107. package/src/Resizable/useResizable.doc.mjs +2 -0
  108. package/src/ScrollableArea/ScrollableArea.doc.mjs +1 -1
  109. package/src/SideNav/SideNav.spec.md +227 -0
  110. package/src/SideNav/SideNav.test.tsx +41 -0
  111. package/src/SideNav/SideNavItem.tsx +5 -2
  112. package/src/Spinner/Spinner.doc.mjs +1 -1
  113. package/src/Table/Table.spec.md +6 -1
  114. package/src/Table/plugins/activation/useTableActivation.spec.md +254 -0
  115. package/src/Timestamp/Timestamp.doc.mjs +1 -1
  116. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +4 -0
  117. package/src/Token/Token.tsx +1 -1
  118. package/src/Token/__tests__/TokenRemoveTarget.a11y.chromium.spec.ts +86 -0
  119. package/src/Toolbar/Toolbar.test.tsx +0 -8
  120. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +163 -0
  121. package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +63 -0
  122. package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +142 -0
  123. package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +126 -0
  124. package/src/TopNav/TopNavMegaMenu.test.tsx +25 -0
  125. package/src/TopNav/TopNavMegaMenu.tsx +2 -0
  126. package/src/TopNav/TopNavMenu.test.tsx +27 -1
  127. package/src/TopNav/TopNavMenu.tsx +2 -0
  128. package/src/postinstall.test.mjs +79 -0
  129. package/src/reset.a11y.chromium.spec.ts +52 -1
  130. package/src/reset.css +9 -0
  131. package/src/reset.test.ts +11 -0
  132. package/src/unicodePropertyEscapes.source-build.test.mjs +69 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  schema_version: 3
3
- template_version: 3
3
+ template_version: 7
4
4
  kind: component
5
5
  id: component:Grid
6
6
  authority: draft
@@ -9,10 +9,12 @@ superseded_by: null
9
9
  approved_by: null
10
10
  approved_at: null
11
11
  owners: [cixzhang]
12
- review_triggers: [layout, theming]
12
+ review_triggers: [public-api, behavior, layout, theming, accessibility]
13
13
  verified_by:
14
14
  [
15
15
  packages/core/src/Grid/Grid.test.tsx,
16
+ apps/storybook/stories/Grid.stories.tsx,
17
+ .github/scripts/story-play-guard.js,
16
18
  packages/core/src/theme/themingTargets.test.ts,
17
19
  scripts/check-knowledge.mjs,
18
20
  ]
@@ -20,27 +22,41 @@ modules: []
20
22
  families: [family:layout-primitives]
21
23
  design_specs: []
22
24
  architecture:
23
- [architecture:component-theming-surface, architecture:public-component-api]
25
+ [
26
+ architecture:component-theming-surface,
27
+ architecture:public-component-api,
28
+ architecture:component-test-sufficiency,
29
+ ]
24
30
  contributing: []
25
31
  system_specs: []
26
32
  ---
27
33
 
28
34
  # Grid component contract
29
35
 
36
+ ## Contract at a glance
37
+
38
+ | Area | Contract |
39
+ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
40
+ | Public contract | `Grid` owns fixed and intrinsic column-track construction; `GridSpan` changes one direct item's row or column participation. Both accept root `BaseProps`. |
41
+ | Behavior | A positive number creates exactly N `1fr` tracks without reflow; an object creates fill/fit tracks from a pixel minimum and optional count cap; axis gaps override uniform gap. |
42
+ | End-user impact | Content keeps DOM order and logical direction while responsive object-form grids reduce their count to fit. Fixed tracks may narrow or overflow when their content cannot shrink. |
43
+ | Builder impact | Builders choose fixed numeric tracks or responsive object tracks explicitly. `'full'` spans all explicit columns, not implicit tracks created by an oversized numeric span. |
44
+ | Compatibility/readiness | This draft records released behavior. It changes no public API, default, target, or runtime behavior. |
45
+ | Review checks | Reject automatic reflow for numeric columns, equal-width promises that ignore minimum content, lost gap precedence, raw inline track templates, caller-overridable owned metadata, or `'full'` as all implicit tracks. |
46
+ | Governing rules | `family:layout-primitives/FR1–FR7`; `architecture:public-component-api/INV5–INV9`; `architecture:component-theming-surface/INV2–INV6`. |
47
+
48
+ This table is a review projection; the body below is authoritative.
49
+
30
50
  ## Intent
31
51
 
32
- Grid arranges caller-supplied items in two-dimensional tracks. GridSpan
33
- optionally wraps one item to control its row or column participation. This draft
34
- records the current consumer anatomy and theming ownership without changing
35
- layout, targets, or public API.
52
+ Grid arranges caller-supplied items in two-dimensional tracks. GridSpan optionally wraps one item to control its row or column participation. Neither component owns the content, a surface, or structural page semantics.
36
53
 
37
54
  ## Compatibility and migration
38
55
 
39
- - Released default preserved: `yes`
40
- - Compatibility class: additive documentation only; runtime, DOM, styling,
41
- targets, and public API remain unchanged
42
- - Controlled/uncontrolled behavior: not applicable
43
- - Migration decision: none
56
+ - Released default preserved: yes; omitted, zero, or negative numeric `columns` resolves to one track.
57
+ - Compatibility class: observational only; runtime, DOM, styling, targets, and public API remain unchanged.
58
+ - Controlled/uncontrolled behavior: not applicable.
59
+ - Migration decision: none.
44
60
 
45
61
  Consumer migration instructions belong in consumer docs and release notes.
46
62
 
@@ -48,106 +64,82 @@ Consumer migration instructions belong in consumer docs and release notes.
48
64
 
49
65
  **Owns**
50
66
 
51
- - The Grid container and its current `grid` theming target.
52
- - The optional GridSpan wrapper and its current `grid-span` theming target.
53
- - Grid track construction and GridSpan row/column participation.
67
+ - Grid's root container, current `grid` target, track construction, axis gaps, item alignment, and explicit box dimensions.
68
+ - GridSpan's optional wrapper, current `grid-span` target, and one item's row/column participation.
69
+ - Root prop and style composition on each owned element.
54
70
 
55
71
  **Does not own / non-goals**
56
72
 
57
- - Content supplied to Grid or GridSpan — owned by the caller.
58
- - Structural page-region or surface semantics.
59
- - New responsive behavior, layout props, targets, or target placement.
73
+ - Caller content, item semantics, surfaces, or interaction — owned by the product callsite or rendered child component.
74
+ - A shared breakpoint or automatic reflow for numeric columns — responsiveness is selected by Grid's object form.
75
+ - Implicit tracks created by CSS Grid when a numeric span exceeds the explicit grid.
60
76
 
61
77
  ## Public concepts
62
78
 
63
- No new public concept is introduced. Consumer props and usage remain documented
64
- in the canonical `Grid.doc.mjs` and the subordinate `GridSpan.doc.mjs`;
65
- `family:layout-primitives` owns the shared composition vocabulary.
79
+ | Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
80
+ | -------------------- | ------------------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------- | --------------------------- | -------------------------- | --------- | --------------------------------------------------------- |
81
+ | column shape | fixed number; responsive object; omitted | Selects fixed `1fr` tracks or intrinsic fill/fit tracks. | Every Grid | one track | `component:Grid` | released | zero/negative number falls back to one track |
82
+ | responsive repeat | `fill`; `fit` | Keeps empty tracks or collapses them so present items stretch. | Object-form columns | `fill` | `component:Grid` | released | Type-constrained |
83
+ | responsive count cap | absent; positive `max`; non-positive `max` | Caps the number of intrinsic tracks while allowing present tracks fill. | Object-form columns | absent | `component:Grid` | released | non-positive behaves as uncapped |
84
+ | spacing | shared `SpacingStep` values | Separates rows/columns; axis values override uniform gap. | Every Grid | browser grid gap default | `family:layout-primitives` | released | Type-constrained |
85
+ | item alignment | `start`; `center`; `end`; `stretch` | Sets `align-items` and `justify-items`. | Every Grid | `stretch` | `component:Grid` | released | Type-constrained |
86
+ | box size | number; CSS value string; absent | Sizes the Grid box; numbers become pixels. | Every Grid | caller/container-determined | `family:layout-primitives` | released | Browser validates strings |
87
+ | implicit row height | number; absent | Sets pixel height for implicit rows used by row spans. | Every Grid | browser `auto` | `component:Grid` | released | Browser validates the resulting CSS |
88
+ | column participation | number; `full`; absent | Spans N columns or every explicit column. | GridSpan under a grid | intrinsic item placement | `component:GridSpan` | released | Browser resolves numeric spans, including implicit tracks |
89
+ | row participation | number; absent | Spans N rows. | GridSpan under a grid | intrinsic item placement | `component:GridSpan` | released | Browser validates the resulting CSS |
66
90
 
67
91
  ## Behavioral and layout contract
68
92
 
69
- | ID | Candidate invariant | Basis | Draft review state |
70
- | --- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------------------------------------------------- |
71
- | FR1 | Grid renders one Grid container carrying the current `grid` target and arranges caller content using the current track model. | Current source, docs, tests, and family | Verified current behavior; no new behavior decided |
72
- | FR2 | GridSpan renders one optional Spanning item wrapper carrying the current `grid-span` target. | Current source, docs, and tests | Verified current behavior; no target change |
73
- | FR3 | GridSpan's current `columns` and `rows` inputs affect only the wrapped item's participation in its parent grid. | Current source, tests, and family | Verified current behavior; no layout change |
93
+ Draft requirements identify their basis so observed code is not mistaken for an intentional decision.
94
+
95
+ | ID | Candidate invariant | Basis | Draft review state |
96
+ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------- |
97
+ | FR1 | A positive numeric `columns={N}` creates exactly N `1fr` tracks at every width and never reflows; minimum content may widen one track and overflow a narrow container. | DEC-1; released docs, source, tests, and browser evidence | settled observation |
98
+ | FR2 | Object-form columns use auto-fill by default or auto-fit for `repeat: 'fit'`; `minWidth` is the track floor, while a positive `max` caps count and allows a lone track to shrink to the container. | Released docs, source, tests, and browser evidence | settled observation |
99
+ | FR3 | `rowGap` overrides `gap` only for rows, and `columnGap` overrides `gap` only for columns; the effective column gap participates in capped-track math. | `family:layout-primitives/FR4`; source and tests | settled observation |
100
+ | FR4 | Grid's dynamic track template uses CSS-variable-backed StyleX so caller `xstyle` and class rules, including media rules, can replace it. Caller `style` wins other inline declarations, including `gridTemplateColumns`; explicit `width`, `height`, `maxWidth`, and `minHeight` props override matching `style` keys. | `architecture:public-component-api/INV5–INV8`; source/tests | settled observation |
101
+ | FR5 | GridSpan changes only its wrapper's participation. `columns="full"` uses `1 / -1`, which covers explicit tracks; a wider numeric span may create implicit tracks that participate in later placement. | Released source, tests, and browser CSS Grid behavior | settled observation |
102
+ | FR6 | Root refs, DOM/data/ARIA/events, `xstyle`, class name, and inline style reach or compose on the owned root without replacing the canonical target or Grid's owned reflected metadata. | `architecture:public-component-api/INV5–INV8` | settled observation |
103
+ | FR7 | Grid and GridSpan preserve caller DOM order and use the document's logical inline direction; neither creates a separate keyboard order. | Platform layout behavior and browser evidence | settled observation |
74
104
 
75
105
  ### Allowed variation
76
106
 
77
- - Grid may use fixed or intrinsic responsive tracks without changing Grid
78
- container ownership.
79
- - Caller content may render directly in Grid or inside optional GridSpan wrappers.
80
- - A Spanning item may span columns, rows, both, or neither while retaining the
81
- same target.
82
-
83
- ### Responsive contract
84
-
85
- The `columns` prop decides how many column tracks Grid lays out and whether
86
- that count changes with the container's width. Other inputs change the result
87
- only by changing what `columns` has to fit: `width` and `maxWidth` set the
88
- container width a reflowing shape measures against; an `xstyle`, `className`,
89
- or `style` that sets the track template (including under a media query)
90
- replaces the computed one; and a numeric GridSpan wider than the grid's tracks
91
- makes the browser add implicit tracks to the whole grid, which later items flow
92
- into. No other prop changes the count.
93
-
94
- | `columns` | Behavior | Reflows |
95
- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
96
- | `N` (number) | Exactly N tracks that share the row equally at every container width; a child wider than its share widens its track and can push the grid past a narrow container. | never |
97
- | `{minWidth}` | As many tracks as fit while each stays at least `minWidth` px wide. A container narrower than `minWidth` keeps one `minWidth`-wide track that overflows it. Empty tracks keep their width. | yes |
98
- | `{minWidth, repeat: 'fit'}` | As above, and empty tracks collapse so the present items stretch across the row. | yes |
99
- | `{minWidth, max}` | Up to `max` tracks, each at least `minWidth` px and never narrower than an equal share of the row; fewer tracks as the container narrows, down to one track that shrinks to the container rather than overflowing it. `repeat: 'fit'` applies the same way. | yes |
100
- | omitted, `0`, or negative | One track. | never |
101
-
102
- - `gap` and `columnGap` count toward the width a reflowing shape has to fit, so
103
- the full count is reached only when the container has room for the tracks and
104
- the gaps between them. They never change the count of a numeric `columns`.
105
- - `height`, `minHeight`, `rowHeight`, `align`, and `justify` size or place the
106
- container and its items and do not change the column count.
107
- - GridSpan spans `k` tracks, or the whole row for `'full'`. `'full'` never
108
- adds tracks. A numeric span wider than the grid's current tracks makes the
109
- browser add implicit tracks to the grid for the remainder; they persist for
110
- every row, items placed before and after the span flow into them, and once
111
- they exist `'full'` covers only the explicit tracks. In a reflowing grid keep
112
- spans within the smallest count the grid can reach or use `'full'`.
113
- - For columns that reflow on small screens, use `{minWidth, max}`: `max` is
114
- what lets the last track shrink to a narrow container, so `{minWidth}` alone
115
- still overflows a phone narrower than `minWidth`. A numeric `columns` never
116
- reflows, so a four-up numeric row on a phone renders four narrow tracks, or
117
- overflows when a child cannot shrink to its share; that is the fixed
118
- contract, not a defect.
107
+ - **AV1 — Caller content.** Any caller-owned element may participate directly or inside GridSpan; Grid does not inspect or rewrite it.
108
+ - **AV2 — Track count.** Container width, minimum content, gaps, and object-form options may change the rendered responsive count within FR1–FR3.
109
+ - **AV3 — Root styling.** Caller styles may replace track templates or box presentation while preserving component-owned targets and metadata.
119
110
 
120
111
  ### Representative states
121
112
 
122
- | State | Required invariant | Allowed variation |
123
- | -------------- | --------------------------------------------------------- | ----------------------------------------------------- |
124
- | Fixed Grid | Grid container owns the current `grid` target. | Positive fixed column count and spacing may vary. |
125
- | Intrinsic Grid | The same container owns responsive track construction. | Fill/fit mode, minimum width, and count cap may vary. |
126
- | Direct item | Caller content participates without a Grid-owned wrapper. | Caller owns its element and styling. |
127
- | Spanning item | GridSpan owns one wrapper and the `grid-span` target. | Column and row span values may vary. |
113
+ | State | Required invariant | Allowed variation |
114
+ | --------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------- |
115
+ | fixed Grid | Exactly N explicit `1fr` tracks; no component-owned reflow. | N, content minimums, gaps, and caller styling. |
116
+ | responsive fill | Empty tracks remain allocated while count responds to available width. | Minimum width, cap, content count, and effective gap. |
117
+ | responsive fit | Empty tracks collapse and present items stretch. | Minimum width, cap, content count, and effective gap. |
118
+ | capped narrow Grid | Count drops and one present track can fill a container below `minWidth`. | Container width and caller content. |
119
+ | direct item | Caller content participates without a Grid-owned wrapper. | Any caller-owned element and semantics. |
120
+ | numeric spanning item | The wrapper spans N rows/columns and may cause implicit tracks. | Positive browser-valid span counts. |
121
+ | full spanning item | The wrapper covers the explicit grid from line 1 to -1. | Explicit count and caller content. |
128
122
 
129
123
  ### Transformation and precedence order
130
124
 
131
- - No new track construction, gap, alignment, sizing, or span precedence rule is
132
- introduced.
125
+ - **ORD1 — Track shape.** Responsive object with positive `max` → capped fill/fit template; responsive object without positive `max` → uncapped fill/fit template; positive number → fixed `repeat(N, 1fr)`; otherwise → one `1fr` track.
126
+ - **ORD2 — Gap.** Axis gap → uniform gap → zero gap for capped-track arithmetic.
127
+ - **ORD3 — Root composition.** Neutral pass-throughs apply first; component-owned target/reflection and StyleX compose next; `xstyle`, class name, and caller inline style retain their documented styling precedence.
128
+ - **ORD4 — Span.** `'full'` becomes `1 / -1`; a number becomes `span N`; absence leaves browser placement intrinsic.
133
129
 
134
130
  ### Performance and resources
135
131
 
136
- - No new performance or resource rule is introduced.
132
+ - **PR1 — CSS-owned layout.** Grid and GridSpan own no state, effect, observer, listener, timer, or JavaScript measurement loop; layout and reflow remain CSS-owned.
137
133
 
138
134
  ## Accessibility contract
139
135
 
140
- This draft does not add semantic roles to Grid or GridSpan or change their
141
- current DOM forwarding and caller-owned content semantics.
136
+ - **AR1 — Layout remains nonsemantic.** Grid and GridSpan render `<div>` elements without inventing grid, list, group, or landmark semantics; callers provide semantics when their composition needs them.
137
+ - **AR2 — Order remains perceivable.** DOM order remains the reading and keyboard order in LTR and RTL; visual tracks do not create a second sequence.
138
+ - **AR3 — Content remains reachable.** GridSpan keeps a zero inline minimum so supported shrinkable descendants can remain inside their track; caller content that cannot shrink may still overflow under FR1.
142
139
 
143
140
  ## Design relationships
144
141
 
145
- | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
146
- | ---------------- | ------------------------------------------------------------------ | ----------------------------------------- | -------------- | ------------------ |
147
- | Grid container | Presents the current two-dimensional layout container. | Current source, docs, and family contract | Supporting | FR1 |
148
- | Spanning item | Optionally changes one item's row or column participation in Grid. | Current source, docs, and family contract | Supporting | FR2, FR3 |
149
-
150
- Direct caller content is not an additional Grid-owned anatomy part.
142
+ No current component-local or shared design record prescribes a Grid-specific visual representation. Grid and GridSpan own geometry only; caller content owns visible surfaces and states.
151
143
 
152
144
  ### Theming anatomy
153
145
 
@@ -162,42 +154,32 @@ Direct caller content is not an additional Grid-owned anatomy part.
162
154
 
163
155
  ## Family and system relationships
164
156
 
165
- - `family:layout-primitives` owns the shared spacing, sizing, alignment, and item
166
- participation vocabulary for Grid and GridSpan.
167
- - `architecture:component-theming-surface` owns anatomy qualification and the
168
- two current local target mappings.
169
- - `architecture:public-component-api` owns the stable prop surface; this
170
- documentation adds no API.
157
+ - `family:layout-primitives` owns shared spacing, sizing, alignment, and item-participation vocabulary; Grid owns the two-dimensional realization.
158
+ - `architecture:public-component-api` owns root prop, styling, event, ref, and released-type composition.
159
+ - `architecture:component-theming-surface` owns the two exact target mappings and Grid's reflected visual axes.
160
+ - `architecture:component-test-sufficiency` owns bounded, mutation-sensitive evidence selection.
171
161
 
172
162
  ## Verification map
173
163
 
174
- | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
175
- | ------------------- | ------------------------------------------------------------ | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
176
- | FR1 | `Grid.test.tsx` track, sizing, alignment, and content suites | Fixed, default, fill, fit, capped, and sized | Changing current track output or removing the container/content fails exact style-variable, inline-style, or content checks. | `audit:Grid/anatomy` |
177
- | FR2, FR3 | `Grid.test.tsx` GridSpan suites | Column, full-row, row, combined, and no span | Changing wrapper rendering or span values fails exact inline-style and content assertions. | `audit:GridSpan/anatomy` |
178
- | Target inventory | Source inspection and `themingTargets.test.ts` | Grid and GridSpan targets | Runtime and documented target metadata drift fails target validation. | `audit:Grid/theming` |
179
- | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and both current targets | Missing, extra, prefixed, or stale mappings fail repository validation. | `audit:Grid/theming` |
180
-
181
- Grid tests pin track and span output, content rendering, and prop forwarding.
182
- They do not assert both target classes in focused component tests; target
183
- placement is source-inspected and the global inventory checks the runtime/docs
184
- relationship.
164
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
165
+ | --------------- | ------------------------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------- |
166
+ | FR1–FR3, ORD1–2 | `Grid.test.tsx` exact track tests and exact-head AuditMatrix story guards | fixed; fill; fit; capped wide/narrow; uniform/axis gap | Track output, count, fill behavior, or gap precedence changes fail exact source or browser checks. | `audit:Grid/behavior` |
167
+ | FR4, FR6, ORD3 | root composition tests, BaseProps lint, theming target tests | DOM/data/ARIA/events; class/style/xstyle; reflected axes | A caller input drops, owned metadata is replaced, or a raw inline track defeats media xstyle. | `audit:Grid/api` |
168
+ | FR5, ORD4, AR3 | `Grid.test.tsx`, long-content play, and AuditMatrix full-span assertion | numeric/full/row/combined spans; long shrinkable child | Span CSS, explicit-grid coverage, target, or bounded shrink behavior changes. | `audit:GridSpan/behavior` |
169
+ | FR7, AR1–AR2 | DOM tests and exact-head LTR/RTL AuditMatrix guards | plain roots; fixed tracks in LTR/RTL | A semantic role appears, DOM order changes, or logical order stops mirroring. | `audit:Grid/a11y-rtl` |
170
+ | Theming anatomy | knowledge validation and `themingTargets.test.ts` | Grid and GridSpan targets | A target loses its semantic owner or the anatomy map drifts. | `audit:Grid/theming` |
171
+ | Documentation | docs typecheck, prop literal/reference tests, and Storybook typecheck | fixed/responsive columns; row height; spans | Docs claim equal widths, automatic numeric reflow, whole implicit rows, or omit public behavior. | `audit:Grid/docs` |
185
172
 
186
173
  ## Decision log
187
174
 
188
175
  ### DEC-1 — Numeric `columns` stays fixed; reflow uses the existing object form
189
176
 
177
+ **Reference:** `component:Grid/DEC-1`
190
178
  **Decider:** `cixzhang`, `2026-10-11`
191
179
 
192
- `columns={N}` means exactly N equal tracks at every width, and a grid that
193
- reflows on small screens uses `{minWidth, max?, repeat?}`. Two shapes, one
194
- responsibility each: the number is the fixed contract, the object form is the
195
- responsive one, and the record and the docs say so where a caller first meets
196
- `columns`.
180
+ `columns={N}` means exactly N `1fr` tracks at every width, and a grid that reflows on small screens uses `{minWidth, max?, repeat?}`. The number is the fixed contract; the object form is the responsive contract.
197
181
 
198
- Rejected: redefining the number as "at most N" (breaks every released numeric
199
- grid and needs an opt-out shape); a `{count, isFixed}` shape (duplicates the
200
- number and the `max` cap).
182
+ Rejected: redefining the number as "at most N" would break released numeric grids; a `{count, isFixed}` shape would duplicate the number and the `max` cap.
201
183
 
202
184
  ## Open questions
203
185
 
@@ -205,6 +187,4 @@ None.
205
187
 
206
188
  ## Content boundary
207
189
 
208
- This file does not duplicate consumer prop tables, examples, family layout
209
- rules, track algorithms, implementation steps, or system rules. It links to
210
- their owners.
190
+ This file records verified shipped behavior. It does not duplicate consumer prop tables or examples, prescribe caller content or visuals, approve a new API or target, or store audit scores and run evidence.
@@ -308,6 +308,28 @@ describe('Grid', () => {
308
308
  expect(grid).toHaveAttribute('aria-label', 'Product grid');
309
309
  });
310
310
 
311
+ it('keeps component-owned theme metadata over colliding pass-throughs', () => {
312
+ render(
313
+ <Grid
314
+ columns={3}
315
+ gap={4}
316
+ align="end"
317
+ justify="center"
318
+ data-columns="9"
319
+ data-gap="10"
320
+ data-align="start"
321
+ data-justify="stretch"
322
+ data-testid="grid">
323
+ <div>Item</div>
324
+ </Grid>,
325
+ );
326
+ const grid = screen.getByTestId('grid');
327
+ expect(grid).toHaveAttribute('data-columns', '3');
328
+ expect(grid).toHaveAttribute('data-gap', '4');
329
+ expect(grid).toHaveAttribute('data-align', 'end');
330
+ expect(grid).toHaveAttribute('data-justify', 'center');
331
+ });
332
+
311
333
  it('renders children correctly', () => {
312
334
  render(
313
335
  <Grid columns={3} data-testid="grid">
package/src/Grid/Grid.tsx CHANGED
@@ -31,13 +31,14 @@ export type GridAlignment = 'start' | 'center' | 'end' | 'stretch';
31
31
  /**
32
32
  * Column configuration for Grid.
33
33
  *
34
- * A number means exactly N equal columns at every width; it never reflows, so
35
- * `columns={4}` on a phone renders four narrow tracks. For columns that reflow
34
+ * A number means exactly N `1fr` tracks at every width; it never reflows, so
35
+ * `columns={4}` on a phone renders four narrow tracks. A child's minimum
36
+ * content can widen its track beyond an equal fraction. For columns that reflow
36
37
  * on small screens, use the object form: `{minWidth: 240, max: 4}` renders at
37
38
  * most four columns, each at least 240px, and fewer when the container is
38
39
  * narrower.
39
40
  *
40
- * - `number` — exactly N equal-width columns (e.g. `columns={3}`); never reflows
41
+ * - `number` — exactly N `1fr` tracks (e.g. `columns={3}`); never reflows
41
42
  * - `object` — responsive columns based on minimum child width:
42
43
  * - `minWidth` — minimum width (px) for each column track
43
44
  * - `repeat` — `'fill'` (default) preserves empty tracks for consistent widths;
@@ -59,10 +60,10 @@ export interface GridProps extends BaseProps<HTMLDivElement> {
59
60
  /** Ref forwarded to the root element */
60
61
  ref?: React.Ref<HTMLDivElement>;
61
62
  /**
62
- * Column configuration. A number is exactly N equal columns and never
63
- * reflows; for columns that reflow on small screens, use
64
- * `{minWidth, max}`.
65
- * - `number` — exactly N equal-width columns at every width
63
+ * Column configuration. A number creates exactly N `1fr` tracks and never
64
+ * reflows; a child's minimum content may widen its track. For columns that
65
+ * reflow on small screens, use `{minWidth, max}`.
66
+ * - `number` — exactly N `1fr` tracks at every width
66
67
  * - `{minWidth, max?, repeat?}` — responsive columns with a px floor and an
67
68
  * optional cap
68
69
  *
@@ -455,6 +456,7 @@ export function Grid({
455
456
  return (
456
457
  <div
457
458
  ref={ref}
459
+ {...props}
458
460
  {...mergeProps(
459
461
  themeProps('grid', {columns: columnsVariant, gap, align, justify}),
460
462
  stylex.props(
@@ -470,8 +472,7 @@ export function Grid({
470
472
  ),
471
473
  className,
472
474
  {...style, ...inlineStyle},
473
- )}
474
- {...props}>
475
+ )}>
475
476
  {children}
476
477
  </div>
477
478
  );
@@ -19,7 +19,7 @@ export const docs = {
19
19
  {
20
20
  name: 'columns',
21
21
  type: "number | 'full'",
22
- description: "Columns to span; use `'full'` to span the entire row.",
22
+ description: "Columns to span; use `'full'` to span all explicit columns.",
23
23
  },
24
24
  {
25
25
  name: 'rows',
@@ -43,7 +43,7 @@ export const docsZh = {
43
43
  {
44
44
  name: 'columns',
45
45
  type: "number | 'full'",
46
- description: "要跨越的列数;使用 `'full'` 跨越整行。",
46
+ description: "要跨越的列数;使用 `'full'` 跨越所有显式列。",
47
47
  },
48
48
  {
49
49
  name: 'rows',
@@ -64,7 +64,7 @@ export const docsDense = {
64
64
  displayName: 'Grid Span',
65
65
  description: 'Grid item spanning multiple columns/rows.',
66
66
  propDescriptions: {
67
- columns: "Columns to span; 'full' spans entire row.",
67
+ columns: "Columns to span; 'full' spans all explicit columns.",
68
68
  rows: 'Rows to span.',
69
69
  children: 'Content.',
70
70
  },
@@ -25,7 +25,7 @@ export interface GridSpanProps extends BaseProps<HTMLDivElement> {
25
25
  /**
26
26
  * Number of columns to span, or 'full' to span all columns.
27
27
  * - Number: `grid-column: span N`
28
- * - 'full': `grid-column: 1 / -1` (spans entire row)
28
+ * - 'full': `grid-column: 1 / -1` (spans all explicit columns)
29
29
  */
30
30
  columns?: number | 'full';
31
31
 
@@ -23,6 +23,42 @@ export const docs = {
23
23
  'pictogram',
24
24
  'graphic',
25
25
  'vector',
26
+ 'arrow',
27
+ 'chevron',
28
+ 'caret',
29
+ 'checkmark',
30
+ 'cross',
31
+ 'gear',
32
+ 'cog',
33
+ 'broom',
34
+ 'sparkle',
35
+ 'sparkles',
36
+ 'star',
37
+ 'heart',
38
+ 'bell',
39
+ 'trash',
40
+ 'pencil',
41
+ 'magnifyingglass',
42
+ 'paperclip',
43
+ 'house',
44
+ 'flame',
45
+ 'rocket',
46
+ 'lightning',
47
+ 'bolt',
48
+ 'globe',
49
+ 'sun',
50
+ 'moon',
51
+ 'bookmark',
52
+ 'thumbsup',
53
+ 'smiley',
54
+ 'emoji',
55
+ 'lock',
56
+ 'eye',
57
+ 'cloud',
58
+ 'hourglass',
59
+ 'trophy',
60
+ 'gift',
61
+ 'camera',
26
62
  ],
27
63
  playground: {
28
64
  // `icon` is required and its type can't be auto-generated, so the
@@ -171,7 +171,7 @@ defineTheme({
171
171
  code: `// When the shape itself is wrong, hand the theme a component. It receives
172
172
  // {state, size, isDisabled, children} and nothing else.
173
173
  //
174
- // Use theme tokens, never raw values — run \`npx @astryxdesign/cli docs tokens\` for the
174
+ // Use theme tokens, never raw values — run \`astryx docs tokens\` for the
175
175
  // full set. Color: --color-accent, --color-on-accent, --color-border,
176
176
  // --color-border-emphasized, --color-background-surface,
177
177
  // --color-background-muted. Radius: --radius-inner, --radius-full.
@@ -236,7 +236,7 @@ defineTheme({name: 'brand', indicators: {check: RadioIndicator}});`,
236
236
  {
237
237
  guidance: true,
238
238
  description:
239
- 'Use theme tokens for every color, radius, and border width in a replacement. Run `npx @astryxdesign/cli docs tokens` for the set.',
239
+ 'Use theme tokens for every color, radius, and border width in a replacement. Run `astryx docs tokens` for the set.',
240
240
  },
241
241
  {
242
242
  guidance: true,
@@ -297,7 +297,7 @@ export const docsDense = {
297
297
  {
298
298
  guidance: true,
299
299
  description:
300
- 'Use theme tokens for every color, radius, and border width in a replacement. Run `npx @astryxdesign/cli docs tokens` for the set.',
300
+ 'Use theme tokens for every color, radius, and border width in a replacement. Run `astryx docs tokens` for the set.',
301
301
  },
302
302
  {
303
303
  guidance: true,
@@ -6,6 +6,11 @@ export const docs = {
6
6
  name: 'LayoutHeader',
7
7
  subComponentOf: 'Layout',
8
8
  displayName: 'Layout Header',
9
+ keywords: [
10
+ 'pageheader',
11
+ 'appheader',
12
+ 'header',
13
+ ],
9
14
  isHiddenFromOverview: true,
10
15
  description: 'Top bar for page titles, app bars, and toolbars.',
11
16
  playground: {
@@ -1718,8 +1718,14 @@ interface DelimiterRun {
1718
1718
  readonly canClose: boolean;
1719
1719
  }
1720
1720
 
1721
- const UNICODE_WHITESPACE = /^[\t\n\f\r\p{Zs}]$/u;
1722
- const UNICODE_PUNCTUATION = /^[\p{P}\p{S}]$/u;
1721
+ // Built at runtime, not written as literals: for targets without property
1722
+ // escapes, or when no browserslist is set, Babel's
1723
+ // transform-unicode-property-regex rewrites a `\p{…}` literal, and the copy
1724
+ // bundled with Next.js lacks the Unicode data it needs, so a source build fails
1725
+ // with "Unknown property". Literals are safe once Next's bundled Babel ships
1726
+ // that data.
1727
+ const UNICODE_WHITESPACE = new RegExp('^[\\t\\n\\f\\r\\p{Zs}]$', 'u');
1728
+ const UNICODE_PUNCTUATION = new RegExp('^[\\p{P}\\p{S}]$', 'u');
1723
1729
 
1724
1730
  /** The character before `index` (a whole code point), if any. */
1725
1731
  function characterBefore(text: string, index: number): string | undefined {
@@ -59,13 +59,21 @@ function fail(message: string): never {
59
59
  throw new TypeError(`Markdown heading links: ${message}`);
60
60
  }
61
61
 
62
+ // Built at runtime, not written as a literal: for targets without property
63
+ // escapes, or when no browserslist is set, Babel's
64
+ // transform-unicode-property-regex rewrites a `\p{…}` literal, and the copy
65
+ // bundled with Next.js lacks the Unicode data it needs, so a source build fails
66
+ // with "Unknown property". A literal is safe once Next's bundled Babel ships
67
+ // that data.
68
+ const NON_SLUG_CHARACTERS = new RegExp('[^\\p{Letter}\\p{Number}]+', 'gu');
69
+
62
70
  function headingSlug(value: string): string {
63
71
  return value
64
72
  .normalize('NFKC')
65
73
  .trim()
66
74
  .toLowerCase()
67
75
  .replace(/['"]/gu, '')
68
- .replace(/[^\p{Letter}\p{Number}]+/gu, '-')
76
+ .replace(NON_SLUG_CHARACTERS, '-')
69
77
  .replace(/^-+|-+$/gu, '');
70
78
  }
71
79
 
@@ -7,7 +7,7 @@ export const docs = {
7
7
  displayName: 'Metadata List',
8
8
  group: 'MetadataList',
9
9
  category: 'Table & List',
10
- keywords: ["metadata","description","definition","keyvalue","properties","details","attributes","summary"],
10
+ keywords: ["metadata","description","definition","keyvalue","properties","details","attributes","summary","descriptionlist","descriptions"],
11
11
  theming: {
12
12
  targets: [
13
13
  {
@@ -9,11 +9,15 @@
9
9
  * SYNC: When MoreMenu.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeEach} from 'vitest';
12
+ import {describe, it, expect, vi, beforeAll, beforeEach} from 'vitest';
13
13
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {MoreMenu} from './MoreMenu';
16
16
  import {DropdownMenu} from '../DropdownMenu/DropdownMenu';
17
+ import {warmMenuBottomSheet} from '../DropdownMenu/__tests__/warmMenuBottomSheet';
18
+
19
+ // DropdownMenu's touch sheet is loaded lazily; resolve it once up front.
20
+ beforeAll(warmMenuBottomSheet, 20000);
17
21
 
18
22
  // Mock showPopover and hidePopover methods since they're not implemented in jsdom
19
23
  beforeEach(() => {