@astryxdesign/lab 0.7.6-canary.f73623d → 0.7.6-canary.f77aaf2

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 (33) hide show
  1. package/dist/CommentPin/CommentPin.js +1 -1
  2. package/dist/Comments/CommentComposer.d.ts.map +1 -1
  3. package/dist/Comments/CommentComposer.js +12 -4
  4. package/dist/MobilePowerSearch/MobilePowerSearch.d.ts +3 -6
  5. package/dist/MobilePowerSearch/MobilePowerSearch.d.ts.map +1 -1
  6. package/dist/MobilePowerSearch/MobilePowerSearch.js +12 -76
  7. package/dist/MobileTokenizer/MobileTokenizer.d.ts +10 -7
  8. package/dist/MobileTokenizer/MobileTokenizer.d.ts.map +1 -1
  9. package/dist/MobileTokenizer/MobileTokenizer.js +191 -114
  10. package/dist/internal/MobileSheetConfirmation.d.ts +12 -0
  11. package/dist/internal/MobileSheetConfirmation.d.ts.map +1 -0
  12. package/dist/internal/MobileSheetConfirmation.js +88 -0
  13. package/dist/internal/useDismissSearchOnTouchDrag.d.ts +21 -0
  14. package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
  15. package/dist/internal/useDismissSearchOnTouchDrag.js +43 -0
  16. package/package.json +4 -4
  17. package/src/CommentPin/CommentPin.tsx +1 -1
  18. package/src/Comments/CommentComposer.test.tsx +56 -0
  19. package/src/Comments/CommentComposer.tsx +18 -13
  20. package/src/MobilePowerSearch/MobilePowerSearch.doc.mjs +1 -1
  21. package/src/MobilePowerSearch/MobilePowerSearch.test.tsx +7 -2
  22. package/src/MobilePowerSearch/MobilePowerSearch.tsx +16 -83
  23. package/src/MobileTokenizer/MobileTokenizer.doc.mjs +3 -3
  24. package/src/MobileTokenizer/MobileTokenizer.test.tsx +287 -31
  25. package/src/MobileTokenizer/MobileTokenizer.tsx +242 -137
  26. package/src/Schedule/__tests__/ScheduleTimeDirection.a11y.chromium.spec.ts +4 -0
  27. package/src/TransferList/TransferList.doc.mjs +1 -1
  28. package/src/internal/MobileSheetConfirmation.test.tsx +68 -0
  29. package/src/internal/MobileSheetConfirmation.tsx +97 -0
  30. package/src/internal/useDismissSearchOnTouchDrag.test.tsx +94 -0
  31. package/src/internal/useDismissSearchOnTouchDrag.ts +57 -0
  32. package/src/CommentPin/CommentPin.spec.md +0 -319
  33. package/src/Schedule/Schedule.spec.md +0 -590
