@astryxdesign/core 0.5.2-canary.1e29ea2 → 0.5.2-canary.1f005bf

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 (72) hide show
  1. package/package.json +2 -2
  2. package/src/BottomSheet/BottomSheet.doc.mjs +31 -0
  3. package/src/BottomSheet/BottomSheet.spec.md +177 -0
  4. package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
  5. package/src/CheckboxList/CheckboxList.spec.md +214 -0
  6. package/src/CommandPalette/CommandPalette.spec.md +2 -2
  7. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  8. package/src/ComplexSelector/ComplexSelector.spec.md +189 -0
  9. package/src/ContextMenu/ContextMenu.doc.mjs +48 -0
  10. package/src/ContextMenu/ContextMenu.spec.md +206 -0
  11. package/src/Divider/Divider.doc.mjs +24 -0
  12. package/src/Divider/Divider.spec.md +150 -0
  13. package/src/DropdownMenu/DropdownMenu.doc.mjs +104 -0
  14. package/src/DropdownMenu/DropdownMenu.spec.md +245 -0
  15. package/src/Grid/Grid.doc.mjs +19 -0
  16. package/src/Grid/Grid.spec.md +161 -0
  17. package/src/Icon/Icon.doc.mjs +196 -48
  18. package/src/Icon/Icon.spec.md +191 -0
  19. package/src/Kbd/Kbd.doc.mjs +18 -0
  20. package/src/Kbd/Kbd.spec.md +141 -0
  21. package/src/Layout/Layout.doc.mjs +32 -0
  22. package/src/Layout/Layout.spec.md +199 -0
  23. package/src/Lightbox/Lightbox.doc.mjs +149 -33
  24. package/src/Lightbox/Lightbox.spec.md +197 -0
  25. package/src/Markdown/Markdown.doc.mjs +60 -0
  26. package/src/Markdown/Markdown.spec.md +200 -0
  27. package/src/MobileNav/MobileNav.doc.mjs +140 -32
  28. package/src/MobileNav/MobileNav.spec.md +208 -0
  29. package/src/MoreMenu/MoreMenu.doc.mjs +42 -0
  30. package/src/MoreMenu/MoreMenu.spec.md +221 -0
  31. package/src/MultiSelector/MultiSelector.doc.mjs +112 -0
  32. package/src/MultiSelector/MultiSelector.spec.md +266 -0
  33. package/src/NavIcon/NavIcon.doc.mjs +17 -0
  34. package/src/NavIcon/NavIcon.spec.md +133 -0
  35. package/src/NavMenu/NavHeadingMenu.spec.md +205 -0
  36. package/src/NavMenu/NavMenu.doc.mjs +37 -0
  37. package/src/Outline/Outline.doc.mjs +35 -0
  38. package/src/Outline/Outline.spec.md +161 -0
  39. package/src/OverflowList/OverflowList.doc.mjs +24 -0
  40. package/src/OverflowList/OverflowList.spec.md +156 -0
  41. package/src/Pagination/Pagination.doc.mjs +74 -0
  42. package/src/Pagination/Pagination.spec.md +225 -0
  43. package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
  44. package/src/ProgressBar/ProgressBar.spec.md +77 -25
  45. package/src/Resizable/Resizable.doc.mjs +59 -16
  46. package/src/Resizable/Resizable.spec.md +194 -0
  47. package/src/Section/Section.doc.mjs +17 -0
  48. package/src/Section/Section.spec.md +138 -0
  49. package/src/SegmentedControl/SegmentedControl.doc.mjs +27 -0
  50. package/src/SegmentedControl/SegmentedControl.spec.md +151 -0
  51. package/src/Selector/Selector.spec.md +202 -0
  52. package/src/Skeleton/Skeleton.doc.mjs +12 -0
  53. package/src/Skeleton/Skeleton.spec.md +126 -0
  54. package/src/Slider/Slider.doc.mjs +184 -30
  55. package/src/Slider/Slider.spec.md +223 -0
  56. package/src/Stack/Stack.doc.mjs +22 -0
  57. package/src/Stack/Stack.spec.md +154 -0
  58. package/src/StatusDot/StatusDot.doc.mjs +17 -0
  59. package/src/StatusDot/StatusDot.spec.md +137 -0
  60. package/src/Switch/Switch.doc.mjs +40 -0
  61. package/src/Switch/Switch.spec.md +174 -0
  62. package/src/Text/Text.doc.mjs +190 -43
  63. package/src/Text/Text.spec.md +205 -0
  64. package/src/TextArea/TextArea.doc.mjs +255 -45
  65. package/src/TextArea/TextArea.spec.md +204 -0
  66. package/src/Toolbar/Toolbar.doc.mjs +19 -0
  67. package/src/Toolbar/Toolbar.spec.md +173 -0
  68. package/src/Tooltip/Tooltip.doc.mjs +17 -0
  69. package/src/Tooltip/Tooltip.spec.md +141 -0
  70. package/src/TreeList/TreeList.spec.md +5 -5
  71. package/src/Typeahead/Typeahead.doc.mjs +83 -1
  72. package/src/Typeahead/Typeahead.spec.md +250 -0
