@chromvoid/uikit 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 (246) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +96 -0
  3. package/dist/components/cv-accordion-item.d.ts +69 -0
  4. package/dist/components/cv-accordion-item.js +176 -0
  5. package/dist/components/cv-accordion.d.ts +79 -0
  6. package/dist/components/cv-accordion.js +310 -0
  7. package/dist/components/cv-alert-dialog.d.ts +86 -0
  8. package/dist/components/cv-alert-dialog.js +393 -0
  9. package/dist/components/cv-alert.d.ts +48 -0
  10. package/dist/components/cv-alert.js +156 -0
  11. package/dist/components/cv-badge.d.ts +56 -0
  12. package/dist/components/cv-badge.js +280 -0
  13. package/dist/components/cv-breadcrumb-item.d.ts +35 -0
  14. package/dist/components/cv-breadcrumb-item.js +64 -0
  15. package/dist/components/cv-breadcrumb.d.ts +39 -0
  16. package/dist/components/cv-breadcrumb.js +160 -0
  17. package/dist/components/cv-button.d.ts +83 -0
  18. package/dist/components/cv-button.js +541 -0
  19. package/dist/components/cv-callout.d.ts +32 -0
  20. package/dist/components/cv-callout.js +221 -0
  21. package/dist/components/cv-card.d.ts +48 -0
  22. package/dist/components/cv-card.js +269 -0
  23. package/dist/components/cv-carousel-slide.d.ts +25 -0
  24. package/dist/components/cv-carousel-slide.js +51 -0
  25. package/dist/components/cv-carousel.d.ts +96 -0
  26. package/dist/components/cv-carousel.js +457 -0
  27. package/dist/components/cv-checkbox.d.ts +84 -0
  28. package/dist/components/cv-checkbox.js +274 -0
  29. package/dist/components/cv-combobox-group.d.ts +15 -0
  30. package/dist/components/cv-combobox-group.js +34 -0
  31. package/dist/components/cv-combobox-option.d.ts +30 -0
  32. package/dist/components/cv-combobox-option.js +66 -0
  33. package/dist/components/cv-combobox.d.ts +135 -0
  34. package/dist/components/cv-combobox.js +834 -0
  35. package/dist/components/cv-command-item.d.ts +30 -0
  36. package/dist/components/cv-command-item.js +68 -0
  37. package/dist/components/cv-command-palette.d.ts +105 -0
  38. package/dist/components/cv-command-palette.js +578 -0
  39. package/dist/components/cv-context-menu.d.ts +98 -0
  40. package/dist/components/cv-context-menu.js +515 -0
  41. package/dist/components/cv-copy-button.d.ts +61 -0
  42. package/dist/components/cv-copy-button.js +318 -0
  43. package/dist/components/cv-date-picker.d.ts +161 -0
  44. package/dist/components/cv-date-picker.js +803 -0
  45. package/dist/components/cv-dialog.d.ts +89 -0
  46. package/dist/components/cv-dialog.js +459 -0
  47. package/dist/components/cv-disclosure.d.ts +57 -0
  48. package/dist/components/cv-disclosure.js +241 -0
  49. package/dist/components/cv-drawer.d.ts +102 -0
  50. package/dist/components/cv-drawer.js +595 -0
  51. package/dist/components/cv-feed-article.d.ts +26 -0
  52. package/dist/components/cv-feed-article.js +52 -0
  53. package/dist/components/cv-feed.d.ts +62 -0
  54. package/dist/components/cv-feed.js +310 -0
  55. package/dist/components/cv-grid-cell.d.ts +30 -0
  56. package/dist/components/cv-grid-cell.js +57 -0
  57. package/dist/components/cv-grid-column.d.ts +30 -0
  58. package/dist/components/cv-grid-column.js +43 -0
  59. package/dist/components/cv-grid-row.d.ts +30 -0
  60. package/dist/components/cv-grid-row.js +42 -0
  61. package/dist/components/cv-grid.d.ts +119 -0
  62. package/dist/components/cv-grid.js +567 -0
  63. package/dist/components/cv-icon.d.ts +57 -0
  64. package/dist/components/cv-icon.js +352 -0
  65. package/dist/components/cv-input.d.ts +127 -0
  66. package/dist/components/cv-input.js +482 -0
  67. package/dist/components/cv-landmark.d.ts +32 -0
  68. package/dist/components/cv-landmark.js +62 -0
  69. package/dist/components/cv-link.d.ts +22 -0
  70. package/dist/components/cv-link.js +99 -0
  71. package/dist/components/cv-listbox-group.d.ts +15 -0
  72. package/dist/components/cv-listbox-group.js +42 -0
  73. package/dist/components/cv-listbox.d.ts +81 -0
  74. package/dist/components/cv-listbox.js +388 -0
  75. package/dist/components/cv-menu-button.d.ts +118 -0
  76. package/dist/components/cv-menu-button.js +822 -0
  77. package/dist/components/cv-menu-group.d.ts +20 -0
  78. package/dist/components/cv-menu-group.js +48 -0
  79. package/dist/components/cv-menu-item.d.ts +52 -0
  80. package/dist/components/cv-menu-item.js +105 -0
  81. package/dist/components/cv-menu.d.ts +62 -0
  82. package/dist/components/cv-menu.js +414 -0
  83. package/dist/components/cv-meter.d.ts +66 -0
  84. package/dist/components/cv-meter.js +154 -0
  85. package/dist/components/cv-number.d.ts +139 -0
  86. package/dist/components/cv-number.js +553 -0
  87. package/dist/components/cv-option.d.ts +30 -0
  88. package/dist/components/cv-option.js +84 -0
  89. package/dist/components/cv-popover.d.ts +87 -0
  90. package/dist/components/cv-popover.js +373 -0
  91. package/dist/components/cv-progress-ring.d.ts +45 -0
  92. package/dist/components/cv-progress-ring.js +169 -0
  93. package/dist/components/cv-progress.d.ts +45 -0
  94. package/dist/components/cv-progress.js +148 -0
  95. package/dist/components/cv-radio-group.d.ts +79 -0
  96. package/dist/components/cv-radio-group.js +398 -0
  97. package/dist/components/cv-radio.d.ts +36 -0
  98. package/dist/components/cv-radio.js +123 -0
  99. package/dist/components/cv-select-group.d.ts +15 -0
  100. package/dist/components/cv-select-group.js +44 -0
  101. package/dist/components/cv-select-option.d.ts +30 -0
  102. package/dist/components/cv-select-option.js +66 -0
  103. package/dist/components/cv-select.d.ts +128 -0
  104. package/dist/components/cv-select.js +666 -0
  105. package/dist/components/cv-sidebar-item.d.ts +26 -0
  106. package/dist/components/cv-sidebar-item.js +142 -0
  107. package/dist/components/cv-sidebar.d.ts +171 -0
  108. package/dist/components/cv-sidebar.js +767 -0
  109. package/dist/components/cv-slider-multi-thumb.d.ts +73 -0
  110. package/dist/components/cv-slider-multi-thumb.js +374 -0
  111. package/dist/components/cv-slider.d.ts +84 -0
  112. package/dist/components/cv-slider.js +328 -0
  113. package/dist/components/cv-spinbutton.d.ts +121 -0
  114. package/dist/components/cv-spinbutton.js +486 -0
  115. package/dist/components/cv-spinner.d.ts +18 -0
  116. package/dist/components/cv-spinner.js +95 -0
  117. package/dist/components/cv-switch.d.ts +81 -0
  118. package/dist/components/cv-switch.js +285 -0
  119. package/dist/components/cv-tab-panel.d.ts +20 -0
  120. package/dist/components/cv-tab-panel.js +37 -0
  121. package/dist/components/cv-tab.d.ts +40 -0
  122. package/dist/components/cv-tab.js +132 -0
  123. package/dist/components/cv-table-cell.d.ts +31 -0
  124. package/dist/components/cv-table-cell.js +49 -0
  125. package/dist/components/cv-table-column.d.ts +37 -0
  126. package/dist/components/cv-table-column.js +63 -0
  127. package/dist/components/cv-table-row.d.ts +30 -0
  128. package/dist/components/cv-table-row.js +45 -0
  129. package/dist/components/cv-table.d.ts +147 -0
  130. package/dist/components/cv-table.js +607 -0
  131. package/dist/components/cv-tabs.d.ts +70 -0
  132. package/dist/components/cv-tabs.js +524 -0
  133. package/dist/components/cv-textarea.d.ts +108 -0
  134. package/dist/components/cv-textarea.js +328 -0
  135. package/dist/components/cv-toast-region.d.ts +39 -0
  136. package/dist/components/cv-toast-region.js +162 -0
  137. package/dist/components/cv-toast.d.ts +67 -0
  138. package/dist/components/cv-toast.js +315 -0
  139. package/dist/components/cv-toolbar-item.d.ts +25 -0
  140. package/dist/components/cv-toolbar-item.js +72 -0
  141. package/dist/components/cv-toolbar-separator.d.ts +25 -0
  142. package/dist/components/cv-toolbar-separator.js +45 -0
  143. package/dist/components/cv-toolbar.d.ts +63 -0
  144. package/dist/components/cv-toolbar.js +295 -0
  145. package/dist/components/cv-tooltip.d.ts +83 -0
  146. package/dist/components/cv-tooltip.js +455 -0
  147. package/dist/components/cv-treegrid-cell.d.ts +30 -0
  148. package/dist/components/cv-treegrid-cell.js +57 -0
  149. package/dist/components/cv-treegrid-column.d.ts +37 -0
  150. package/dist/components/cv-treegrid-column.js +53 -0
  151. package/dist/components/cv-treegrid-row.d.ts +55 -0
  152. package/dist/components/cv-treegrid-row.js +90 -0
  153. package/dist/components/cv-treegrid.d.ts +96 -0
  154. package/dist/components/cv-treegrid.js +632 -0
  155. package/dist/components/cv-treeitem.d.ts +58 -0
  156. package/dist/components/cv-treeitem.js +144 -0
  157. package/dist/components/cv-treeview.d.ts +70 -0
  158. package/dist/components/cv-treeview.js +396 -0
  159. package/dist/components/cv-window-splitter.d.ts +79 -0
  160. package/dist/components/cv-window-splitter.js +316 -0
  161. package/dist/components/index.d.ts +94 -0
  162. package/dist/components/index.js +79 -0
  163. package/dist/dialog/create-dialog-controller.d.ts +31 -0
  164. package/dist/dialog/create-dialog-controller.js +320 -0
  165. package/dist/dialog/index.d.ts +2 -0
  166. package/dist/dialog/index.js +1 -0
  167. package/dist/form-associated/FormAssociatedReatomElement.d.ts +25 -0
  168. package/dist/form-associated/FormAssociatedReatomElement.js +70 -0
  169. package/dist/form-associated/withFormAssociated.d.ts +5 -0
  170. package/dist/form-associated/withFormAssociated.js +1 -0
  171. package/dist/index.d.ts +10 -0
  172. package/dist/index.js +9 -0
  173. package/dist/reatom-lit/ReatomLitElement.d.ts +27 -0
  174. package/dist/reatom-lit/ReatomLitElement.js +118 -0
  175. package/dist/reatom-lit/html.d.ts +4 -0
  176. package/dist/reatom-lit/html.js +10 -0
  177. package/dist/reatom-lit/index.d.ts +4 -0
  178. package/dist/reatom-lit/index.js +4 -0
  179. package/dist/reatom-lit/watch.d.ts +15 -0
  180. package/dist/reatom-lit/watch.js +40 -0
  181. package/dist/reatom-lit/withReatomElement.d.ts +4 -0
  182. package/dist/reatom-lit/withReatomElement.js +57 -0
  183. package/dist/register.d.ts +1 -0
  184. package/dist/register.js +84 -0
  185. package/dist/styles/component-styles.d.ts +4 -0
  186. package/dist/styles/component-styles.js +78 -0
  187. package/dist/theme/cv-theme-provider.d.ts +32 -0
  188. package/dist/theme/cv-theme-provider.js +110 -0
  189. package/dist/theme/index.d.ts +4 -0
  190. package/dist/theme/index.js +2 -0
  191. package/dist/theme/theme-engine.d.ts +4 -0
  192. package/dist/theme/theme-engine.js +67 -0
  193. package/dist/theme/tokens.css +265 -0
  194. package/dist/theme/types.d.ts +7 -0
  195. package/dist/theme/types.js +1 -0
  196. package/dist/toast/create-toast-controller.d.ts +12 -0
  197. package/dist/toast/create-toast-controller.js +12 -0
  198. package/dist/toast/index.d.ts +2 -0
  199. package/dist/toast/index.js +1 -0
  200. package/package.json +146 -0
  201. package/specs/_template.md +110 -0
  202. package/specs/components/accordion.md +207 -0
  203. package/specs/components/alert.md +83 -0
  204. package/specs/components/badge.md +183 -0
  205. package/specs/components/breadcrumb.md +152 -0
  206. package/specs/components/button.md +227 -0
  207. package/specs/components/callout.md +153 -0
  208. package/specs/components/card.md +192 -0
  209. package/specs/components/carousel.md +232 -0
  210. package/specs/components/checkbox.md +141 -0
  211. package/specs/components/combobox.md +427 -0
  212. package/specs/components/context-menu.md +375 -0
  213. package/specs/components/copy-button.md +236 -0
  214. package/specs/components/date-picker.md +290 -0
  215. package/specs/components/dialog.md +184 -0
  216. package/specs/components/disclosure.md +151 -0
  217. package/specs/components/drawer.md +216 -0
  218. package/specs/components/feed.md +266 -0
  219. package/specs/components/grid.md +423 -0
  220. package/specs/components/input.md +237 -0
  221. package/specs/components/landmark.md +92 -0
  222. package/specs/components/link.md +117 -0
  223. package/specs/components/listbox.md +327 -0
  224. package/specs/components/menu.md +508 -0
  225. package/specs/components/meter.md +148 -0
  226. package/specs/components/number.md +268 -0
  227. package/specs/components/option.md +167 -0
  228. package/specs/components/popover.md +207 -0
  229. package/specs/components/progress-ring.md +134 -0
  230. package/specs/components/progress.md +110 -0
  231. package/specs/components/radio.md +208 -0
  232. package/specs/components/select.md +305 -0
  233. package/specs/components/sidebar.md +204 -0
  234. package/specs/components/spinbutton.md +157 -0
  235. package/specs/components/spinner.md +83 -0
  236. package/specs/components/switch.md +145 -0
  237. package/specs/components/table.md +372 -0
  238. package/specs/components/tabs.md +242 -0
  239. package/specs/components/textarea.md +166 -0
  240. package/specs/components/theme.md +364 -0
  241. package/specs/components/toast.md +198 -0
  242. package/specs/components/toolbar.md +258 -0
  243. package/specs/components/tooltip.md +152 -0
  244. package/specs/components/treegrid.md +363 -0
  245. package/specs/components/treeview.md +263 -0
  246. package/specs/components/window-splitter.md +225 -0
