@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,204 @@
1
+ # `cv-sidebar`
2
+
3
+ Persistent layout sidebar with desktop expand/collapse, mobile overlay mode, and opt-in same-page `scrollspy`.
4
+
5
+ **Headless base:** [`createSidebar`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/sidebar.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```text
10
+ <cv-sidebar>
11
+ ├── <div part="overlay">
12
+ └── <aside part="panel">
13
+ ├── <header part="header">
14
+ │ ├── <slot name="header">
15
+ │ └── <button part="toggle">
16
+ │ └── <slot name="toggle">
17
+ ├── <nav part="body">
18
+ │ └── <slot>
19
+ └── <footer part="footer">
20
+ └── <slot name="footer">
21
+ ```
22
+
23
+ ## Attributes
24
+
25
+ | Attribute | Type | Default | Description |
26
+ | -------------------------- | ------------------------------------- | ---------------------- | ---------------------------------------------------------- |
27
+ | `expanded` | Boolean | `true` | Desktop full-width mode |
28
+ | `collapsed` | Boolean | `false` | Desktop rail mode; inverse of `expanded` |
29
+ | `mobile` | Boolean | `false` | Mobile/overlay mode |
30
+ | `overlay-open` | Boolean | `false` | Mobile overlay visibility |
31
+ | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Width preset |
32
+ | `breakpoint` | String | `"768px"` | Auto-switch breakpoint for mobile mode |
33
+ | `close-on-escape` | Boolean | `true` | Whether `Escape` closes mobile overlay |
34
+ | `close-on-outside-pointer` | Boolean | `true` | Whether outside pointer closes mobile overlay |
35
+ | `initial-focus-id` | String | --- | Initial focus target when overlay opens |
36
+ | `aria-label` | String | `"Sidebar navigation"` | Accessible label for the panel landmark/dialog |
37
+ | `scrollspy` | Boolean | `false` | Enables same-page hash navigation tracking |
38
+ | `scrollspy-offset-top` | Number | `0` | Top offset used when resolving the active section |
39
+ | `scrollspy-strategy` | `"top-anchor" \| "viewport-dominant"` | `"top-anchor"` | Strategy used to resolve the active section |
40
+ | `scrollspy-smooth-scroll` | Boolean | `true` | Uses `scrollIntoView({behavior: "smooth"})` for hash items |
41
+
42
+ ## Properties
43
+
44
+ | Property | Type | Default | Description |
45
+ | --------------- | ------------------------------------------- | ------- | ------------------------------------------------ |
46
+ | `scrollspyRoot` | `Document \| ShadowRoot \| Element \| null` | `null` | Explicit root used for resolving section targets |
47
+ | `activeId` | `string \| null` | `null` | Readonly id of the active `scrollspy` target |
48
+
49
+ When `scrollspyRoot` is `null`, `cv-sidebar` resolves section targets in the host element root (`Document` or parent `ShadowRoot`).
50
+
51
+ ## Slots
52
+
53
+ | Slot | Description |
54
+ | -------- | --------------------------------------------------------------------- |
55
+ | default | Navigation content, including `cv-sidebar-item` or plain hash anchors |
56
+ | `header` | Header content |
57
+ | `toggle` | Custom toggle icon/content |
58
+ | `footer` | Footer content |
59
+
60
+ ## CSS Parts
61
+
62
+ | Part | Description |
63
+ | --------- | --------------------------------- |
64
+ | `overlay` | Mobile backdrop |
65
+ | `panel` | Sidebar panel |
66
+ | `header` | Header area |
67
+ | `toggle` | Expand/collapse or overlay toggle |
68
+ | `body` | Main nav body |
69
+ | `footer` | Footer area |
70
+
71
+ ## CSS Custom Properties
72
+
73
+ | Property | Default |
74
+ | ---------------------------------- | --------------------------------------------- |
75
+ | `--cv-sidebar-inline-size` | `280px` |
76
+ | `--cv-sidebar-rail-inline-size` | `56px` |
77
+ | `--cv-sidebar-z-index` | `30` |
78
+ | `--cv-sidebar-background` | `var(--cv-color-surface, #141923)` |
79
+ | `--cv-sidebar-border-color` | `var(--cv-color-border, #2a3245)` |
80
+ | `--cv-sidebar-padding-block` | `var(--cv-space-3, 12px)` |
81
+ | `--cv-sidebar-padding-inline` | `var(--cv-space-3, 12px)` |
82
+ | `--cv-sidebar-overlay-color` | `color-mix(in oklab, black 56%, transparent)` |
83
+ | `--cv-sidebar-transition-duration` | `var(--cv-duration-normal, 200ms)` |
84
+ | `--cv-sidebar-transition-easing` | `var(--cv-easing-standard, ease)` |
85
+
86
+ ## Events
87
+
88
+ | Event | Detail | Description |
89
+ | ------------------------ | ---------------------------- | ----------------------------------------- |
90
+ | `cv-input` | `{expanded: boolean}` | Desktop user-driven expand/collapse |
91
+ | `cv-change` | `{expanded: boolean}` | Desktop committed expand/collapse |
92
+ | `cv-input` | `{overlayOpen: boolean}` | Mobile user-driven overlay open/close |
93
+ | `cv-change` | `{overlayOpen: boolean}` | Mobile committed overlay open/close |
94
+ | `cv-expand` | --- | Desktop expand lifecycle start |
95
+ | `cv-after-expand` | --- | Desktop expand lifecycle end |
96
+ | `cv-collapse` | --- | Desktop collapse lifecycle start |
97
+ | `cv-after-collapse` | --- | Desktop collapse lifecycle end |
98
+ | `cv-overlay-open` | --- | Mobile overlay open lifecycle start |
99
+ | `cv-after-overlay-open` | --- | Mobile overlay open lifecycle end |
100
+ | `cv-overlay-close` | --- | Mobile overlay close lifecycle start |
101
+ | `cv-after-overlay-close` | --- | Mobile overlay close lifecycle end |
102
+ | `cv-scrollspy-change` | `{activeId: string \| null}` | Fires when the active hash target changes |
103
+
104
+ ## Scrollspy behavior
105
+
106
+ - `scrollspy` only manages same-page hash items (`href="#section-id"`).
107
+ - Slotted `cv-sidebar-item` elements receive `active` state automatically.
108
+ - Slotted plain anchors receive `aria-current="location"` automatically.
109
+ - Same-page hash clicks are intercepted and resolved by the sidebar scrollspy controller.
110
+ - No `scroll` listeners are used. Active state is derived from `IntersectionObserver`.
111
+ - `top-anchor` keeps classic TOC semantics based on the section closest to the configured top anchor.
112
+ - `viewport-dominant` resolves the active section from effective viewport dominance using visible coverage, distance to viewport center, and hysteresis.
113
+ - In `viewport-dominant`, same-page hash clicks scroll the target to the viewport center instead of the top edge.
114
+ - In `viewport-dominant`, same-page hash clicks do not optimistically switch `activeId`; the active item updates only after observer-driven recompute.
115
+
116
+ ## `cv-sidebar-item`
117
+
118
+ Lightweight sidebar navigation item that adapts to expanded and collapsed rail modes.
119
+
120
+ ### Anatomy
121
+
122
+ ```text
123
+ <cv-sidebar-item>
124
+ └── <a part="base">
125
+ ├── <span part="prefix">
126
+ │ └── <slot name="prefix">
127
+ ├── <span part="label">
128
+ │ └── <slot>
129
+ └── <span part="suffix">
130
+ └── <slot name="suffix">
131
+ ```
132
+
133
+ ### Attributes
134
+
135
+ | Attribute | Type | Default | Description |
136
+ | ---------- | ------- | ------- | -------------------- |
137
+ | `href` | String | `""` | Item target |
138
+ | `active` | Boolean | `false` | Current/active state |
139
+ | `disabled` | Boolean | `false` | Disables interaction |
140
+
141
+ ### Slots
142
+
143
+ | Slot | Description |
144
+ | -------- | ------------------------ |
145
+ | default | Label |
146
+ | `prefix` | Leading icon/content |
147
+ | `suffix` | Trailing badge/indicator |
148
+
149
+ ### CSS Parts
150
+
151
+ | Part | Description |
152
+ | -------- | -------------- |
153
+ | `base` | Root anchor |
154
+ | `prefix` | Prefix wrapper |
155
+ | `label` | Label wrapper |
156
+ | `suffix` | Suffix wrapper |
157
+
158
+ ### CSS Custom Properties
159
+
160
+ | Property | Default |
161
+ | ------------------------------------- | ------------------------------------- |
162
+ | `--cv-sidebar-item-gap` | `var(--cv-space-2, 8px)` |
163
+ | `--cv-sidebar-item-min-block-size` | `32px` |
164
+ | `--cv-sidebar-item-padding-block` | `var(--cv-space-2, 8px)` |
165
+ | `--cv-sidebar-item-padding-inline` | `var(--cv-space-3, 12px)` |
166
+ | `--cv-sidebar-item-border-radius` | `var(--cv-radius-sm, 6px)` |
167
+ | `--cv-sidebar-item-color` | `var(--cv-color-text-muted, #9aa6bf)` |
168
+ | `--cv-sidebar-item-color-hover` | `var(--cv-color-text, #e8ecf6)` |
169
+ | `--cv-sidebar-item-color-active` | `var(--cv-color-primary, #65d7ff)` |
170
+ | `--cv-sidebar-item-background` | `transparent` |
171
+ | `--cv-sidebar-item-background-hover` | mixed surface highlight |
172
+ | `--cv-sidebar-item-background-active` | `transparent` |
173
+ | `--cv-sidebar-item-indicator-width` | `2px` |
174
+ | `--cv-sidebar-item-indicator-color` | `var(--cv-color-primary, #65d7ff)` |
175
+
176
+ `cv-sidebar` propagates `collapsed` and `mobile` context to direct child `cv-sidebar-item` elements so labels and suffix content are visually hidden in desktop rail mode without consumer-specific wiring.
177
+
178
+ ## Usage
179
+
180
+ ```html
181
+ <cv-sidebar>
182
+ <span slot="header">Threat Model</span>
183
+
184
+ <cv-sidebar-item href="#assets">
185
+ <cv-icon slot="prefix" name="database"></cv-icon>
186
+ Assets
187
+ </cv-sidebar-item>
188
+
189
+ <cv-sidebar-item href="#crypto" active>
190
+ <cv-icon slot="prefix" name="shield"></cv-icon>
191
+ Crypto
192
+ <cv-badge slot="suffix">Active</cv-badge>
193
+ </cv-sidebar-item>
194
+ </cv-sidebar>
195
+ ```
196
+
197
+ ```html
198
+ <cv-sidebar scrollspy scrollspy-offset-top="80">
199
+ <span slot="header">On this page</span>
200
+ <cv-sidebar-item href="#assets">Assets</cv-sidebar-item>
201
+ <cv-sidebar-item href="#trust-boundaries">Trust Boundaries</cv-sidebar-item>
202
+ <cv-sidebar-item href="#crypto">Crypto</cv-sidebar-item>
203
+ </cv-sidebar>
204
+ ```
@@ -0,0 +1,157 @@
1
+ # cv-spinbutton
2
+
3
+ Numeric spinbutton input with keyboard, stepper controls, form-associated behavior, and headless-driven ARIA contracts.
4
+
5
+ **Headless:** [`createSpinbutton`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/spinbutton.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-spinbutton> (host)
11
+ └── <div part="base">
12
+ ├── <input part="input" role="spinbutton" inputmode="decimal">
13
+ └── <div part="actions">
14
+ ├── <button part="increment" type="button">
15
+ └── <button part="decrement" type="button">
16
+ ```
17
+
18
+ ## Attributes
19
+
20
+ | Attribute | Type | Default | Description |
21
+ | ------------------ | ------- | ------- | ------------------------------------------------------- |
22
+ | `name` | String | `""` | Form field name for submit serialization |
23
+ | `value` | Number | `0` | Current numeric value |
24
+ | `min` | Number | — | Optional minimum boundary |
25
+ | `max` | Number | — | Optional maximum boundary |
26
+ | `step` | Number | `1` | Small increment/decrement step |
27
+ | `large-step` | Number | `10` | Large increment/decrement step (`PageUp`/`PageDown`) |
28
+ | `disabled` | Boolean | `false` | Blocks interaction and omits value from form submission |
29
+ | `read-only` | Boolean | `false` | Keeps focusable/announced but blocks user mutation |
30
+ | `required` | Boolean | `false` | Enables constraint-validation checks |
31
+ | `aria-label` | String | `""` | Accessible label |
32
+ | `aria-labelledby` | String | `""` | ID reference to visible label |
33
+ | `aria-describedby` | String | `""` | ID reference to description |
34
+
35
+ ## Slots
36
+
37
+ None.
38
+
39
+ ## CSS Parts
40
+
41
+ | Part | Element | Description |
42
+ | ----------- | ---------- | ------------------------------------------------- |
43
+ | `base` | `<div>` | Layout container for input and actions |
44
+ | `input` | `<input>` | Focusable spinbutton control, editable text input |
45
+ | `actions` | `<div>` | Wrapper for increment/decrement controls |
46
+ | `increment` | `<button>` | Increments value by `step` |
47
+ | `decrement` | `<button>` | Decrements value by `step` |
48
+
49
+ ## CSS Custom Properties
50
+
51
+ No component-scoped `--cv-spinbutton-*` properties are currently defined.
52
+
53
+ Theme tokens used with fallbacks:
54
+
55
+ | Theme Property | Default | Description |
56
+ | ----------------------------- | --------- | ------------------------- |
57
+ | `--cv-space-1` | `4px` | Internal spacing |
58
+ | `--cv-radius-sm` | `6px` | Base radius |
59
+ | `--cv-color-border` | `#2a3245` | Border color |
60
+ | `--cv-color-surface` | `#141923` | Base background |
61
+ | `--cv-color-surface-elevated` | `#1d2432` | Stepper button background |
62
+ | `--cv-color-text` | `#e8ecf6` | Foreground color |
63
+ | `--cv-color-primary` | `#65d7ff` | Focus ring color |
64
+
65
+ ## Visual States
66
+
67
+ | Host selector | Description |
68
+ | -------------------- | ------------------------------------------------------------------- |
69
+ | `:host([disabled])` | Base and controls are visually muted and non-interactive |
70
+ | `:host([read-only])` | Input remains focusable; stepper controls are muted/non-interactive |
71
+
72
+ ## Reactive State Mapping
73
+
74
+ `cv-spinbutton` is a thin adapter over headless `createSpinbutton`.
75
+
76
+ | UIKit Property | Direction | Headless Binding |
77
+ | ------------------ | ------------- | ------------------------------------- |
78
+ | `value` | attr → action | `actions.setValue(value)` |
79
+ | `disabled` | attr → action | `actions.setDisabled(value)` |
80
+ | `read-only` | attr → action | `actions.setReadOnly(value)` |
81
+ | `min` | attr → option | passed to `createSpinbutton(options)` |
82
+ | `max` | attr → option | passed to `createSpinbutton(options)` |
83
+ | `step` | attr → option | passed to `createSpinbutton(options)` |
84
+ | `large-step` | attr → option | passed to `createSpinbutton(options)` |
85
+ | `aria-label` | attr → option | passed as `ariaLabel` |
86
+ | `aria-labelledby` | attr → option | passed as `ariaLabelledBy` |
87
+ | `aria-describedby` | attr → option | passed as `ariaDescribedBy` |
88
+
89
+ | Headless State/Contract | Direction | DOM Reflection |
90
+ | ------------------------------------- | ---------------- | ------------------------------------------------------------ |
91
+ | `state.value()` | state → value | `[part="input"].value` and host `value` property |
92
+ | `contracts.getSpinbuttonProps()` | contract → attrs | role, tabindex, and ARIA attributes on `[part="input"]` |
93
+ | `contracts.getIncrementButtonProps()` | contract → attrs | id/tabindex/aria-disabled/aria-label on `[part="increment"]` |
94
+ | `contracts.getDecrementButtonProps()` | contract → attrs | id/tabindex/aria-disabled/aria-label on `[part="decrement"]` |
95
+
96
+ - UIKit may hold transient draft text while editing.
97
+ - Draft text commits to headless on `Enter` or `blur` only.
98
+ - UIKit does not implement keyboard stepping, clamping, snapping, or ARIA computation itself.
99
+
100
+ ## Events
101
+
102
+ | Event | Detail | Description |
103
+ | ----------- | ----------------- | --------------------------------------------------------------------- |
104
+ | `cv-input` | `{value: number}` | Fires on each user-triggered value mutation (buttons/stepping/commit) |
105
+ | `cv-change` | `{value: number}` | Fires together with `cv-input` for the same user-triggered mutation |
106
+
107
+ Programmatic mutations through imperative API do not emit `cv-input`/`cv-change`.
108
+
109
+ ## Imperative API
110
+
111
+ | Method / Property | Description |
112
+ | ---------------------------- | ------------------------------------------------------- |
113
+ | `stepUp(times = 1)` | Increments by `step` `times` times |
114
+ | `stepDown(times = 1)` | Decrements by `step` `times` times |
115
+ | `pageUp(times = 1)` | Increments by `largeStep` `times` times |
116
+ | `pageDown(times = 1)` | Decrements by `largeStep` `times` times |
117
+ | `setValue(value)` | Sets numeric value through headless normalization |
118
+ | `getValue()` | Returns current committed numeric value |
119
+ | `setRange(min, max)` | Updates range boundaries (`min`/`max`) |
120
+ | `focus(options?)` | Focuses inner input control |
121
+ | `select()` | Selects text in inner input control |
122
+ | `checkValidity()` | Runs current validation checks |
123
+ | `reportValidity()` | Reports validation state to UA when supported |
124
+ | `setCustomValidity(message)` | Sets/clears custom validity message |
125
+ | `form` | Form owner when form-associated internals are supported |
126
+ | `validity` | Current validity state when supported |
127
+ | `validationMessage` | Current validation message |
128
+ | `willValidate` | Whether control participates in validation |
129
+
130
+ UA callbacks supported for form-associated lifecycle:
131
+
132
+ - `formDisabledCallback(disabled)`
133
+ - `formResetCallback()`
134
+ - `formStateRestoreCallback(state)`
135
+
136
+ ## Form Association
137
+
138
+ - Component is form-associated via `ElementInternals` when available.
139
+ - Submit value is serialized as raw numeric string from committed `value`.
140
+ - `disabled` state removes form value from submission.
141
+ - Reset restores the initial `value` snapshot captured on first connection.
142
+
143
+ ## Usage
144
+
145
+ ```html
146
+ <cv-spinbutton name="quantity" value="2" min="0" max="10" step="1"></cv-spinbutton>
147
+
148
+ <cv-spinbutton value="50" min="0" max="100" step="5" large-step="25"></cv-spinbutton>
149
+
150
+ <cv-spinbutton value="5" aria-label="Quantity"></cv-spinbutton>
151
+
152
+ <cv-spinbutton value="3" read-only></cv-spinbutton>
153
+
154
+ <form>
155
+ <cv-spinbutton name="items" value="1" required></cv-spinbutton>
156
+ </form>
157
+ ```
@@ -0,0 +1,83 @@
1
+ # cv-spinner
2
+
3
+ Indeterminate loading spinner with SVG track and indicator animation.
4
+
5
+ **Headless:** [`createSpinner`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/spinner.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-spinner> (host)
11
+ └── <svg part="base" role="progressbar" aria-label="...">
12
+ ├── <circle part="track">
13
+ └── <circle part="indicator">
14
+ ```
15
+
16
+ ## Attributes
17
+
18
+ | Attribute | Type | Default | Description |
19
+ | --------- | ------ | ----------- | ------------------------------------------------- |
20
+ | `label` | String | `"Loading"` | Accessible name announced by assistive technology |
21
+
22
+ No `size` attribute is provided. Sizing is controlled entirely via CSS `font-size` on the host element; the SVG scales relative to `1em`.
23
+
24
+ ## Slots
25
+
26
+ None. The spinner is purely visual with no slotted content.
27
+
28
+ ## CSS Parts
29
+
30
+ | Part | Element | Description |
31
+ | ----------- | ---------- | ---------------------------------------------- |
32
+ | `base` | `<svg>` | Root SVG element with ARIA attributes |
33
+ | `track` | `<circle>` | Background circle (static ring) |
34
+ | `indicator` | `<circle>` | Animated arc indicating indeterminate progress |
35
+
36
+ ## CSS Custom Properties
37
+
38
+ | Property | Default | Description |
39
+ | ------------------------------ | ---------------------------------- | ---------------------------------------- |
40
+ | `--cv-spinner-track-width` | `4px` | Stroke width of both track and indicator |
41
+ | `--cv-spinner-track-color` | `var(--cv-color-border, #2a3245)` | Color of the background track ring |
42
+ | `--cv-spinner-indicator-color` | `var(--cv-color-primary, #65d7ff)` | Color of the animated indicator arc |
43
+ | `--cv-spinner-speed` | `600ms` | Duration of one full rotation cycle |
44
+
45
+ ## Visual States
46
+
47
+ None. The spinner is always animating when rendered; there are no conditional visual states.
48
+
49
+ ## Events
50
+
51
+ None. The spinner is purely presentational and does not emit events.
52
+
53
+ ## Reactive State Mapping
54
+
55
+ `cv-spinner` is a visual adapter over headless `createSpinner`.
56
+
57
+ | UIKit Property | Direction | Headless Binding |
58
+ | -------------- | -------------- | ------------------------- |
59
+ | `label` | attr -> action | `actions.setLabel(value)` |
60
+
61
+ | Headless State | Direction | DOM Reflection |
62
+ | --------------- | ----------------- | -------------------------------- |
63
+ | `state.label()` | state -> contract | Consumed via `getSpinnerProps()` |
64
+
65
+ - `contracts.getSpinnerProps()` is spread onto the inner `[part="base"]` SVG element to apply `role="progressbar"` and `aria-label`.
66
+ - UIKit does not own ARIA semantics; headless state is the source of truth.
67
+ - `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, and `aria-valuetext` are never present (indeterminate mode only).
68
+
69
+ ## Usage
70
+
71
+ ```html
72
+ <!-- Default spinner -->
73
+ <cv-spinner></cv-spinner>
74
+
75
+ <!-- Custom accessible label -->
76
+ <cv-spinner label="Saving changes"></cv-spinner>
77
+
78
+ <!-- Sized via CSS font-size -->
79
+ <cv-spinner style="font-size: 2rem;"></cv-spinner>
80
+
81
+ <!-- Themed via custom properties -->
82
+ <cv-spinner style="--cv-spinner-indicator-color: #ff7d86; --cv-spinner-speed: 800ms;"></cv-spinner>
83
+ ```
@@ -0,0 +1,145 @@
1
+ # cv-switch
2
+
3
+ Toggle control that represents an on/off state, visually distinct from a checkbox.
4
+
5
+ **Headless:** [`createSwitch`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/switch.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-switch> (host)
11
+ └── <div part="base">
12
+ ├── <div part="control" role="switch">
13
+ │ ├── <span part="toggled" hidden>
14
+ │ │ └── <slot name="toggled">
15
+ │ ├── <span part="untoggled" hidden>
16
+ │ │ └── <slot name="untoggled">
17
+ │ └── <span part="thumb">
18
+ ├── <span part="label">
19
+ │ └── <slot>
20
+ └── <span part="help-text" id="{idBase}-help-text">
21
+ └── <slot name="help-text">
22
+ ```
23
+
24
+ When `checked` is `true`, the `toggled` wrapper is visible and `untoggled` is hidden. When `checked` is `false`, the opposite applies. Both wrappers are always in the DOM; visibility is toggled via CSS or the `hidden` attribute.
25
+
26
+ The `help-text` part is rendered only when the `help-text` attribute is set or the `help-text` slot is populated.
27
+
28
+ ## Attributes
29
+
30
+ | Attribute | Type | Default | Description |
31
+ | ----------- | ------- | ---------- | ------------------------------------------- |
32
+ | `checked` | Boolean | `false` | On/off state |
33
+ | `disabled` | Boolean | `false` | Prevents interaction |
34
+ | `size` | String | `"medium"` | Size: `small` \| `medium` \| `large` |
35
+ | `help-text` | String | `""` | Descriptive text displayed below the switch |
36
+
37
+ ## Sizes
38
+
39
+ | Size | `--cv-switch-width` | `--cv-switch-height` | `--cv-switch-thumb-size` |
40
+ | -------- | ------------------- | -------------------- | ------------------------ |
41
+ | `small` | `36px` | `20px` | `14px` |
42
+ | `medium` | `44px` | `24px` | `18px` |
43
+ | `large` | `52px` | `28px` | `22px` |
44
+
45
+ ## Slots
46
+
47
+ | Slot | Description |
48
+ | ----------- | ---------------------------------------------------------------------- |
49
+ | `(default)` | Label text displayed beside the switch |
50
+ | `toggled` | Content shown inside the track when checked (e.g., icon, text) |
51
+ | `untoggled` | Content shown inside the track when unchecked (e.g., icon, text) |
52
+ | `help-text` | Descriptive text below the switch; overrides the `help-text` attribute |
53
+
54
+ ## CSS Parts
55
+
56
+ | Part | Element | Description |
57
+ | ----------- | -------- | ------------------------------------------------------------ |
58
+ | `base` | `<div>` | Root layout wrapper (contains control + label + help-text) |
59
+ | `control` | `<div>` | Track/oval with `role="switch"` |
60
+ | `thumb` | `<span>` | Sliding knob inside the control |
61
+ | `toggled` | `<span>` | Wrapper around the `toggled` slot (visible when checked) |
62
+ | `untoggled` | `<span>` | Wrapper around the `untoggled` slot (visible when unchecked) |
63
+ | `label` | `<span>` | Wrapper around the default slot |
64
+ | `help-text` | `<span>` | Wrapper around the `help-text` slot or attribute text |
65
+
66
+ ## CSS Custom Properties
67
+
68
+ | Property | Default | Description |
69
+ | --------------------------------- | ------------------------------------- | --------------------------------------- |
70
+ | `--cv-switch-width` | `44px` | Inline size of the control track |
71
+ | `--cv-switch-height` | `24px` | Block size of the control track |
72
+ | `--cv-switch-thumb-size` | `18px` | Size of the thumb knob |
73
+ | `--cv-switch-gap` | `var(--cv-space-2, 8px)` | Spacing between control track and label |
74
+ | `--cv-switch-help-text-color` | `var(--cv-color-text-muted, #9aa6bf)` | Color of the help text |
75
+ | `--cv-switch-help-text-font-size` | `0.85em` | Font size of the help text |
76
+
77
+ ## Visual States
78
+
79
+ | Host selector | Description |
80
+ | ----------------------- | ------------------------------------------------------------------------------------------------------- |
81
+ | `:host([checked])` | Primary-tinted track, thumb translated to end position; `toggled` part visible, `untoggled` part hidden |
82
+ | `:host([disabled])` | Reduced opacity (`0.55`), `cursor: not-allowed` |
83
+ | `:host([size="small"])` | Small size overrides |
84
+ | `:host([size="large"])` | Large size overrides |
85
+
86
+ ## Reactive State Mapping
87
+
88
+ `cv-switch` is a visual adapter over headless `createSwitch`.
89
+
90
+ | UIKit Property | Direction | Headless Binding |
91
+ | -------------- | -------------- | --------------------------------------------------------------------------------------------------------------------- |
92
+ | `checked` | attr -> action | `actions.setOn(value)` |
93
+ | `disabled` | attr -> action | `actions.setDisabled(value)` |
94
+ | `help-text` | attr -> option | When present, generates an id for the help-text element and passes it as `ariaDescribedBy` in `createSwitch(options)` |
95
+
96
+ | Headless State | Direction | DOM Reflection |
97
+ | -------------------- | ------------- | --------------------------- |
98
+ | `state.isOn()` | state -> attr | `[checked]` host attribute |
99
+ | `state.isDisabled()` | state -> attr | `[disabled]` host attribute |
100
+
101
+ - `contracts.getSwitchProps()` is spread onto the inner `[part="control"]` element to apply `role`, `aria-checked`, `aria-disabled`, `tabindex`, and keyboard/click handlers.
102
+ - When help text is present (via attribute or slot), the component generates the id `{idBase}-help-text` for the help-text element and passes it as the `ariaDescribedBy` option to `createSwitch`, which produces the `aria-describedby` attribute in `getSwitchProps()`.
103
+ - UIKit dispatches `cv-input` and `cv-change` events by observing `isOn` changes triggered by user activation (not by controlled `setOn`).
104
+ - Toggled/untoggled slot visibility is purely visual (CSS-driven); no headless state or ARIA changes are involved.
105
+ - UIKit does not own toggle or keyboard logic; headless state is the source of truth.
106
+
107
+ ## Events
108
+
109
+ | Event | Detail | Description |
110
+ | ----------- | -------------------- | -------------------------------- |
111
+ | `cv-input` | `{checked: boolean}` | Fires on toggle interaction |
112
+ | `cv-change` | `{checked: boolean}` | Fires when checked state commits |
113
+
114
+ ## Usage
115
+
116
+ ```html
117
+ <cv-switch>Dark mode</cv-switch>
118
+
119
+ <cv-switch checked>Notifications</cv-switch>
120
+
121
+ <cv-switch disabled>Locked setting</cv-switch>
122
+
123
+ <cv-switch size="small">Compact</cv-switch>
124
+
125
+ <cv-switch size="large">Large toggle</cv-switch>
126
+
127
+ <cv-switch help-text="Reduces blue light emission after sunset"> Night mode </cv-switch>
128
+
129
+ <cv-switch>
130
+ Airplane mode
131
+ <span slot="help-text">Disables all wireless connections</span>
132
+ </cv-switch>
133
+
134
+ <cv-switch checked>
135
+ Wi-Fi
136
+ <cv-icon slot="toggled" name="wifi-on"></cv-icon>
137
+ <cv-icon slot="untoggled" name="wifi-off"></cv-icon>
138
+ </cv-switch>
139
+
140
+ <cv-switch>
141
+ Sound
142
+ <span slot="toggled">ON</span>
143
+ <span slot="untoggled">OFF</span>
144
+ </cv-switch>
145
+ ```