@@ -0,0 +1,214 @@
1
+ ---
2
+ schema_version: 1
3
+ template_version: 3
4
+ kind: component
5
+ id: component:CheckboxList
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers: [theming]
13
+ verified_by:
14
+ [
15
+ packages/core/src/CheckboxList/CheckboxList.test.tsx,
16
+ packages/core/src/theme/themingTargets.test.ts,
17
+ scripts/check-knowledge.mjs,
18
+ ]
19
+ families: []
20
+ design_specs: []
21
+ architecture: [architecture:component-theming-surface]
22
+ contributing: []
23
+ system_specs: []
24
+ ---
25
+
26
+ # CheckboxList component contract
27
+
28
+ ## Intent
29
+
30
+ CheckboxList presents a labeled group of checkbox options. This draft records
31
+ its current consumer anatomy and the theming ownership of parts rendered by
32
+ CheckboxList, List, CheckboxInput, Field, FieldStatus, and Spinner. It changes no
33
+ runtime behavior or public API.
34
+
35
+ ## Compatibility and migration
36
+
37
+ - Released default preserved: `yes`
38
+ - Compatibility class: additive documentation only; runtime, DOM, styling,
39
+ targets, and public API remain unchanged
40
+ - Controlled/uncontrolled behavior: unchanged
41
+ - Migration decision: none
42
+
43
+ Consumer migration instructions belong in consumer docs and release notes.
44
+
45
+ ## Ownership boundary
46
+
47
+ **Owns**
48
+
49
+ - The checkbox-group composition and its existing `checkbox-list` target.
50
+ - Collection state shared with CheckboxListItem.
51
+
52
+ **Does not own / non-goals**
53
+
54
+ - List and option-row presentation — owned by `component:List`; its
55
+ `list-item` target reaches only the row root.
56
+ - Standard option-label and option-description wrappers — rendered as separate,
57
+ untargeted spans by Item rather than Text; rich label content and end content
58
+ remain caller-owned.
59
+ - The checkbox indicator — owned by `component:CheckboxInput`.
60
+ - Group-label and validation-message presentation — owned by `component:Field`
61
+ and `component:FieldStatus`.
62
+ - Loading-indicator presentation — owned by `component:Spinner`.
63
+ - New targets for untargeted descriptions or caller-provided item content.
64
+
65
+ ## Public concepts
66
+
67
+ No new public concept is introduced. Consumer props, collection and standalone
68
+ item modes, and usage remain documented in `CheckboxList.doc.mjs` and
69
+ `CheckboxListItem.doc.mjs`.
70
+
71
+ ## Behavioral and layout contract
72
+
73
+ | ID | Candidate invariant | Basis | Draft review state |
74
+ | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- | -------------------------------------------------- |
75
+ | FR1 | The current render places CheckboxListItem children inside a List and a group named by the group label. | Current source, docs, and focused tests | Verified current behavior; no new behavior decided |
76
+ | FR2 | The CheckboxList root carries the current `checkbox-list` target. | Current source and public docs | Verified current behavior; no target change |
77
+ | FR3 | The option-row root and checkbox indicator continue to be rendered by List and CheckboxInput rather than reimplemented. | Current source | Verified composition boundary |
78
+ | FR5 | Item renders option label and description as separate untargeted children; end content remains caller-provided inside its own untargeted slot wrapper. | Current source and focused tests | Verified child ownership; no target change |
79
+ | FR4 | A pending item renders Spinner inside that item's checkbox; a status message renders through detached FieldStatus. | Current source, docs, and focused tests | Verified conditional composition |
80
+
81
+ ### Allowed variation
82
+
83
+ - Option labels, descriptions, and end content may vary with caller-provided
84
+ CheckboxListItem content without becoming new CheckboxList targets.
85
+ - Density, dividers, checked state, and disabled state remain capabilities of
86
+ their current owning components rather than separate anatomy parts.
87
+
88
+ ### Representative states
89
+
90
+ | State | Required invariant | Allowed variation |
91
+ | ------------------------- | --------------------------------------------------------------- | ---------------------------------- |
92
+ | Default collection | Named group, options list, rows, and checkbox indicators render | Option content and selected values |
93
+ | Item pending | Only the pending checkbox includes a Spinner | Which option is pending |
94
+ | Group status with message | Detached FieldStatus follows the group | Error, warning, or success status |
95
+
96
+ ### Transformation and precedence order
97
+
98
+ - No new state, event, layout, or styling precedence rule is introduced.
99
+
100
+ ### Performance and resources
101
+
102
+ - No new performance or resource rule is introduced.
103
+
104
+ ## Accessibility contract
105
+
106
+ This draft does not change or extend CheckboxList's existing group naming,
107
+ checkbox semantics, focus order, busy state, or description/status association.
108
+
109
+ ## Design relationships
110
+
111
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
112
+ | ------------------ | ---------------------------------------------------------------------------- | ------------------------------- | -------------- | ------------------ |
113
+ | Group | Contains the current checkbox-group composition. | Current source and public docs | Supporting | FR1, FR2 |
114
+ | Options and rows | Present the List-owned list and row root plus Item-rendered child structure. | Current source | Supporting | FR1, FR3 |
115
+ | Checkbox | Presents each option's current selection indicator. | Current CheckboxInput source | Prominent | FR3 |
116
+ | Spinner and status | Present the current conditional feedback components. | Current shared-component source | Supporting | FR4 |
117
+
118
+ ### Theming anatomy
119
+
120
+ <!-- anatomy-theming:v1 -->
121
+
122
+ ```json
123
+ {
124
+ "Group": {"target": "checkbox-list"},
125
+ "Group label": {
126
+ "delegatesTo": {"owner": "component:Field", "target": "field-label"}
127
+ },
128
+ "Description": {
129
+ "none": {
130
+ "reason": "unsettled: No current public target reaches the stable Description; future exposure still needs an owner decision"
131
+ }
132
+ },
133
+ "Options list": {
134
+ "delegatesTo": {"owner": "component:List", "target": "list"}
135
+ },
136
+ "Option row": {
137
+ "delegatesTo": {"owner": "component:List", "target": "list-item"}
138
+ },
139
+ "Checkbox": {
140
+ "delegatesTo": {
141
+ "owner": "component:CheckboxInput",
142
+ "target": "checkbox-indicator"
143
+ }
144
+ },
145
+ "Option label": {
146
+ "none": {
147
+ "reason": "unsettled: Item rather than Text renders the stable Option label in a separate untargeted span; future exposure still needs an owner decision"
148
+ }
149
+ },
150
+ "Option description": {
151
+ "none": {
152
+ "reason": "unsettled: Item rather than Text renders the stable Option description in a separate untargeted span; future exposure still needs an owner decision"
153
+ }
154
+ },
155
+ "End content": {
156
+ "none": {
157
+ "reason": "intentional: End content is caller-provided content outside CheckboxList's public theming ownership"
158
+ }
159
+ },
160
+ "Spinner": {
161
+ "delegatesTo": {"owner": "component:Spinner", "target": "spinner"}
162
+ },
163
+ "Status message": {
164
+ "delegatesTo": {
165
+ "owner": "component:FieldStatus",
166
+ "target": "field-status"
167
+ }
168
+ }
169
+ }
170
+ ```
171
+
172
+ `Description` remains stable consumer anatomy, but no current public target
173
+ reaches it. Item, not Text, renders option labels and descriptions in separate
174
+ untargeted spans, so neither delegates to Text nor inherits the row-root
175
+ `list-item` target; future exposure remains unsettled. Rich label content and end
176
+ content are caller-provided, and end content intentionally stays outside
177
+ CheckboxList's public theming ownership.
178
+
179
+ ## Family and system relationships
180
+
181
+ - `architecture:component-theming-surface` owns anatomy qualification, factual
182
+ `none` dispositions, and composition-preserving target ownership.
183
+ - List, CheckboxInput, Field, FieldStatus, and Spinner retain their existing
184
+ public target contracts when composed by CheckboxList.
185
+
186
+ ## Verification map
187
+
188
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
189
+ | ------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
190
+ | FR1, FR3, FR5 | `CheckboxList.test.tsx` structure, content, and interaction suites plus `Item.tsx` source inspection | Collection mode; string/rich label; description; end content | Removing the group, row, checkbox, or separate content children breaks existing DOM and interaction assertions. | `audit:CheckboxList/anatomy` |
191
+ | FR4 | `CheckboxList.test.tsx` loading and status suites | Pending item and detached status | Removing composed feedback breaks existing spinner, busy-state, or message assertions. | `audit:CheckboxList/anatomy` |
192
+ | Local target source | `themingTargets.test.ts` | `checkbox-list` | Source/docs target drift fails the repository target guard. | `audit:CheckboxList/theming` |
193
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current local target | Canonical-key drift, invalid dispositions or target spelling, or an unclaimed current local target fails repository validation. | `audit:CheckboxList/theming` |
194
+
195
+ The focused CheckboxList suite does not pin the exact delegated target classes
196
+ or the local `checkbox-list` class in rendered DOM. The source/metadata target
197
+ guard independently verifies that each owner's emitted targets remain
198
+ documented. No current repository check resolves `delegatesTo` owner/target
199
+ pairs; the pairs in this draft were verified manually, so semantic delegation
200
+ drift remains a validation gap.
201
+
202
+ ## Decision log
203
+
204
+ None. This draft records current facts and introduces no component-local design
205
+ or API decision.
206
+
207
+ ## Open questions
208
+
209
+ None.
210
+
211
+ ## Content boundary
212
+
213
+ This file does not duplicate consumer prop tables, examples, implementation
214
+ steps, or shared-component contracts. It links to their owners.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  schema_version: 1
3
- template_version: 2
3
+ template_version: 3
4
4
  kind: component
