@chromvoid/headless-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/a11y-contracts/index.d.ts +23 -0
  4. package/dist/a11y-contracts/index.js +1 -0
  5. package/dist/accordion/index.d.ts +78 -0
  6. package/dist/accordion/index.js +264 -0
  7. package/dist/adapters/index.d.ts +9 -0
  8. package/dist/adapters/index.js +1 -0
  9. package/dist/alert/index.d.ts +33 -0
  10. package/dist/alert/index.js +54 -0
  11. package/dist/alert-dialog/index.d.ts +69 -0
  12. package/dist/alert-dialog/index.js +94 -0
  13. package/dist/badge/index.d.ts +48 -0
  14. package/dist/badge/index.js +89 -0
  15. package/dist/breadcrumb/index.d.ts +55 -0
  16. package/dist/breadcrumb/index.js +77 -0
  17. package/dist/button/index.d.ts +46 -0
  18. package/dist/button/index.js +86 -0
  19. package/dist/callout/index.d.ts +41 -0
  20. package/dist/callout/index.js +63 -0
  21. package/dist/card/index.d.ts +54 -0
  22. package/dist/card/index.js +103 -0
  23. package/dist/carousel/index.d.ts +98 -0
  24. package/dist/carousel/index.js +243 -0
  25. package/dist/checkbox/index.d.ts +50 -0
  26. package/dist/checkbox/index.js +87 -0
  27. package/dist/combobox/index.d.ts +114 -0
  28. package/dist/combobox/index.js +431 -0
  29. package/dist/command-palette/index.d.ts +73 -0
  30. package/dist/command-palette/index.js +147 -0
  31. package/dist/context-menu/index.d.ts +111 -0
  32. package/dist/context-menu/index.js +372 -0
  33. package/dist/copy-button/index.d.ts +62 -0
  34. package/dist/copy-button/index.js +183 -0
  35. package/dist/core/index.d.ts +20 -0
  36. package/dist/core/index.js +2 -0
  37. package/dist/core/selection.d.ts +5 -0
  38. package/dist/core/selection.js +39 -0
  39. package/dist/core/value-range.d.ts +49 -0
  40. package/dist/core/value-range.js +134 -0
  41. package/dist/date-picker/index.d.ts +210 -0
  42. package/dist/date-picker/index.js +895 -0
  43. package/dist/dialog/index.d.ts +95 -0
  44. package/dist/dialog/index.js +153 -0
  45. package/dist/disclosure/index.d.ts +52 -0
  46. package/dist/disclosure/index.js +159 -0
  47. package/dist/drawer/index.d.ts +30 -0
  48. package/dist/drawer/index.js +39 -0
  49. package/dist/feed/index.d.ts +77 -0
  50. package/dist/feed/index.js +260 -0
  51. package/dist/grid/index.d.ts +103 -0
  52. package/dist/grid/index.js +415 -0
  53. package/dist/index.d.ts +51 -0
  54. package/dist/index.js +51 -0
  55. package/dist/input/index.d.ts +86 -0
  56. package/dist/input/index.js +156 -0
  57. package/dist/interactions/composite-navigation.d.ts +69 -0
  58. package/dist/interactions/composite-navigation.js +169 -0
  59. package/dist/interactions/index.d.ts +15 -0
  60. package/dist/interactions/index.js +4 -0
  61. package/dist/interactions/keyboard-intents.d.ts +16 -0
  62. package/dist/interactions/keyboard-intents.js +33 -0
  63. package/dist/interactions/overlay-focus.d.ts +40 -0
  64. package/dist/interactions/overlay-focus.js +93 -0
  65. package/dist/interactions/typeahead.d.ts +20 -0
  66. package/dist/interactions/typeahead.js +41 -0
  67. package/dist/landmarks/index.d.ts +39 -0
  68. package/dist/landmarks/index.js +58 -0
  69. package/dist/link/index.d.ts +34 -0
  70. package/dist/link/index.js +39 -0
  71. package/dist/listbox/index.d.ts +92 -0
  72. package/dist/listbox/index.js +337 -0
  73. package/dist/menu/index.d.ts +132 -0
  74. package/dist/menu/index.js +541 -0
  75. package/dist/menu-button/index.d.ts +71 -0
  76. package/dist/menu-button/index.js +121 -0
  77. package/dist/meter/index.d.ts +45 -0
  78. package/dist/meter/index.js +106 -0
  79. package/dist/number/index.d.ts +113 -0
  80. package/dist/number/index.js +252 -0
  81. package/dist/popover/index.d.ts +70 -0
  82. package/dist/popover/index.js +126 -0
  83. package/dist/progress/index.d.ts +49 -0
  84. package/dist/progress/index.js +79 -0
  85. package/dist/radio-group/index.d.ts +61 -0
  86. package/dist/radio-group/index.js +150 -0
  87. package/dist/select/index.d.ts +92 -0
  88. package/dist/select/index.js +239 -0
  89. package/dist/sidebar/index.d.ts +74 -0
  90. package/dist/sidebar/index.js +186 -0
  91. package/dist/slider/index.d.ts +61 -0
  92. package/dist/slider/index.js +150 -0
  93. package/dist/slider-multi-thumb/index.d.ts +70 -0
  94. package/dist/slider-multi-thumb/index.js +222 -0
  95. package/dist/spinbutton/index.d.ts +75 -0
  96. package/dist/spinbutton/index.js +214 -0
  97. package/dist/spinner/index.d.ts +1 -0
  98. package/dist/spinner/index.js +1 -0
  99. package/dist/spinner/spinner.d.ts +23 -0
  100. package/dist/spinner/spinner.js +25 -0
  101. package/dist/switch/index.d.ts +40 -0
  102. package/dist/switch/index.js +61 -0
  103. package/dist/table/index.d.ts +117 -0
  104. package/dist/table/index.js +377 -0
  105. package/dist/tabs/index.d.ts +63 -0
  106. package/dist/tabs/index.js +174 -0
  107. package/dist/textarea/index.d.ts +68 -0
  108. package/dist/textarea/index.js +137 -0
  109. package/dist/toast/index.d.ts +67 -0
  110. package/dist/toast/index.js +145 -0
  111. package/dist/toolbar/index.d.ts +59 -0
  112. package/dist/toolbar/index.js +139 -0
  113. package/dist/tooltip/index.d.ts +52 -0
  114. package/dist/tooltip/index.js +169 -0
  115. package/dist/treegrid/index.d.ts +101 -0
  116. package/dist/treegrid/index.js +463 -0
  117. package/dist/treeview/index.d.ts +68 -0
  118. package/dist/treeview/index.js +370 -0
  119. package/dist/window-splitter/index.d.ts +65 -0
  120. package/dist/window-splitter/index.js +204 -0
  121. package/package.json +92 -0
  122. package/specs/ADR-001-headless-architecture.md +461 -0
  123. package/specs/ADR-002-repo-release-model.md +108 -0
  124. package/specs/ADR-003-public-api-versioning.md +136 -0
  125. package/specs/ADR-004-focus-selection-policy.md +117 -0
  126. package/specs/IMPLEMENTATION-ROADMAP.md +237 -0
  127. package/specs/ISSUE-BACKLOG.md +681 -0
  128. package/specs/RELEASE-CANDIDATE.md +30 -0
  129. package/specs/components/accordion.md +130 -0
  130. package/specs/components/alert-dialog.md +72 -0
  131. package/specs/components/alert.md +65 -0
  132. package/specs/components/badge.md +220 -0
  133. package/specs/components/breadcrumb.md +74 -0
  134. package/specs/components/button.md +115 -0
  135. package/specs/components/callout.md +195 -0
  136. package/specs/components/card.md +280 -0
  137. package/specs/components/carousel.md +140 -0
  138. package/specs/components/checkbox.md +172 -0
  139. package/specs/components/combobox.md +423 -0
  140. package/specs/components/command-palette.md +92 -0
  141. package/specs/components/context-menu.md +556 -0
  142. package/specs/components/copy-button.md +293 -0
  143. package/specs/components/date-picker.md +400 -0
  144. package/specs/components/dialog.md +298 -0
  145. package/specs/components/disclosure.md +257 -0
  146. package/specs/components/drawer.md +353 -0
  147. package/specs/components/feed.md +265 -0
  148. package/specs/components/grid.md +186 -0
  149. package/specs/components/input.md +254 -0
  150. package/specs/components/landmarks.md +136 -0
  151. package/specs/components/link.md +134 -0
  152. package/specs/components/listbox.md +351 -0
  153. package/specs/components/menu-button.md +76 -0
  154. package/specs/components/menu.md +623 -0
  155. package/specs/components/meter.md +149 -0
  156. package/specs/components/number.md +393 -0
  157. package/specs/components/popover.md +252 -0
  158. package/specs/components/progress.md +188 -0
  159. package/specs/components/radio-group.md +151 -0
  160. package/specs/components/select.md +144 -0
  161. package/specs/components/sidebar.md +321 -0
  162. package/specs/components/slider-multi-thumb.md +78 -0
  163. package/specs/components/slider.md +84 -0
  164. package/specs/components/spinbutton.md +140 -0
  165. package/specs/components/spinner.md +132 -0
  166. package/specs/components/switch.md +175 -0
  167. package/specs/components/table.md +403 -0
  168. package/specs/components/tabs.md +265 -0
  169. package/specs/components/textarea.md +185 -0
  170. package/specs/components/toast.md +198 -0
  171. package/specs/components/toolbar.md +278 -0
  172. package/specs/components/tooltip.md +252 -0
  173. package/specs/components/treegrid.md +281 -0
  174. package/specs/components/treeview.md +91 -0
  175. package/specs/components/window-splitter.md +297 -0
  176. package/specs/ops/git-shard-sync.md +107 -0
  177. package/specs/ops/release-checklist.md +76 -0
  178. package/specs/release/GAP-TO-GREEN-ISSUES.md +88 -0
  179. package/specs/release/api-freeze-candidate.md +54 -0
  180. package/specs/release/changelog-automation.md +76 -0
  181. package/specs/release/changelog.generated.md +53 -0
  182. package/specs/release/changelog.patch.generated.md +46 -0
  183. package/specs/release/consumer-integration.md +53 -0
  184. package/specs/release/migration-notes-pre-v1.md +40 -0
  185. package/specs/release/mvp-changelog.md +57 -0
  186. package/specs/release/release-notes-template.md +61 -0
  187. package/specs/release/release-rehearsal.md +113 -0
  188. package/specs/release/semver-deprecation-dry-run.md +89 -0
  189. package/specs/release/shard-release-drill-report.md +50 -0
  190. package/specs/release/shard-release-follow-ups.md +31 -0
  191. package/specs/signals.md +208 -0