@@ -1,319 +0,0 @@
1
- ---
2
- schema_version: 3
3
- template_version: 7
4
- kind: component
5
- id: component:CommentPin
6
- authority: draft
7
- archive_reason: null
8
- superseded_by: null
9
- approved_by: null
10
- approved_at: null
11
- owners: [cixzhang, imdreamrunner]
12
- review_triggers:
13
- [
14
- public-api,
15
- behavior,
16
- layout,
17
- theming,
18
- accessibility,
19
- interaction,
20
- testing,
21
- visual,
22
- ]
23
- verified_by:
24
- [
25
- packages/lab/src/CommentPin/CommentPin.test.tsx,
26
- apps/storybook/stories/CommentPin.stories.tsx,
27
- packages/lab/blocks/CommentPinShowcase.tsx,
28
- scripts/check-knowledge.mjs,
29
- ]
30
- modules: []
31
- families: []
32
- design_specs: []
33
- architecture:
34
- [
35
- architecture:component-test-sufficiency,
36
- architecture:component-theming-surface,
37
- architecture:interaction-modality,
38
- architecture:public-component-api,
39
- architecture:theme-tokens,
40
- ]
41
- contributing: []
42
- system_specs: [spec:AST-002]
43
- ---
44
-
45
- # CommentPin component contract
46
-
47
- ## Contract at a glance
48
-
49
- | Area | Contract |
50
- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
51
- | Public contract | `@astryxdesign/lab` exports one `CommentPin` button, its props, and its participant data type. |
52
- | Behavior | A marker renders the first participant for one thread. A marker representing multiple threads renders its distinct participants as an AvatarGroup of at most three circles (two Avatars plus an ellipsis overflow beyond three) and the total thread count. Whenever any represented thread is unread, an accent status dot follows the total, or the participant on a single marker. A resolved marker renders nothing unless it is selected. Three sizes follow the element-size scale with equal padding on every side. |
53
- | End-user impact | Annotation anchors identify people rather than positions in an ordinal sequence. Grouped anchors keep a bounded set of participant identities while exposing the number of threads they open, unread activity stays visible as a dot, and resolved discussion leaves the content. |
54
- | Builder impact | Callers provide every distinct participant in order, the total and unread thread counts, an accessible action label, activation behavior, an optional size, and controlled selected/dimmed/resolved/disabled state. |
55
- | Compatibility/readiness | Additive experimental Lab component. Its API and visual treatment remain draft until product use and visual evidence settle them. |
56
- | Review checks | Reject ordinal identity, pin-owned clustering or coordinates, center-anchored placement, interactive descendant avatars, more than three participant circles, a numeric overflow beside the count, unequal padding, an unread number beside or instead of the total, an unselected resolved marker that still renders, generated application copy, hidden thread-count semantics, dimming that disables sibling selection, or selected/resolved paint that obscures participant identity. |
57
- | Governing rules | `architecture:public-component-api`, `architecture:component-theming-surface`, `architecture:interaction-modality`, `architecture:component-test-sufficiency`, and `spec:AST-002`. |
58
-
59
- This table is a review projection; the body below is authoritative.
60
-
61
- ## Intent
62
-
63
- CommentPin is the compact interactive surface for opening one anchored
64
- annotation thread or a caller-defined group of threads. It preserves a human
65
- identity cue at the anchor while remaining usable over content or in a content
66
- margin.
67
-
68
- ## Compatibility and migration
69
-
70
- - Released default preserved: `not yet released`
71
- - Compatibility class: additive experimental Lab API
72
- - Controlled/uncontrolled behavior: selected, dimmed, resolved, and disabled
73
- states and the unread count are caller-controlled; the component owns no
74
- selection or read state
75
- - Migration decision: none; callers replace bespoke annotation-pin visuals after
76
- the Lab contract is accepted
77
-
78
- Consumer migration instructions belong in consumer docs and release notes.
79
-
80
- ## Ownership boundary
81
-
82
- **Owns**
83
-
84
- - One button surface, its single/grouped visual transformation, its three
85
- sizes, and its selected, dimmed, resolved, disabled, hover, press, and
86
- focus-visible presentation.
87
- - Rendering ordered participant data through `Avatar`, or for a group through
88
- `AvatarGroup` with a non-interactive `AvatarGroupOverflow`, without making
89
- descendants interactive.
90
- - Showing the total represented thread count when that total is greater than
91
- one, then an accent `StatusDot` whenever the unread count is above zero.
92
- - Rendering nothing for a resolved marker that is not selected.
93
- - The stable `comment-pin` theming target and state reflection.
94
-
95
- **Does not own / non-goals**
96
-
97
- - Thread data, unread tracking, authorization, loading, transport, selection
98
- state, or the panel opened by activation — owned by the product callsite.
99
- - Clustering, participant ordering, anchor identity, coordinates, collision
100
- handling, image transforms, margin layout, or connector lines — owned by the
101
- product callsite.
102
- - Avatar loading/fallback, shape, and identity paint — delegated to
103
- `component:Avatar`; overflow indicator paint — delegated to
104
- `component:AvatarGroup`.
105
- - Ordinal numbering of annotations. Thread count is a quantity for a grouped
106
- marker, never the marker's identity.
107
-
108
- ## Public concepts
109
-
110
- Consumer prop syntax and examples remain in `CommentPin.doc.mjs`.
111
-
112
- | Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
113
- | ------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------ | ----------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------- |
114
- | participants | non-empty ordered participant tuple | every distinct identity in the thread or group, lead first; groups display at most three circles | every pin | required non-empty tuple | product supplies; component renders | experimental Lab | TypeScript rejects an empty tuple; untyped empty input falls back to a decorative anonymous Avatar and warns in development |
115
- | size | `sm`, `md`, `lg` | pin height on the element-size scale (28, 32, 36px) with 22, 24, and 28px participant circles | every pin; a standalone axis, not enrolled in `architecture:component-size-cascade` | `sm` | product | experimental Lab | an unknown runtime value resolves to `sm` |
116
- | represented thread total | positive integer | number of threads opened by the marker; values above one select grouped presentation | every marker | `1` | product | experimental Lab | non-finite, fractional, or values below one resolve to one and warn in development |
117
- | unread thread total | non-negative integer no greater than the total | number of represented threads with unread activity; above zero, an accent dot follows the total | every marker | `0` | product | experimental Lab | non-finite, fractional, or negative values are ignored; values above the total are capped; both warn in development |
118
- | selected state | selected, not selected | whether this pin is the active annotation target | enabled and disabled | not selected | product | experimental Lab | closed boolean |
119
- | dimmed state | dimmed, not dimmed | whether another pin is selected; lowers prominence without disabling activation | non-selected pins | not dimmed | product | experimental Lab | closed boolean; selected paint wins when both are true |
120
- | resolved state | resolved, unresolved | whether the represented thread set is resolved; an unselected resolved marker renders nothing | every marker | unresolved | product | experimental Lab | closed boolean |
121
- | disabled state | disabled, enabled | whether the marker can activate | every marker | enabled | product | experimental Lab | closed boolean |
122
- | accessible action name | non-empty caller-authored string | names the action and supplies the marker tooltip without generating application-specific copy | every marker | required | product | experimental Lab | TypeScript requires a string; blank content remains a caller error |
123
- | DOM extension | supported button `BaseProps`, styling, and ref | positions the marker and wires product-owned interaction without giving the component coordinates | every marker | no additional inputs | `architecture:public-component-api` | experimental Lab | unsupported native props remain unavailable |
124
-
125
- ## Behavioral and layout contract
126
-
127
- Draft requirements identify their basis so observed code is not mistaken for an
128
- intentional decision. A `current` contract contains no unresolved rows.
129
-
130
- | ID | Candidate invariant | Basis | Draft review state |
131
- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------- |
132
- | FR1 | CommentPin MUST render one button and MUST NOT accept, derive, or mutate anchor coordinates, grouping membership, thread selection, or collision layout. | product ownership boundary | product direction; pending owner approval |
133
- | FR2 | With one represented thread, the marker MUST render only the first participant Avatar and MUST NOT use an ordinal number as identity. | product direction | product direction; pending owner approval |
134
- | FR3 | With more than one represented thread, the marker MUST render its distinct participants as an AvatarGroup of at most three circles — every participant when three or fewer are distinct, otherwise the first two followed by a non-interactive AvatarGroupOverflow circle that shows an ellipsis and no number — followed by the exact represented thread total unless FR10 applies. | product direction | product direction; pending owner approval |
135
- | FR4 | Participant Avatars and the overflow circle MUST use their core components' fallback and paint, MUST suppress tooltips and interaction, and MUST remain decorative descendants of the marker button. | Avatar composition and valid interaction tree | candidate implementation |
136
- | FR5 | `isSelected`, `isDimmed`, `isResolved`, and `isDisabled` MUST remain independent caller-controlled states. Selected presentation MUST draw a solid 2px accent outline outside the pin as a box-shadow ring over a transparent border, with no accent fill and no layout shift, MUST preserve the resting medium elevation and Avatar legibility, MUST remain distinct from the shared focus outline positioned farther outside, MUST retain a visible forced-colors fallback, and MUST win over dimming; dimmed presentation MUST lower non-selected pins' prominence without disabling activation; an unselected resolved marker MUST render nothing, and a selected resolved marker MUST take the selected presentation. | migration use cases and visual direction | candidate implementation; rendered visual review pending |
137
- | FR6 | The marker root MUST combine `xstyle`, `className`, and `style`, forward neutral button DOM/event props and its ref, and keep component-owned type, label, selected semantics, disabled state, target, and reflected states authoritative. | public component API architecture | candidate implementation |
138
- | FR7 | Hover paint MUST be guarded by hover capability, touch press feedback MUST use the shared press model, and keyboard focus MUST use the shared focus-visible treatment. | interaction-modality architecture | candidate implementation |
139
- | FR8 | The root MUST keep a speech-bubble silhouette with a fixed 2px logical bottom-start corner and fully round logical start-start, start-end, and end-end corners of half the pin's height in every visual state, in LTR and RTL. The 2px corner MUST NOT follow a theme radius override, and no corner MAY be large enough to make the browser scale the corners to fit (the CSS corner-overlap rule), which would erase the 2px corner. | product visual direction and logical-layout requirement | candidate implementation; rendered visual review pending |
140
- | FR9 | The root's transform origin and documented absolute-placement convention MUST use the logical bottom-start corner. A host maps its anchor point to that corner by translating the pin upward by its full height and MUST NOT center the pin over the point. | product placement direction | candidate implementation; rendered placement review pending |
141
- | FR10 | When `unreadCount` resolves above zero, the marker MUST show an accent `StatusDot` after the thread total, or after the participant on a single marker, and MUST NOT show the unread number; the dot MUST keep a visible forced-colors treatment and MUST remain decorative. | product direction | product direction; rendered visual review pending |
142
- | FR11 | `size` MUST set the root height and minimum width to `--size-element-sm`, `--size-element-md`, or `--size-element-lg` and draw 22, 24, or 28px participant circles, and the reflected size MUST match. Padding MUST be equal on every side and equal to the inset that centers one participant circle in that height; a grouped marker MUST sit its first AvatarGroup ring inside the start padding, so participant circles keep that inset from the start edge. The trailing total or unread dot MUST add `--spacing-1` of end padding, so neither crowds the edge. | product visual direction | product direction; rendered visual review pending |
143
-
144
- ### Allowed variation
145
-
146
- - **AV1 — Participant identity.** Names and image sources vary; Avatar owns
147
- image loading, fallback initials, and anonymous fallback.
148
- - **AV2 — Product placement.** Callers position the root over content or in a
149
- content margin through layout composition and supported styling inputs. An
150
- absolute anchor coordinate maps to the logical bottom-start corner rather
151
- than the pin's center.
152
- - **AV3 — Thread grouping.** Callers may use any clustering policy and
153
- participant ordering while the marker's visual transformation remains fixed.
154
- - **AV4 — Theme output.** Themes may repaint the admitted root target and its
155
- reflected states without changing behavior or descendant Avatar ownership.
156
-
157
- ### Representative states
158
-
159
- | State | Required invariant | Allowed variation |
160
- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
161
- | Single | first participant Avatar; no visible count; an accent dot after it when unread | identity, selected/dimmed/resolved/disabled state, placement |
162
- | Grouped, one identity | one Avatar plus total thread count | count and states |
163
- | Grouped, two or three | every distinct participant Avatar plus total thread count | ordered identities, count, and states |
164
- | Grouped, more than three | first two distinct Avatars, an ellipsis overflow circle, and total thread count | ordered identities and states |
165
- | Unread | accent dot after the total, or after the participant on a single marker | other states |
166
- | Sizes | 28, 32, or 36px tall with 22, 24, or 28px circles and the same inset on every side | every other state |
167
- | Selected | active accent treatment preserves Avatar and count legibility and wins over dimming | theme paint |
168
- | Dimmed | lower opacity identifies a non-selected sibling without disabling activation | disabled may also apply |
169
- | Resolved | renders nothing unless selected; selected, it takes the selected treatment | disabled may also apply |
170
- | Disabled | an aria-disabled button does not activate but keeps its tab stop, so keyboard and screen-reader users reach it and hear it is unavailable; an unselected disabled pin paints GrayText color and border under forced colors | selected representation remains visible, keeping its Highlight treatment |
171
-
172
- ### Transformation and precedence order
173
-
174
- - **ORD1 — Count normalization.** Validate `threadCount`; an invalid value
175
- resolves to one, then a value above one selects grouped presentation.
176
- Validate `unreadCount` next; an invalid value is ignored and a value above
177
- the resolved total is capped to it.
178
- - **ORD2 — Participants.** Preserve caller order and remove duplicate IDs. A
179
- single marker takes the first participant; a grouped marker shows every
180
- distinct participant when three or fewer remain, otherwise the first two plus
181
- an ellipsis overflow circle.
182
- - **ORD3 — Count display.** A grouped marker shows the total; a resolved unread
183
- count above zero adds an accent dot after it, or after the participant on a
184
- single marker.
185
- - **ORD4 — Paint.** An unselected resolved marker renders nothing. Otherwise
186
- base surface, dimmed treatment, selected treatment, then disabled treatment
187
- compose in that order. Selected identity remains fully prominent, and
188
- disabled interaction always wins.
189
-
190
- ### Performance and resources
191
-
192
- - **PR1 — Bounded anatomy.** Rendering work is bounded to at most three
193
- participant circles (Avatars plus overflow indicator), one count element,
194
- and one unread dot regardless of the number of represented threads or
195
- supplied participants.
196
- - **PR2 — Shared interaction resources.** Touch feedback uses the shared
197
- document-level press controller and adds no marker-local listener, observer,
198
- timer, or state.
199
-
200
- Current measurements belong in the audit record; this subsection owns only
201
- durable constraints and their verification target.
202
-
203
- ## Accessibility contract
204
-
205
- - **AR1 — Action.** The root is one native `button type="button"` named by the
206
- required `label`; `isDisabled` exposes `aria-disabled="true"` and blocks every
207
- activation path while the pin keeps its tab stop (`spec:AST-085`).
208
- - **AR2 — Selection.** `isSelected` is exposed through `aria-pressed`, one solid
209
- 2px accent outline outside the pin, and a forced-colors Highlight treatment;
210
- the outline hugs the pin while the shared focus-visible outline sits farther
211
- outside, so the two states remain distinguishable.
212
- Hosts clear the selection when the selected pin is activated again.
213
- - **AR3 — Decorative descendants.** Participant Avatars, the overflow circle,
214
- the visible thread total, and the unread dot are hidden from assistive
215
- technology because the caller-authored button label owns the complete action
216
- name.
217
- - **AR4 — Tooltip.** The action label is available visually on pointer hover and
218
- keyboard focus; activation remains the primary touch behavior.
219
- - **AR5 — State naming.** Callers include resolved, grouped, or unread context
220
- in `label` when it changes the action's meaning; the component does not mint
221
- application-specific or locale-sensitive copy.
222
- - **AR6 — Dimmed siblings.** `isDimmed` changes presentation only. It MUST NOT
223
- set disabled or aria-disabled because a non-selected pin remains an available
224
- target for switching selection.
225
-
226
- ## Design relationships
227
-
228
- | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
229
- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------- | ------------------ |
230
- | Marker surface | Compact floating surface distinguishes the action from underlying content | candidate implementation pending visual review | prominent anchor | FR1, FR5, FR7 |
231
- | Representative avatars | Human identity remains the primary recognition cue; groups cap at three circles | human-selected product direction | primary content | FR2–FR4 |
232
- | Overflow indicator | An ellipsis circle summarizes participants beyond the first two without a number, so it never competes with the thread count | human-selected product direction | supporting content | FR3, FR4, AR3 |
233
- | Group count | Count communicates quantity without becoming ordinal identity | human-selected product direction | supporting content | FR3, AR3 |
234
- | Unread dot | An accent dot after the total marks unread activity without a second number | human-selected product direction | supporting state signal | FR10, AR3 |
235
- | Selected/dimmed/resolved states | Selected draws one solid 2px accent outline outside the pin with no fill, distinct from the farther-out focus outline; non-selected siblings dim without disabling; resolved disappears unless selected | human-selected product direction | supporting state signal | FR5, AR2, AR6 |
236
- | Speech-bubble corner | Logical bottom-start stays fixed at 2px and mirrors in RTL; the other three logical corners stay fully rounded | human-selected product direction | silhouette cue | FR8 |
237
- | Placement origin | Absolute host coordinates map to logical bottom-start, never the center | human-selected product direction | spatial anchor | FR9 |
238
- | Sizes | Three heights on the element-size scale; participant circles, overflow, and count keep one inset from every edge | human-selected product direction | density | FR11 |
239
-
240
- ### Design decisions
241
-
242
- <!-- design-decisions:v1 -->
243
-
244
- | ID | Decision | Intent or reason | Applies to | Allowed variation |
245
- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | ----------------------------------------------------------------------------------------------- |
246
- | DD1 | Participant Avatars, not ordinal numbers, identify comment pins. | Ordinal identity becomes hard to scan and manage as annotation volume grows. | single and grouped markers | identity data and Avatar fallback may vary |
247
- | DD2 | A grouped marker shows at most three participant circles — two Avatars plus an ellipsis overflow circle when more than three people participate — and separately shows the thread total. | Identity and quantity answer different questions; capping the facepile keeps grouped pins compact and scannable as participation grows, and an ellipsis keeps the total the pin's only number. | grouped markers | caller chooses participant order and grouping policy; themes choose the ellipsis glyph |
248
- | DD3 | Selected paint is one solid 2px accent outline outside the pin, drawn as a box-shadow ring over a transparent border; it preserves the pin's medium elevation and adds no accent fill; the shared focus outline sits farther outside. | A single 2px outline reads as selection rather than input chrome, shifts no layout, preserves the Avatar area, and remains distinct from keyboard focus. | selected and selected-plus-focused states | theme accent and elevation tokens may vary; the outline width, placement, and separation do not |
249
- | DD4 | The logical bottom-start corner is a fixed 2px logical longhand; the other three logical corners are half the pin's height. | A theme may enlarge `--radius-inner`, so a literal keeps the speech-bubble cue exactly 2px. Half-height corners are fully round without the 9999px full-radius token, which makes the browser scale all four corners to fit and erases the small one. Explicit logical longhands avoid shorthand cascade collisions and mirror in RTL by construction. | every pin and visual state | the three rounded corners follow the element-size tokens; the 2px anchor corner does not vary |
250
- | DD5 | Absolute anchor coordinates map to the pin's logical bottom-start corner, not its center. | The speech-bubble corner stays attached to the annotated point while the body grows toward block-start and inline-end. | pins positioned over content or in margins | host coordinates and transforms may vary; the anchor corner does not |
251
- | DD6 | When a pin is selected, sibling pins use controlled dimming and remain enabled. | The active target gains visual focus without preventing a viewer from switching directly to another pin. | sets of pins with one active target | host chooses which siblings belong to the focused set; selected always wins over dimming |
252
- | DD7 | Unread threads add an accent `StatusDot` after the thread total, or after the participant on a single marker; the unread number is not shown. | A dot keeps unread activity visible on every pin without a second number that competes with the total. | single and grouped markers | theme accent may vary; the dot's place after the total does not |
253
- | DD8 | Sizes `sm`, `md`, and `lg` are 28, 32, and 36px tall on the element-size tokens with 22, 24, and 28px participant circles, padded equally on every side; a grouped marker's first ring sits in the start padding, and the trailing total or dot adds `--spacing-1` of end padding. | Hosts match the pin to the density of what it annotates; one inset on every edge keeps identity and count centered and gives single and grouped pins the same edge rhythm. | every pin | themes may change the element-size tokens; the circle diameters and the equal inset do not vary |
254
- | DD9 | A resolved marker renders nothing unless it is selected. | Resolved discussion leaves the content; a selected marker stays so a thread the reader opened, or resolved from its popover, keeps its anchor until the host clears the selection. | resolved markers | the host decides which threads are resolved and which is selected |
255
-
256
- ### Theming anatomy
257
-
258
- <!-- anatomy-theming:v1 -->
259
-
260
- ```json
261
- {
262
- "Marker surface": {"target": "comment-pin"},
263
- "Representative avatars": {
264
- "delegatesTo": {"owner": "component:Avatar", "target": "avatar"}
265
- },
266
- "Overflow indicator": {
267
- "delegatesTo": {
268
- "owner": "component:AvatarGroup",
269
- "target": "avatar-group-overflow"
270
- }
271
- },
272
- "Group count": {"inherits": "comment-pin"},
273
- "Unread dot": {
274
- "delegatesTo": {"owner": "component:StatusDot", "target": "status-dot"}
275
- }
276
- }
277
- ```
278
-
279
- ## Family and system relationships
280
-
281
- - `architecture:public-component-api` owns DOM reachability, prop semantics,
282
- styling composition, handler forwarding, and React 19 refs.
283
- - `architecture:component-theming-surface` owns the one root painting target,
284
- reflected states, and delegated Avatar ownership.
285
- - `architecture:interaction-modality` owns hover capability, touch press, and
286
- focus-visible behavior.
287
- - `architecture:component-test-sufficiency` owns representative-state and
288
- mutation-sensitive regression evidence.
289
- - `architecture:theme-tokens` owns the semantic color, radius, spacing,
290
- typography, motion, and shadow vocabulary used by the baseline paint.
291
- - `spec:AST-002` owns admission of the public Lab surface and invalid-input
292
- behavior.
293
-
294
- ## Verification map
295
-
296
- | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
297
- | ---------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
298
- | FR1–FR4, FR10, FR11, PR1, AR1, AR3 | `CommentPin.test.tsx` plus the Storybook size matrix | single, grouped within three, grouped overflow, unread single and grouped, invalid unread, duplicate identities, empty participants, each size | extra anatomy, more than three circles, a numeric overflow, an unread number or a missing unread dot, ordinal identity, lost count, a size whose circles or reflected value disagree, or interactive/avatar semantics fail focused assertions | `audit:CommentPin/behavior` |
299
- | FR5, FR8, FR9, AR2, AR5, AR6 | focused tests plus Storybook state matrix, placement example, and docsite showcase | selected, selected+focused, dimmed, forced colors, resolved hidden and resolved selected, disabled, LTR/RTL silhouette and placement | missing state reflection, disabled dimmed siblings, merged selection/focus cues, or centered/non-mirroring placement fails semantic/source assertions or rendered review | `audit:CommentPin/states` |
300
- | FR6 | focused passthrough, ref, stable-target, and state-reflection tests | custom DOM/style inputs and every state | dropped inputs/ref or overwritten component-owned semantics fail focused assertions | `audit:CommentPin/api-theming` |
301
- | FR7, PR2, AR4 | focus, hover-capability source inspection, tooltip test, build scan | pointer, keyboard, touch model | unguarded hover, missing focus treatment, or local press machinery fails source/test/build checks | `audit:CommentPin/interaction` |
302
- | Documentation and surface | component docs, block metadata, root export, Storybook, and knowledge check | public examples and component discovery | missing or stale discoverability, syntax, boundaries, or relationships fail repository checks | `audit:CommentPin/docs` |
303
-
304
- ## Decision log
305
-
306
- None. This record remains draft; its candidate requirements and local design
307
- intent await component-owner and visual review.
308
-
309
- ## Open questions
310
-
311
- - **OQ1 — Does the selected state treatment preserve enough contrast on
312
- representative images and every theme while remaining distinct over both
313
- image content and page margins?** (`human-design`)
314
-
315
- ## Content boundary
316
-
317
- This file does not duplicate consumer prop tables/examples, current audit
318
- results, implementation steps, product grouping algorithms, coordinate systems,
319
- or family/system rules. It links to their owners.