5
5
  id: component:CommandPalette
6
6
  authority: draft
@@ -153,7 +153,7 @@ reachability, not a decision that it must remain unthemeable.
153
153
  },
154
154
  "Query field": {
155
155
  "none": {
156
- "reason": "No current public target is applied directly to the native query field"
156
+ "reason": "unsettled: No current public target is applied directly to the native query field; future exposure still needs an owner decision"
157
157
  }
158
158
  },
159
159
  "Loading spinner": {
@@ -9,6 +9,46 @@
9
9
  * SYNC: When modified, update ComplexSelector.tsx, tests, and stories.
10
10
  */
11
11
 
12
+ /** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
13
+ const anatomy = [
14
+ {
15
+ name: 'Field',
16
+ required: true,
17
+ description:
18
+ 'Field shell that provides the label and optional supporting field content.',
19
+ },
20
+ {
21
+ name: 'Trigger',
22
+ required: true,
23
+ description:
24
+ 'Control that displays the current value or placeholder and opens the popup.',
25
+ },
26
+ {
27
+ name: 'Icon-rendered start icon',
28
+ required: false,
29
+ description:
30
+ 'Optional leading semantic icon or icon component rendered through Icon.',
31
+ },
32
+ {
33
+ name: 'Caller-rendered start content',
34
+ required: false,
35
+ description:
36
+ 'Optional arbitrary React content rendered directly at the start of the trigger.',
37
+ },
38
+ {
39
+ name: 'Indicator icon',
40
+ required: true,
41
+ description:
42
+ 'Trailing chevron that rotates to reflect whether the popup is open.',
43
+ },
44
+ {
45
+ name: 'Popup',
46
+ required: true,
47
+ description:
48
+ 'Mounted dialog surface that is painted and shown while open and hidden while closed.',
49
+ },
50
+ ];
51
+
12
52
  /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
13
53
 
14
54
  export const docs = {
@@ -158,6 +198,7 @@ export const docs = {
158
198
  },
159
199
  ],
160
200
  usage: {
201
+ anatomy,
161
202
  description:
162
203
  'Use ComplexSelector when a selection needs richer custom content than a Selector option row. It is intentionally one component: ComplexSelector owns the field, trigger, popover, focus restore, and changeAction flow, while the content render prop owns the selector-specific accessible structure.',
163
204
  bestPractices: [
@@ -218,6 +259,7 @@ export const docsDense = {
218
259
  description:
219
260
  'Input/ghost trigger + dialog-popover shell for rich custom selectors. Content gets value/onChange/close/state; content owns semantics. Use focus hooks and evaluate custom content against WCAG 2.2.',
220
261
  usage: {
262
+ anatomy,
221
263
  description:
222
264
  'Use when a selection needs richer custom content than a Selector row. One component: it owns field, trigger, popover, focus restore, and changeAction; the render prop owns the selector-specific accessible structure.',
223
265
  bestPractices: [
@@ -0,0 +1,189 @@
1
+ ---
2
+ schema_version: 1
3
+ template_version: 3
4
+ kind: component
5
+ id: component:ComplexSelector
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers: [theming]
13
+ verified_by:
14
+ [
15
+ packages/core/src/ComplexSelector/ComplexSelector.test.tsx,
16
+ packages/core/src/Field/Field.test.tsx,
17
+ packages/core/src/Icon/Icon.test.tsx,
18
+ scripts/check-knowledge.mjs,
19
+ ]
20
+ families: [family:overlay-dismissal]
21
+ design_specs: []
22
+ architecture:
23
+ [architecture:component-theming-surface, architecture:layer-runtime]
24
+ contributing: []
25
+ system_specs: []
26
+ ---
27
+
28
+ # ComplexSelector component contract
29
+
30
+ ## Intent
31
+
32
+ ComplexSelector renders a field-owned shell, an owned trigger, and a dialog
33
+ popup for caller-provided rich selection content. This draft records current
34
+ consumer anatomy and theming ownership without changing runtime behavior,
35
+ styling, targets, or public API.
36
+
37
+ ## Compatibility and migration
38
+
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: unchanged
43
+ - Migration decision: none
44
+
45
+ Consumer migration instructions belong in consumer docs and release notes.
46
+
47
+ ## Ownership boundary
48
+
49
+ **Owns**
50
+
51
+ - The trigger and its current `complex-selector` target.
52
+ - The trailing indicator icon and its current
53
+ `complex-selector-indicator-icon` target.
54
+ - The painted popup surface and its current `complex-selector-popup` target.
55
+
56
+ **Does not own / non-goals**
57
+
58
+ - Field-shell presentation — rendered by Field and themed through Field's
59
+ current `field` target.
60
+ - General Icon presentation — rendered by Icon for semantic icon names and icon
61
+ component types and themed through Icon's current `icon` target.
62
+ - Arbitrary ReactNode content supplied through `startIcon` — rendered directly
63
+ and owned by the product callsite.
64
+ - The selector-specific structure supplied through `children` — owned by the
65
+ product callsite.
66
+ - Shared layer lifecycle, positioning, and dismissal behavior.
67
+
68
+ ## Public concepts
69
+
70
+ No new public concept is introduced. Consumer props and usage remain documented
71
+ in `ComplexSelector.doc.mjs`.
72
+
73
+ ## Behavioral and layout contract
74
+
75
+ | ID | Candidate invariant | Basis | Draft review state |
76
+ | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------- |
77
+ | FR1 | The current render contains a Field, Trigger, Indicator icon, and mounted Popup; one optional start slot may contain either an Icon-rendered start icon or caller-rendered start content. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
78
+ | FR2 | Trigger, Indicator icon, and Popup carry `complex-selector`, `complex-selector-indicator-icon`, and `complex-selector-popup`, respectively. | Current source, docs, and tests | Verified current behavior; no target change |
79
+ | FR3 | Field delegates to Field's `field` target, and a semantic name or icon component in the start slot delegates to Icon's `icon` target. | Current source and owner tests | Verified current behavior; no target change |
80
+ | FR4 | Arbitrary ReactNode start content renders directly and carries no ComplexSelector-owned target. | Current source | Verified current behavior; no target change |
81
+
82
+ ### Allowed variation
83
+
84
+ - Caller-provided popup content may vary without becoming a new
85
+ ComplexSelector-owned anatomy part or target.
86
+ - The optional start slot has two factual branches: semantic names and icon
87
+ component types render through Icon, while arbitrary ReactNode content renders
88
+ directly under caller ownership.
89
+
90
+ ### Representative states
91
+
92
+ - Input and ghost variants use the same six-part anatomy and current targets.
93
+ - The start slot is absent when `startIcon` is omitted. When present, exactly
94
+ one of Icon-rendered start icon or Caller-rendered start content applies.
95
+ - The Popup remains mounted in both states: it is hidden while closed and shown
96
+ while open. The Indicator icon remains present and reflects collapsed or
97
+ expanded state on its current target.
98
+
99
+ ### Transformation and precedence order
100
+
101
+ - No new value, open-state, positioning, or style precedence rule is introduced.
102
+
103
+ ### Performance and resources
104
+
105
+ - No new performance or resource rule is introduced.
106
+
107
+ ## Accessibility contract
108
+
109
+ This draft does not change or extend ComplexSelector's existing field, trigger,
110
+ dialog, focus, or keyboard behavior.
111
+
112
+ ## Design relationships
113
+
114
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
115
+ | ----------------------------- | -------------------------------------------------------------------------------- | ------------------------------ | ----------------- | ------------------ |
116
+ | Field | Provides the field shell around the owned trigger. | Field component | Supporting | FR1, FR3 |
117
+ | Trigger | Displays the current value or placeholder and opens the popup. | Current source and public docs | Prominent | FR1, FR2 |
118
+ | Icon-rendered start icon | Optionally presents a semantic or component icon through Icon. | Icon component | Supporting | FR1, FR3 |
119
+ | Caller-rendered start content | Optionally presents arbitrary React content directly in the start slot. | Caller-supplied content | Context-dependent | FR1, FR4 |
120
+ | Indicator icon | Presents the trailing disclosure glyph and reflects collapsed or expanded state. | Current source and public docs | Supporting | FR1, FR2 |
121
+ | Popup | Keeps the dialog surface mounted, hidden while closed and painted while open. | Current source and public docs | Prominent | FR1, FR2 |
122
+
123
+ The Field and Icon delegations preserve their existing owners. Arbitrary
124
+ ReactNode start content remains caller-owned and receives no local target. The
125
+ local Indicator icon target remains distinct because it carries the selector's
126
+ expanded/collapsed state and rotation on the glyph itself. Caller-provided popup
127
+ content remains outside the owned anatomy inventory.
128
+
129
+ ### Theming anatomy
130
+
131
+ <!-- anatomy-theming:v1 -->
132
+
133
+ ```json
134
+ {
135
+ "Field": {"delegatesTo": {"owner": "component:Field", "target": "field"}},
136
+ "Trigger": {"target": "complex-selector"},
137
+ "Icon-rendered start icon": {
138
+ "delegatesTo": {"owner": "component:Icon", "target": "icon"}
139
+ },
140
+ "Caller-rendered start content": {
141
+ "none": {
142
+ "reason": "intentional: Arbitrary ReactNode content is caller-owned and receives no ComplexSelector target."
143
+ }
144
+ },
145
+ "Indicator icon": {"target": "complex-selector-indicator-icon"},
146
+ "Popup": {"target": "complex-selector-popup"}
147
+ }
148
+ ```
149
+
150
+ ## Family and system relationships
151
+
152
+ - `architecture:component-theming-surface` owns anatomy qualification, local
153
+ target mapping, and delegation rules.
154
+ - `architecture:layer-runtime` owns the current `usePopover` host, positioning,
155
+ native light-dismiss, and visibility reconciliation used by the Popup.
156
+ - `family:overlay-dismissal` owns shared Escape and platform-close ordering;
157
+ ComplexSelector participates through its composed Popover owner.
158
+ - Field and Icon retain ownership of their delegated targets and presentation.
159
+
160
+ ## Verification map
161
+
162
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
163
+ | ------------------- | -------------------------------------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------- |
164
+ | FR1 | `ComplexSelector.test.tsx` render, ghost-trigger, and popup suites | Default closed, ghost start branches, open Popup | Removing a documented part fails existing role, content, or structure assertions. | `audit:ComplexSelector/anatomy` |
165
+ | FR2 | Component target suites, source inspection, and theming target inventories | Closed/open Popup; expanded Indicator icon | Removing or renaming a current local target fails component assertions or target inventories. | `audit:ComplexSelector/theming` |
166
+ | FR3, FR4 | Icon owner tests and `renderIconSlot` source inspection | Semantic/component icon; arbitrary ReactNode | A branch gains the wrong owner, loses its target, or receives an invented local target. | `audit:ComplexSelector/theming` |
167
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current local targets | Missing, extra, prefixed, stale, or multiply assigned mappings fail repository validation. | `audit:ComplexSelector/theming` |
168
+
169
+ Current component tests directly assert the Trigger and Popup targets. Source
170
+ inspection confirms that non-lazy `useLayer` keeps the Popup mounted while
171
+ closed. The Indicator icon target is covered by source inspection and shared
172
+ target inventories rather than a dedicated component assertion. `renderIconSlot`
173
+ and Icon owner tests distinguish the Icon-rendered branch from arbitrary
174
+ caller-owned ReactNode content.
175
+
176
+ ## Decision log
177
+
178
+ None. This draft records current facts and introduces no component-local design,
179
+ API, theming, or layer-system decision.
180
+
181
+ ## Open questions
182
+
183
+ None.
184
+
185
+ ## Content boundary
186
+
187
+ This file does not duplicate consumer prop tables, examples, caller-provided
188
+ popup structure, implementation steps, or shared layer and theming rules. It
189
+ links to their owners.
@@ -1,4 +1,50 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
4
+ const anatomy = [
5
+ {
6
+ name: 'Trigger area',
7
+ required: true,
8
+ description:
9
+ 'Caller-provided region that accepts right-click, keyboard context-menu, and long-press input.',
10
+ },
11
+ {
12
+ name: 'Pointer menu surface',
13
+ required: false,
14
+ description:
15
+ 'Cursor-positioned menu panel used by the pointer presentation.',
16
+ },
17
+ {
18
+ name: 'Pointer action row',
19
+ required: false,
20
+ description:
21
+ 'DropdownMenu-owned action, selectable option, or submenu row in the pointer presentation.',
22
+ },
23
+ {
24
+ name: 'Touch sheet frame',
25
+ required: false,
26
+ description:
27
+ 'BottomSheet panel, content area, handle, and optional scrim that host touch actions.',
28
+ },
29
+ {
30
+ name: 'Touch menu surface',
31
+ required: false,
32
+ description:
33
+ 'ContextMenu-owned content panel rendered inside the touch sheet frame.',
34
+ },
35
+ {
36
+ name: 'Touch action list',
37
+ required: false,
38
+ description: 'Spacious List that groups data-driven touch actions.',
39
+ },
40
+ {
41
+ name: 'Touch action row',
42
+ required: false,
43
+ description:
44
+ 'ListItem button used for a data-driven action or drill-in entry in the touch presentation.',
45
+ },
46
+ ];
47
+
2
48
  /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
3
49
 
4
50
  export const docs = {
@@ -80,6 +126,7 @@ export const docs = {
80
126
  {name: 'ContextMenuItem'},
81
127
  ],
82
128
  usage: {
129
+ anatomy,
83
130
  description: 'A right-click context menu that appears at the cursor position. Use to provide contextual actions for specific elements or regions without cluttering the UI with visible buttons.',
84
131
  bestPractices: [
85
132
  { guidance: true, description: 'Keep menu items concise and action-oriented; users expect quick access to contextual actions.' },
@@ -128,6 +175,7 @@ export const docsZh = {
128
175
  export const docsDense = {
129
176
  description: 'right-click context menu at cursor position',
130
177
  usage: {
178
+ anatomy,
131
179
  description: 'A right-click context menu that appears at the cursor position. Use to provide contextual actions for specific elements or regions.',
132
180
  bestPractices: [
133
181
  { guidance: true, description: 'Keep items concise and action-oriented.' },