@@ -0,0 +1,208 @@
1
+ # Signals Integration Blueprint for Headless + Lit
2
+
3
+ ## Goal
4
+
5
+ Make `@chromvoid/headless-ui` reactive out of the box for UI libraries (Lit included),
6
+ without requiring manual subscriptions or imperative sync glue.
7
+
8
+ The key idea is simple:
9
+
10
+ - headless models expose **signals as first-class state**
11
+ - UI layer reads signals in render
12
+ - updates propagate automatically
13
+
14
+ ## Why this matters
15
+
16
+ If headless returns plain mutable objects, UI adapters must re-render manually.
17
+ That causes drift and duplicate wiring.
18
+
19
+ If headless returns signals/derived signals, UI becomes declarative:
20
+
21
+ - read state in template
22
+ - call actions in handlers
23
+ - done
24
+
25
+ ## Product Principle (Pre-v1)
26
+
27
+ The package currently has no external production consumers.
28
+
29
+ Therefore, pre-v1 API design should optimize for **best consumer ergonomics**,
30
+ not backward compatibility with interim internal shapes.
31
+
32
+ Rule:
33
+
34
+ - API redesign is allowed when it produces a cleaner and more consistent consumer DX.
35
+
36
+ ## Design Principles
37
+
38
+ 1. **Signal-first state**
39
+
40
+ - expose atoms/computed values, not snapshots
41
+ - never require `requestUpdate`-style manual sync in consumers
42
+
43
+ 2. **Intent-first actions**
44
+
45
+ - keyboard/pointer handlers call model actions
46
+ - model owns state transitions and APG semantics
47
+
48
+ 3. **Contract-first props**
49
+
50
+ - `get*Props` returns ARIA/role/tabindex contracts from current signal state
51
+ - no visual decisions in headless contracts
52
+
53
+ 4. **Adapter-thinness**
54
+
55
+ - Lit adapter maps state and handlers only
56
+ - no business logic in adapters
57
+
58
+ ## Recommended Public Shape per Component
59
+
60
+ Each component model should follow this structure:
61
+
62
+ ```ts
63
+ type XModel = {
64
+ state: {
65
+ // atoms/computed signals
66
+ activeId: Atom<string | null>
67
+ selectedIds: Atom<string[]>
68
+ isOpen: Atom<boolean>
69
+ }
70
+ actions: {
71
+ moveNext(): void
72
+ movePrev(): void
73
+ select(id: string): void
74
+ handleKeyDown(event: KeyboardEvent): void
75
+ }
76
+ contracts: {
77
+ getRootProps(): RootProps
78
+ getItemProps(id: string): ItemProps
79
+ }
80
+ }
81
+ ```
82
+
83
+ Important:
84
+
85
+ - `state` must be signal references
86
+ - `contracts` must derive from signals
87
+ - `actions` must be the only mutation path
88
+
89
+ ## Reatom Implementation Pattern
90
+
91
+ Use Reatom for headless model internals:
92
+
93
+ - `atom` for mutable state
94
+ - `computed` for derived state and props-ready values
95
+ - `action` for transitions
96
+
97
+ Example (simplified):
98
+
99
+ ```ts
100
+ import {atom, action, computed} from '@reatom/core'
101
+
102
+ export const createListbox = () => {
103
+ const activeId = atom<string | null>(null, 'listbox.activeId')
104
+ const selectedIds = atom<string[]>([], 'listbox.selectedIds')
105
+
106
+ const isSelectionEmpty = computed(() => selectedIds().length === 0, 'listbox.isSelectionEmpty')
107
+
108
+ const moveNext = action(() => {
109
+ // transition logic
110
+ }, 'listbox.moveNext')
111
+
112
+ const select = action((id: string) => {
113
+ selectedIds.set([id])
114
+ activeId.set(id)
115
+ }, 'listbox.select')
116
+
117
+ const getRootProps = () => ({
118
+ role: 'listbox' as const,
119
+ tabindex: '0' as const,
120
+ 'aria-activedescendant': activeId() ?? undefined,
121
+ })
122
+
123
+ return {
124
+ state: {activeId, selectedIds, isSelectionEmpty},
125
+ actions: {moveNext, select},
126
+ contracts: {getRootProps},
127
+ }
128
+ }
129
+ ```
130
+
131
+ ## Lit Consumption Pattern
132
+
133
+ For Lit-based UI library, the adapter should read signals directly in `render`.
134
+
135
+ If you use a signal-aware base element (for example an XLit-style base),
136
+ this gives automatic dependency tracking.
137
+
138
+ ```ts
139
+ class ListboxView extends XLitElement {
140
+ model = createListbox()
141
+
142
+ render() {
143
+ const rootProps = this.model.contracts.getRootProps()
144
+ return html`
145
+ <div
146
+ role=${rootProps.role}
147
+ tabindex=${rootProps.tabindex}
148
+ aria-activedescendant=${rootProps['aria-activedescendant'] ?? nothing}
149
+ @keydown=${this.model.actions.handleKeyDown}
150
+ >
151
+ Active: ${this.model.state.activeId() ?? 'none'}
152
+ </div>
153
+ `
154
+ }
155
+ }
156
+ ```
157
+
158
+ ## Packaging Strategy
159
+
160
+ Use two-layer package strategy:
161
+
162
+ 1. `@chromvoid/headless-ui`
163
+
164
+ - pure headless contracts and Reatom models
165
+ - no Lit dependency
166
+
167
+ 2. `@chromvoid/ui` (optional UI kit package)
168
+
169
+ - tiny wrappers/helpers for Lit ergonomics
170
+ - no business logic
171
+
172
+ This keeps core portable and gives Lit users first-class DX.
173
+
174
+ ## Migration Path from Current Signal Models
175
+
176
+ Current models already use Reatom signals internally.
177
+ Migration should focus on API and adapter ergonomics:
178
+
179
+ 1. keep public `state` fields explicitly typed as signal references (`Atom`/`Computed`)
180
+ 2. keep contract derivation inside model (`contracts.get*Props`) only
181
+ 3. ensure adapter-level reactive rendering coverage stays green
182
+ 4. publish migration notes for any pre-v1 contract reshaping
183
+
184
+ ## Testing Requirements for Signal Integration
185
+
186
+ For each component, add tests for:
187
+
188
+ 1. signal updates after actions
189
+ 2. computed contract consistency
190
+ 3. keyboard action -> signal transition mapping
191
+ 4. adapter-level reactive rendering behavior (Lit integration tests)
192
+
193
+ ## Anti-Patterns to Avoid
194
+
195
+ - returning only snapshot objects from `state`
196
+ - deriving ARIA props outside the model
197
+ - mixing UI logic into model actions
198
+ - exposing mutable arrays/objects without signal wrappers
199
+ - requiring manual re-render calls from component consumers
200
+
201
+ ## Practical Next Step
202
+
203
+ Start with one component migration end-to-end:
204
+
205
+ 1. redesign `listbox` to a signal-first consumer API
206
+ 2. align naming and return shape for future components
207
+ 3. add one Lit integration demo proving out-of-box reactivity
208
+ 4. use this as template for `combobox`, `menu`, `tabs`, `treeview`