@@ -0,0 +1,198 @@
1
+ # cv-toast-region
2
+
3
+ Container that manages a queue of toast notifications with positioning, stacking layout, and auto-dismiss lifecycle.
4
+
5
+ **Headless:** [`createToast`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/toast.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-toast-region> (host)
11
+ └── <section part="base" role="region" aria-live="polite" aria-atomic="false">
12
+ ├── <cv-toast part="item" role="status|alert" data-level="info">
13
+ ├── <cv-toast part="item" role="alert" data-level="error">
14
+ └── …
15
+ ```
16
+
17
+ ## Attributes
18
+
19
+ | Attribute | Type | Default | Description |
20
+ | ------------- | ------ | ----------- | ------------------------------------------------------------------------------------------------------------------------ |
21
+ | `position` | String | `"top-end"` | Positioning of the region: `top-start` \| `top-center` \| `top-end` \| `bottom-start` \| `bottom-center` \| `bottom-end` |
22
+ | `max-visible` | Number | `3` | Maximum number of toasts displayed simultaneously |
23
+
24
+ ## Slots
25
+
26
+ | Slot | Description |
27
+ | -------- | ------------------------------------------------------------------------------- |
28
+ | _(none)_ | Content is rendered programmatically from the toast queue; no user-facing slots |
29
+
30
+ ## CSS Parts
31
+
32
+ | Part | Element | Description |
33
+ | ------ | ----------- | -------------------------------------------------------- |
34
+ | `base` | `<section>` | Root region element with `role="region"` and `aria-live` |
35
+
36
+ ## CSS Custom Properties
37
+
38
+ | Property | Default | Description |
39
+ | ----------------------------- | ------------------------- | --------------------------------- |
40
+ | `--cv-toast-region-gap` | `var(--cv-space-2, 8px)` | Spacing between stacked toasts |
41
+ | `--cv-toast-region-inset` | `var(--cv-space-4, 16px)` | Distance from viewport edges |
42
+ | `--cv-toast-region-z-index` | `9999` | Stacking order above page content |
43
+ | `--cv-toast-region-max-width` | `420px` | Maximum width of the toast region |
44
+
45
+ ## Visual States
46
+
47
+ | Host selector | Description |
48
+ | ----------------------------------- | ----------------------------------- |
49
+ | `:host([position="top-start"])` | Region fixed to top-left |
50
+ | `:host([position="top-center"])` | Region fixed to top-center |
51
+ | `:host([position="top-end"])` | Region fixed to top-right (default) |
52
+ | `:host([position="bottom-start"])` | Region fixed to bottom-left |
53
+ | `:host([position="bottom-center"])` | Region fixed to bottom-center |
54
+ | `:host([position="bottom-end"])` | Region fixed to bottom-right |
55
+
56
+ ## Events
57
+
58
+ | Event | Detail | Description |
59
+ | ---------- | ---------------- | ------------------------------------------------------------------------------- |
60
+ | `cv-close` | `{ id: string }` | Fires when a toast is removed from the queue (auto-dismiss or explicit dismiss) |
61
+
62
+ ## Reactive State Mapping
63
+
64
+ `cv-toast-region` is a visual adapter over headless `createToast`.
65
+
66
+ | UIKit Property | Direction | Headless Binding |
67
+ | -------------- | ------------- | --------------------------------------------------------------------------------------------------------- |
68
+ | `max-visible` | attr → option | Passed as `maxVisible` in `createToast(options)` |
69
+ | _(controller)_ | property | `createToastController()` wraps `createToast()` and exposes `push`, `dismiss`, `clear`, `pause`, `resume` |
70
+
71
+ | Headless State | Direction | DOM Reflection |
72
+ | ---------------------- | ---------------- | --------------------------------------------------------------------------- |
73
+ | `state.visibleItems()` | state → render | Drives the rendered list of `cv-toast` items |
74
+ | `state.isPaused()` | state → internal | Tracked internally; toggled by `mouseenter`/`mouseleave` on `[part="base"]` |
75
+
76
+ - `contracts.getRegionProps()` is spread onto the inner `[part="base"]` element to apply `id`, `role="region"`, `aria-live`, and `aria-atomic`.
77
+ - `mouseenter` on `[part="base"]` calls `actions.pause()`; `mouseleave` calls `actions.resume()`.
78
+ - UIKit tracks the previous set of toast IDs across renders and dispatches a `cv-close` event for each ID that disappears from the queue.
79
+ - UIKit does not own queue logic, timer management, or visibility slicing; headless state is the source of truth.
80
+
81
+ ## Usage
82
+
83
+ ```html
84
+ <!-- Basic usage (imperative API via controller) -->
85
+ <cv-toast-region></cv-toast-region>
86
+
87
+ <script>
88
+ const region = document.querySelector('cv-toast-region')
89
+ region.controller.push({message: 'File saved', level: 'success'})
90
+ region.controller.push({message: 'Connection lost', level: 'error', durationMs: 0})
91
+ </script>
92
+
93
+ <!-- Positioned bottom-center -->
94
+ <cv-toast-region position="bottom-center"></cv-toast-region>
95
+ ```
96
+
97
+ ## Child Elements
98
+
99
+ ### cv-toast
100
+
101
+ Individual toast notification item rendered within `cv-toast-region`. Displays a message with severity-based styling, an optional icon, and an optional dismiss button.
102
+
103
+ #### Anatomy
104
+
105
+ ```
106
+ <cv-toast> (host)
107
+ └── <div part="base" role="status|alert" data-level="info">
108
+ ├── <span part="icon">
109
+ │ └── <slot name="icon"> ← severity icon
110
+ ├── <div part="content">
111
+ │ └── <span part="label">
112
+ │ └── <slot> ← message text
113
+ └── <button part="dismiss"> ← only when closable
114
+ ```
115
+
116
+ #### Attributes
117
+
118
+ | Attribute | Type | Default | Description |
119
+ | ---------- | ------- | -------- | ----------------------------------------------------------------- |
120
+ | `level` | String | `"info"` | Severity level: `info` \| `success` \| `warning` \| `error` |
121
+ | `closable` | Boolean | `true` | Whether the dismiss button is shown |
122
+ | `toast-id` | String | `""` | Identifier for the toast, included in the `cv-close` event detail |
123
+
124
+ #### Slots
125
+
126
+ | Slot | Description |
127
+ | ----------- | ------------------------------------------------------- |
128
+ | `(default)` | Message text content |
129
+ | `icon` | Severity icon; overrides the default icon for the level |
130
+
131
+ #### CSS Parts
132
+
133
+ | Part | Element | Description |
134
+ | --------- | ---------- | ------------------------------------------------------------ |
135
+ | `base` | `<div>` | Root wrapper for the toast item with `role` and `data-level` |
136
+ | `icon` | `<span>` | Wrapper around the `icon` slot |
137
+ | `content` | `<div>` | Wrapper around the message area |
138
+ | `label` | `<span>` | Wrapper around the default slot (message text) |
139
+ | `dismiss` | `<button>` | Dismiss/close button |
140
+
141
+ #### CSS Custom Properties
142
+
143
+ | Property | Default | Description |
144
+ | --------------------------- | --------------------------------------------------- | ------------------------------------------------- |
145
+ | `--cv-toast-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding |
146
+ | `--cv-toast-padding-block` | `var(--cv-space-2, 8px)` | Vertical padding |
147
+ | `--cv-toast-border-radius` | `var(--cv-radius-md, 10px)` | Border radius |
148
+ | `--cv-toast-gap` | `var(--cv-space-2, 8px)` | Spacing between icon, content, and dismiss button |
149
+ | `--cv-toast-background` | `var(--cv-color-surface-elevated, #1d2432)` | Background color |
150
+ | `--cv-toast-border-color` | `var(--cv-color-border, #2a3245)` | Default border color |
151
+ | `--cv-toast-color` | `var(--cv-color-text, #e8ecf6)` | Text color |
152
+ | `--cv-toast-shadow` | `var(--cv-shadow-1, 0 2px 8px rgba(0, 0, 0, 0.24))` | Box shadow |
153
+
154
+ Additionally, component styles depend on theme tokens through fallback values:
155
+
156
+ | Theme Property | Default | Description |
157
+ | ----------------------------- | --------- | ------------------------------------ |
158
+ | `--cv-color-border` | `#2a3245` | Base border color |
159
+ | `--cv-color-surface-elevated` | `#1d2432` | Elevated surface background |
160
+ | `--cv-color-text` | `#e8ecf6` | Default text color |
161
+ | `--cv-color-text-muted` | `#9aa6bf` | Muted text color (dismiss button) |
162
+ | `--cv-color-primary` | `#65d7ff` | Primary accent (focus ring) |
163
+ | `--cv-color-success` | `#6ef7c8` | Success tint for border |
164
+ | `--cv-color-warning` | `#ffd36e` | Warning tint for border |
165
+ | `--cv-color-danger` | `#ff7d86` | Error/danger tint for border |
166
+ | `--cv-radius-md` | `10px` | Medium border radius |
167
+ | `--cv-radius-sm` | `6px` | Small border radius (dismiss button) |
168
+ | `--cv-space-2` | `8px` | Spacing scale |
169
+ | `--cv-space-3` | `12px` | Spacing scale |
170
+
171
+ #### Visual States
172
+
173
+ | Host selector / Part selector | Description |
174
+ | ----------------------------- | --------------------------------------------------------- |
175
+ | `[data-level="info"]` | Default styling with standard border |
176
+ | `[data-level="success"]` | Border tinted with `--cv-color-success` via `color-mix()` |
177
+ | `[data-level="warning"]` | Border tinted with `--cv-color-warning` via `color-mix()` |
178
+ | `[data-level="error"]` | Border tinted with `--cv-color-danger` via `color-mix()` |
179
+
180
+ #### Events
181
+
182
+ | Event | Detail | Description |
183
+ | ---------- | ---------------- | ----------------------------------------------------------------- |
184
+ | `cv-close` | `{ id: string }` | Fires when the dismiss button is clicked; bubbles and is composed |
185
+
186
+ #### Reactive State Mapping
187
+
188
+ Each toast item is bound to headless contracts per toast ID.
189
+
190
+ | Headless Contract | Direction | DOM Binding |
191
+ | ------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------ |
192
+ | `contracts.getToastProps(id)` | state → attrs | Spread onto `[part="base"]`: `id`, `role` (`status` or `alert`), `data-level` |
193
+ | `contracts.getDismissButtonProps(id)` | state → attrs | Spread onto `[part="dismiss"]`: `id`, `role="button"`, `tabindex="0"`, `aria-label`, `onClick` handler |
194
+
195
+ - `role` is determined by the headless contract based on level: `role="status"` for `info`/`success`, `role="alert"` for `warning`/`error`.
196
+ - In standalone usage, `cv-toast` computes role from its `level` property matching the headless contract logic.
197
+ - When rendered within `cv-toast-region`, the region spreads headless contract props onto each inline toast item.
198
+ - The dismiss button `onClick` handler from `getDismissButtonProps(id)` calls `actions.dismiss(id)` internally.
@@ -0,0 +1,258 @@
1
+ # cv-toolbar
2
+
3
+ Container of interactive elements that provides a single tab stop and arrow-key navigation between items, with separator support and focus memory.
4
+
5
+ **Headless:** [`createToolbar`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/toolbar.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-toolbar> (host)
11
+ └── <div part="base" role="toolbar" aria-orientation="…">
12
+ └── <slot> ← accepts <cv-toolbar-item> and <cv-toolbar-separator> children
13
+ ```
14
+
15
+ ## Attributes
16
+
17
+ | Attribute | Type | Default | Description |
18
+ | ------------- | ------- | -------------- | ----------------------------------------------------------------------------------------------------- |
19
+ | `value` | String | `""` | Active item value (reflects `activeId` from headless state) |
20
+ | `orientation` | String | `"horizontal"` | Navigation axis: `horizontal` \| `vertical` |
21
+ | `wrap` | Boolean | `true` | Whether arrow navigation wraps from last to first and vice versa. When `false`, clamps at boundaries. |
22
+ | `aria-label` | String | `""` | Accessible label for the toolbar |
23
+
24
+ ## Slots
25
+
26
+ | Slot | Description |
27
+ | ----------- | --------------------------------------------------------- |
28
+ | `(default)` | `<cv-toolbar-item>` and `<cv-toolbar-separator>` children |
29
+
30
+ ## CSS Parts
31
+
32
+ | Part | Element | Description |
33
+ | ------ | ------- | ------------------------------------------- |
34
+ | `base` | `<div>` | Root layout container with `role="toolbar"` |
35
+
36
+ ## CSS Custom Properties
37
+
38
+ | Property | Default | Description |
39
+ | ---------------------------- | --------------------------- | ----------------------------------------- |
40
+ | `--cv-toolbar-gap` | `var(--cv-space-1, 4px)` | Spacing between toolbar children |
41
+ | `--cv-toolbar-padding` | `var(--cv-space-1, 4px)` | Internal padding of the toolbar container |
42
+ | `--cv-toolbar-border-radius` | `var(--cv-radius-md, 10px)` | Border radius of the toolbar container |
43
+
44
+ Additionally, component styles depend on theme tokens through fallback values:
45
+
46
+ | Theme Property | Default | Description |
47
+ | -------------------- | --------- | ------------------------ |
48
+ | `--cv-color-border` | `#2a3245` | Toolbar border color |
49
+ | `--cv-color-surface` | `#141923` | Toolbar background color |
50
+ | `--cv-space-1` | `4px` | Gap and padding fallback |
51
+ | `--cv-radius-md` | `10px` | Border radius fallback |
52
+
53
+ ## Visual States
54
+
55
+ | Host selector | Description |
56
+ | --------------------------------- | -------------------------------------------------------------- |
57
+ | `:host([orientation="vertical"])` | Flex direction switches to column; items stretch to fill width |
58
+
59
+ ## Events
60
+
61
+ | Event | Detail | Description |
62
+ | ----------- | ---------------------------- | -------------------------------------------------------------------------------------------- |
63
+ | `cv-input` | `{activeId: string \| null}` | Fires on any user-driven active item change (arrow keys, click, focus) |
64
+ | `cv-change` | `{activeId: string \| null}` | Fires when active item commits (same detail as `cv-input`; both fire together on navigation) |
65
+
66
+ Both `cv-input` and `cv-change` fire when user interaction changes the active item. Programmatic `value` attribute changes that result in a headless state update also dispatch these events.
67
+
68
+ ## Reactive State Mapping
69
+
70
+ `cv-toolbar` is a visual adapter over headless `createToolbar`.
71
+
72
+ ### UIKit Property to Headless Binding
73
+
74
+ | UIKit Property | Direction | Headless Binding |
75
+ | -------------- | -------------- | --------------------------------------------------------- |
76
+ | `value` | attr -> action | `actions.setActive(value)` when `value` attribute changes |
77
+ | `orientation` | attr -> option | passed as `orientation` in `createToolbar(options)` |
78
+ | `wrap` | attr -> option | passed as `wrap` in `createToolbar(options)` |
79
+ | `aria-label` | attr -> option | passed as `ariaLabel` in `createToolbar(options)` |
80
+
81
+ ### Headless State to DOM Reflection
82
+
83
+ | Headless State | Direction | DOM Reflection |
84
+ | ------------------ | ------------- | ---------------------------------------------------------------------- |
85
+ | `state.activeId()` | state -> attr | `cv-toolbar[value]` host attribute |
86
+ | `state.activeId()` | state -> attr | `cv-toolbar-item[active]` boolean attribute on the active item element |
87
+ | `state.activeId()` | state -> prop | `cv-toolbar-item.tabIndex` set to `0` for active, `-1` for others |
88
+
89
+ ### Headless Actions Called
90
+
91
+ | Action | UIKit Trigger |
92
+ | ------------------------------ | -------------------------------------------------------------------------------------------------- |
93
+ | `actions.setActive(id)` | Item receives focus or click; also called when `value` attribute changes |
94
+ | `actions.handleKeyDown(event)` | `keydown` event on the toolbar root `[part="base"]` |
95
+ | `actions.handleToolbarFocus()` | `focusin` event on toolbar root when toolbar was not previously focused (re-entry detection) |
96
+ | `actions.handleToolbarBlur()` | `focusout` event on toolbar root when `relatedTarget` is outside the toolbar (full blur detection) |
97
+
98
+ ### Headless Contracts Spread
99
+
100
+ | Contract | UIKit Target |
101
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
102
+ | `contracts.getRootProps()` | Spread onto the `[part="base"]` element (`id`, `role`, `aria-orientation`, `aria-label`) |
103
+ | `contracts.getItemProps(id)` | Spread onto each `cv-toolbar-item` element (`id`, `tabindex`, `aria-disabled`, `data-active`). The `onFocus` callback is bound to the element's `focus` event. |
104
+ | `contracts.getSeparatorProps(id)` | Spread onto each `cv-toolbar-separator` element (`id`, `role`, `aria-orientation`) |
105
+
106
+ ### UIKit-Only Concerns (Not in Headless)
107
+
108
+ - **Focus management DOM calls**: Calling `.focus()` on the DOM element matching `activeId` after keyboard navigation. Headless sets state; UIKit moves DOM focus.
109
+ - **Focus-in/focus-out tracking**: UIKit must detect toolbar entry vs. internal focus moves (e.g., using a `hasFocus` flag updated on `focusin`/`focusout` with `relatedTarget` checks).
110
+ - **Separator rendering**: Visual appearance of separators (line style, thickness, spacing) is a UIKit concern. Headless only provides ARIA props.
111
+ - **Slot change handling**: Rebuilding the headless model when children are added or removed via `slotchange`.
112
+ - **`cv-input` / `cv-change` events**: Custom DOM events dispatched by the UIKit wrapper, not part of the headless model.
113
+
114
+ UIKit does not own navigation or focus-memory logic; headless state is the source of truth.
115
+
116
+ ## Keyboard Interaction
117
+
118
+ | Key | Horizontal | Vertical |
119
+ | ------------ | --------------------- | --------------------- |
120
+ | `ArrowRight` | Move to next item | — |
121
+ | `ArrowLeft` | Move to previous item | — |
122
+ | `ArrowDown` | — | Move to next item |
123
+ | `ArrowUp` | — | Move to previous item |
124
+ | `Home` | Move to first item | Move to first item |
125
+ | `End` | Move to last item | Move to last item |
126
+
127
+ - Disabled items and separators are skipped during keyboard navigation.
128
+ - When `wrap` is `true` (default), navigation wraps from last to first and vice versa.
129
+ - When `wrap` is `false`, navigation clamps at boundaries (first/last item).
130
+ - Roving tabindex: only the active item has `tabindex="0"`; all others have `tabindex="-1"`.
131
+ - Focus memory: re-entering the toolbar via Tab restores focus to the last-active item.
132
+
133
+ ## Usage
134
+
135
+ ```html
136
+ <cv-toolbar aria-label="Text formatting">
137
+ <cv-toolbar-item value="bold">Bold</cv-toolbar-item>
138
+ <cv-toolbar-item value="italic">Italic</cv-toolbar-item>
139
+ <cv-toolbar-item value="underline">Underline</cv-toolbar-item>
140
+ </cv-toolbar>
141
+
142
+ <cv-toolbar aria-label="Actions" wrap="false">
143
+ <cv-toolbar-item value="cut">Cut</cv-toolbar-item>
144
+ <cv-toolbar-item value="copy">Copy</cv-toolbar-item>
145
+ <cv-toolbar-separator></cv-toolbar-separator>
146
+ <cv-toolbar-item value="paste">Paste</cv-toolbar-item>
147
+ </cv-toolbar>
148
+
149
+ <cv-toolbar orientation="vertical" aria-label="Tools">
150
+ <cv-toolbar-item value="brush">Brush</cv-toolbar-item>
151
+ <cv-toolbar-item value="eraser">Eraser</cv-toolbar-item>
152
+ <cv-toolbar-separator></cv-toolbar-separator>
153
+ <cv-toolbar-item value="fill">Fill</cv-toolbar-item>
154
+ <cv-toolbar-item value="picker" disabled>Picker</cv-toolbar-item>
155
+ </cv-toolbar>
156
+ ```
157
+
158
+ ## Child Elements
159
+
160
+ ### cv-toolbar-item
161
+
162
+ Interactive element within a toolbar that participates in roving tabindex navigation.
163
+
164
+ #### Anatomy
165
+
166
+ ```
167
+ <cv-toolbar-item> (host)
168
+ └── <div part="base">
169
+ └── <slot>
170
+ ```
171
+
172
+ #### Attributes
173
+
174
+ | Attribute | Type | Default | Description |
175
+ | ---------- | ------- | ------- | ------------------------------------------------------------------------ |
176
+ | `value` | String | `""` | Unique identifier for this item. Auto-generated as `item-{n}` if empty. |
177
+ | `disabled` | Boolean | `false` | Prevents this item from receiving focus via keyboard navigation |
178
+ | `active` | Boolean | `false` | Whether this item is the current roving-focus target (managed by parent) |
179
+
180
+ #### Slots
181
+
182
+ | Slot | Description |
183
+ | ----------- | ------------------------------------------------ |
184
+ | `(default)` | Item content (text, icon, or any inline content) |
185
+
186
+ #### CSS Parts
187
+
188
+ | Part | Element | Description |
189
+ | ------ | ------- | ------------------------ |
190
+ | `base` | `<div>` | Root interactive wrapper |
191
+
192
+ #### CSS Custom Properties
193
+
194
+ | Property | Default | Description |
195
+ | ---------------------------------- | -------------------------- | ------------------------------ |
196
+ | `--cv-toolbar-item-min-height` | `32px` | Minimum block size of the item |
197
+ | `--cv-toolbar-item-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding |
198
+ | `--cv-toolbar-item-border-radius` | `var(--cv-radius-sm, 6px)` | Border radius |
199
+
200
+ Additionally, component styles depend on theme tokens through fallback values:
201
+
202
+ | Theme Property | Default | Description |
203
+ | ---------------------- | --------- | -------------------------- |
204
+ | `--cv-color-border` | `#2a3245` | Item border color |
205
+ | `--cv-color-surface` | `#141923` | Item background color |
206
+ | `--cv-color-text` | `#e8ecf6` | Item text color |
207
+ | `--cv-color-primary` | `#65d7ff` | Active state accent color |
208
+ | `--cv-duration-fast` | `120ms` | Transition duration |
209
+ | `--cv-easing-standard` | `ease` | Transition timing function |
210
+
211
+ #### Visual States
212
+
213
+ | Host selector | Description |
214
+ | ----------------------- | ----------------------------------------------------------------------- |
215
+ | `:host([active])` | Primary-tinted border and blended background using `--cv-color-primary` |
216
+ | `:host([disabled])` | Reduced opacity (`0.55`) |
217
+ | `:host(:focus-visible)` | Focus ring: `2px solid var(--cv-color-primary)` with `1px` offset |
218
+
219
+ ---
220
+
221
+ ### cv-toolbar-separator
222
+
223
+ Non-interactive visual divider within a toolbar. Separators are skipped by keyboard navigation and cannot receive focus.
224
+
225
+ #### Anatomy
226
+
227
+ ```
228
+ <cv-toolbar-separator> (host)
229
+ └── <div part="base" role="separator" aria-orientation="…">
230
+ ```
231
+
232
+ The separator's `aria-orientation` is perpendicular to the toolbar's orientation: a horizontal toolbar renders vertical separators, and vice versa.
233
+
234
+ #### Attributes
235
+
236
+ | Attribute | Type | Default | Description |
237
+ | --------- | ------ | ------- | ---------------------------------------------------------------- |
238
+ | `value` | String | `""` | Identifier used to match the separator in the headless item list |
239
+
240
+ #### CSS Parts
241
+
242
+ | Part | Element | Description |
243
+ | ------ | ------- | ---------------------------------------------- |
244
+ | `base` | `<div>` | Separator line element with `role="separator"` |
245
+
246
+ #### CSS Custom Properties
247
+
248
+ | Property | Default | Description |
249
+ | ------------------------------- | --------------------------------- | ------------------------------- |
250
+ | `--cv-toolbar-separator-size` | `1px` | Thickness of the separator line |
251
+ | `--cv-toolbar-separator-color` | `var(--cv-color-border, #2a3245)` | Color of the separator line |
252
+ | `--cv-toolbar-separator-margin` | `var(--cv-space-1, 4px)` | Margin around the separator |
253
+
254
+ #### Visual States
255
+
256
+ | Host selector | Description |
257
+ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
258
+ | `:host` | In a horizontal toolbar: vertical line (`height: auto`, `width: --cv-toolbar-separator-size`). In a vertical toolbar: horizontal line (`width: auto`, `height: --cv-toolbar-separator-size`). Orientation is communicated via the parent spreading `getSeparatorProps`. |
@@ -0,0 +1,152 @@
1
+ # cv-tooltip
2
+
3
+ Contextual information overlay that appears near a trigger element on hover, focus, click, or programmatic control.
4
+
5
+ **Headless:** [`createTooltip`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/tooltip.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-tooltip> (host)
11
+ └── <span part="base">
12
+ ├── <span part="trigger">
13
+ │ └── <slot name="trigger">
14
+ └── <span part="content" role="tooltip">
15
+ ├── <slot name="content">
16
+ └── <span part="arrow"> ← only when [arrow]
17
+ ```
18
+
19
+ ## Attributes
20
+
21
+ | Attribute | Type | Default | Description |
22
+ | ------------ | ------- | --------------- | -------------------------------------------------------------------------- |
23
+ | `open` | Boolean | `false` | Reflects tooltip visibility; can be set to programmatically show or hide |
24
+ | `disabled` | Boolean | `false` | Disables all trigger interactions; removes `aria-describedby` from trigger |
25
+ | `show-delay` | Number | `120` | Milliseconds to wait before showing the tooltip |
26
+ | `hide-delay` | Number | `80` | Milliseconds to wait before hiding the tooltip |
27
+ | `trigger` | String | `'hover focus'` | Space-separated trigger modes: `hover` \| `focus` \| `click` \| `manual` |
28
+ | `arrow` | Boolean | `false` | Renders the `part="arrow"` CSS triangle pointing toward the trigger |
29
+
30
+ ## Slots
31
+
32
+ | Slot | Description |
33
+ | --------- | ---------------------------------------------------------------------------- |
34
+ | `trigger` | The element that activates the tooltip (receives `aria-describedby` linkage) |
35
+ | `content` | Rich HTML tooltip body content |
36
+
37
+ ## CSS Parts
38
+
39
+ | Part | Element | Description |
40
+ | --------- | -------- | -------------------------------------------------------------------- |
41
+ | `base` | `<span>` | Root layout wrapper, positioned relatively |
42
+ | `trigger` | `<span>` | Wrapper around the trigger slot; receives event listeners |
43
+ | `content` | `<span>` | Tooltip body container with `role="tooltip"`; hidden when not open |
44
+ | `arrow` | `<span>` | Optional CSS triangle indicator; rendered only when `[arrow]` is set |
45
+
46
+ ## CSS Custom Properties
47
+
48
+ Component styles depend on theme tokens through inline fallback values:
49
+
50
+ > **Note:** Component-level `--cv-tooltip-*` custom property indirection is not implemented.
51
+ > Styles reference theme tokens directly.
52
+
53
+ | Theme Property | Default | Description |
54
+ | ----------------------------- | ------------------------------- | --------------------------- |
55
+ | `--cv-color-surface-elevated` | `#1d2432` | Elevated surface background |
56
+ | `--cv-color-border` | `#2a3245` | Border color |
57
+ | `--cv-color-text` | `#e8ecf6` | Default text color |
58
+ | `--cv-shadow-1` | `0 2px 8px rgba(0, 0, 0, 0.24)` | Drop shadow |
59
+ | `--cv-radius-sm` | `6px` | Border radius |
60
+ | `--cv-space-1` | `4px` | Small spacing scale |
61
+ | `--cv-space-2` | `8px` | Medium spacing scale |
62
+
63
+ ## Visual States
64
+
65
+ | Host selector | Description |
66
+ | ------------------- | ------------------------------------------------------------------- |
67
+ | `:host([open])` | Tooltip content is visible; `[part="content"]` has `hidden` removed |
68
+ | `:host([disabled])` | All trigger interactions are blocked; `aria-describedby` is removed |
69
+ | `:host([arrow])` | Arrow indicator (`[part="arrow"]`) is rendered and visible |
70
+
71
+ ## Events
72
+
73
+ | Event | Detail | Description |
74
+ | ----------- | ------------------- | ------------------------------------------------------------- |
75
+ | `cv-input` | `{ open: boolean }` | Fires on every open/close transition triggered by interaction |
76
+ | `cv-change` | `{ open: boolean }` | Fires when the open state commits after interaction |
77
+
78
+ Both events bubble and are composed. They are dispatched together on every transition of `isOpen`.
79
+
80
+ ## Reactive State Mapping
81
+
82
+ `cv-tooltip` is a visual adapter over headless `createTooltip`.
83
+
84
+ | UIKit Property | Direction | Headless Binding |
85
+ | -------------- | ------------- | ------------------------------------------------------------------- |
86
+ | `open` | attr → action | `actions.open()` / `actions.close()` depending on value |
87
+ | `disabled` | attr → action | `actions.setDisabled(value)` |
88
+ | `show-delay` | attr → option | passed as `showDelay` to `createTooltip(options)` (recreates model) |
89
+ | `hide-delay` | attr → option | passed as `hideDelay` to `createTooltip(options)` (recreates model) |
90
+ | `trigger` | attr → option | passed as `trigger` to `createTooltip(options)` |
91
+
92
+ | Headless State | Direction | DOM Reflection |
93
+ | -------------------- | ------------ | --------------------------- |
94
+ | `state.isOpen()` | state → attr | `[open]` host attribute |
95
+ | `state.isDisabled()` | state → attr | `[disabled]` host attribute |
96
+
97
+ - `contracts.getTriggerProps()` is spread onto `[part="trigger"]` to apply `id`, `aria-describedby`, and all active event handlers (`onPointerEnter`, `onPointerLeave`, `onFocus`, `onBlur`, `onClick`, `onKeyDown`) depending on the active `trigger` modes.
98
+ - `contracts.getTooltipProps()` is spread onto `[part="content"]` to apply `id`, `role="tooltip"`, `tabindex="-1"`, and `hidden`.
99
+ - `aria-describedby` is also propagated to assigned slotted trigger elements directly via `syncTriggerAria`.
100
+ - UIKit dispatches `cv-input` and `cv-change` events by observing transitions of `isOpen` caused by interaction; programmatic `open`/`close` calls that do not change state do not re-emit.
101
+ - UIKit does not own trigger or delay logic; headless state is the source of truth.
102
+ - When `show-delay` or `hide-delay` changes, the model is recreated with the current `open` state preserved as `initialOpen`.
103
+ - Public methods `show()` and `hide()` delegate to `model.actions.show()` / `model.actions.hide()` for `manual` mode consumers.
104
+
105
+ ## Usage
106
+
107
+ ```html
108
+ <!-- Default: hover + focus triggers -->
109
+ <cv-tooltip>
110
+ <button slot="trigger">Hover me</button>
111
+ <span slot="content">Contextual info</span>
112
+ </cv-tooltip>
113
+
114
+ <!-- Click trigger only -->
115
+ <cv-tooltip trigger="click">
116
+ <button slot="trigger">Click me</button>
117
+ <span slot="content">Shown on click</span>
118
+ </cv-tooltip>
119
+
120
+ <!-- With arrow indicator -->
121
+ <cv-tooltip arrow>
122
+ <button slot="trigger">With arrow</button>
123
+ <span slot="content">Tooltip with arrow</span>
124
+ </cv-tooltip>
125
+
126
+ <!-- Disabled -->
127
+ <cv-tooltip disabled>
128
+ <button slot="trigger">No tooltip</button>
129
+ <span slot="content">Never shown</span>
130
+ </cv-tooltip>
131
+
132
+ <!-- Manual mode (programmatic control) -->
133
+ <cv-tooltip trigger="manual" id="my-tip">
134
+ <button slot="trigger">Target</button>
135
+ <span slot="content">Shown programmatically</span>
136
+ </cv-tooltip>
137
+ <script>
138
+ document.querySelector('#my-tip').show()
139
+ </script>
140
+
141
+ <!-- Custom delays -->
142
+ <cv-tooltip show-delay="300" hide-delay="200">
143
+ <button slot="trigger">Delayed</button>
144
+ <span slot="content">Appears after 300ms</span>
145
+ </cv-tooltip>
146
+
147
+ <!-- Rich HTML content -->
148
+ <cv-tooltip>
149
+ <button slot="trigger">Rich tooltip</button>
150
+ <span slot="content"> <strong>Title</strong><br />Supporting text </span>
151
+ </cv-tooltip>
152
+ ```