@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,268 @@
1
+ # cv-number
2
+
3
+ Numeric input field with optional stepper controls, clearable behavior, and form-field chrome (label, help-text, prefix/suffix slots).
4
+
5
+ **Headless:** [`createNumber`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/number.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-number> (host)
11
+ ├── <span part="form-control-label">
12
+ │ └── <slot name="label">
13
+ ├── <div part="base">
14
+ │ ├── <span part="prefix">
15
+ │ │ └── <slot name="prefix">
16
+ │ ├── <input part="input" role="spinbutton" inputmode="decimal">
17
+ │ ├── <span part="clear-button" role="button"> ← conditional on showClearButton
18
+ │ │ └── <slot name="clear-icon">×</slot>
19
+ │ ├── <span part="stepper"> ← conditional on stepper
20
+ │ │ ├── <button part="increment" type="button">
21
+ │ │ └── <button part="decrement" type="button">
22
+ │ └── <span part="suffix">
23
+ │ └── <slot name="suffix">
24
+ └── <span part="form-control-help-text">
25
+ └── <slot name="help-text">
26
+ ```
27
+
28
+ ## Attributes
29
+
30
+ | Attribute | Type | Default | Reflects | Description |
31
+ | ------------------ | ------- | ------------ | -------- | -------------------------------------------------------- |
32
+ | `value` | Number | `0` | no | Current numeric value |
33
+ | `default-value` | Number | `min ?? 0` | no | Value to reset to on clear |
34
+ | `min` | Number | — | no | Optional minimum boundary |
35
+ | `max` | Number | — | no | Optional maximum boundary |
36
+ | `step` | Number | `1` | no | Small increment/decrement step |
37
+ | `large-step` | Number | `10` | no | Large increment/decrement step (`PageUp`/`PageDown`) |
38
+ | `name` | String | `""` | no | Form field name for submit serialization |
39
+ | `disabled` | Boolean | `false` | yes | Prevents interaction and dims the component |
40
+ | `read-only` | Boolean | `false` | yes | Keeps focusable/announced but blocks user mutation |
41
+ | `required` | Boolean | `false` | yes | Marks the field as required for form validation |
42
+ | `clearable` | Boolean | `false` | yes | Shows a clear button when the value differs from default |
43
+ | `stepper` | Boolean | `false` | yes | Shows increment/decrement stepper buttons |
44
+ | `placeholder` | String | `""` | no | Placeholder text displayed when the input is empty |
45
+ | `size` | String | `"medium"` | yes | Component size: `small` \| `medium` \| `large` |
46
+ | `variant` | String | `"outlined"` | yes | Visual variant: `outlined` \| `filled` |
47
+ | `aria-label` | String | `""` | no | Accessible label |
48
+ | `aria-labelledby` | String | `""` | no | ID reference to visible label |
49
+ | `aria-describedby` | String | `""` | no | ID reference to description |
50
+
51
+ ## Variants
52
+
53
+ | Variant | Description |
54
+ | ---------- | ------------------------------------------------------------ |
55
+ | `outlined` | Default style with visible border and transparent background |
56
+ | `filled` | Subtle background fill with no visible border |
57
+
58
+ ## Sizes
59
+
60
+ | Size | `--cv-number-height` | `--cv-number-padding-inline` | `--cv-number-font-size` |
61
+ | -------- | -------------------- | ---------------------------- | -------------------------------- |
62
+ | `small` | `30px` | `var(--cv-space-2, 8px)` | `var(--cv-font-size-sm, 13px)` |
63
+ | `medium` | `36px` | `var(--cv-space-3, 12px)` | `var(--cv-font-size-base, 14px)` |
64
+ | `large` | `42px` | `var(--cv-space-4, 16px)` | `var(--cv-font-size-md, 16px)` |
65
+
66
+ ## Slots
67
+
68
+ | Slot | Description |
69
+ | ------------ | -------------------------------------------------------------- |
70
+ | `prefix` | Content rendered before the input (e.g., currency symbol icon) |
71
+ | `suffix` | Content rendered after the stepper controls (e.g., unit label) |
72
+ | `clear-icon` | Custom icon for the clear button (default: `×`) |
73
+ | `label` | Label text displayed above the input |
74
+ | `help-text` | Help or description text displayed below the input |
75
+
76
+ > **Note:** The native `<input>` element is not slottable. There is no default slot.
77
+
78
+ ## CSS Parts
79
+
80
+ | Part | Element | Description |
81
+ | ------------------------ | ---------- | ------------------------------------------------------------------ |
82
+ | `base` | `<div>` | Outermost wrapper element containing input and controls |
83
+ | `input` | `<input>` | The native input element with `role="spinbutton"` |
84
+ | `prefix` | `<span>` | Wrapper around the `prefix` slot |
85
+ | `suffix` | `<span>` | Wrapper around the `suffix` slot |
86
+ | `clear-button` | `<span>` | The clear button wrapper (conditionally visible) |
87
+ | `stepper` | `<span>` | Wrapper around increment/decrement buttons (conditionally visible) |
88
+ | `increment` | `<button>` | Increment stepper button |
89
+ | `decrement` | `<button>` | Decrement stepper button |
90
+ | `form-control-label` | `<span>` | Wrapper around the `label` slot |
91
+ | `form-control-help-text` | `<span>` | Wrapper around the `help-text` slot |
92
+
93
+ ## CSS Custom Properties
94
+
95
+ | Property | Default | Description |
96
+ | --------------------------------- | -------------------------------------------- | --------------------------------------------------------------- |
97
+ | `--cv-number-height` | `36px` | Component block size |
98
+ | `--cv-number-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding inside the input container |
99
+ | `--cv-number-font-size` | `var(--cv-font-size-base, 14px)` | Font size of the input text |
100
+ | `--cv-number-border-radius` | `var(--cv-radius-sm, 6px)` | Border radius of the input container |
101
+ | `--cv-number-border-color` | `var(--cv-color-border, #2a3245)` | Border color in default state |
102
+ | `--cv-number-background` | `transparent` | Background color of the input container |
103
+ | `--cv-number-color` | `var(--cv-color-text, #e8ecf6)` | Text color of the input value |
104
+ | `--cv-number-placeholder-color` | `var(--cv-color-text-muted, #6b7a99)` | Placeholder text color |
105
+ | `--cv-number-focus-ring` | `0 0 0 2px var(--cv-color-primary, #65d7ff)` | Box-shadow applied on focus |
106
+ | `--cv-number-icon-size` | `1em` | Size of prefix/suffix/clear icons |
107
+ | `--cv-number-gap` | `var(--cv-space-2, 8px)` | Spacing between inner elements (prefix, input, buttons, suffix) |
108
+ | `--cv-number-transition-duration` | `var(--cv-duration-fast, 120ms)` | Transition duration for state changes |
109
+ | `--cv-number-stepper-width` | `24px` | Width of each stepper button |
110
+
111
+ Additionally, component styles depend on theme tokens through fallback values:
112
+
113
+ | Theme Property | Default | Description |
114
+ | ----------------------------- | --------- | --------------------------------------------------- |
115
+ | `--cv-color-border` | `#2a3245` | Base border color |
116
+ | `--cv-color-surface` | `#141923` | Surface background color (used by `filled` variant) |
117
+ | `--cv-color-surface-elevated` | `#1d2432` | Stepper button background |
118
+ | `--cv-color-text` | `#e8ecf6` | Default text color |
119
+ | `--cv-color-text-muted` | `#6b7a99` | Muted text color for placeholder |
120
+ | `--cv-color-primary` | `#65d7ff` | Primary accent color for focus ring |
121
+ | `--cv-duration-fast` | `120ms` | Transition duration |
122
+ | `--cv-easing-standard` | `ease` | Transition timing function |
123
+ | `--cv-radius-sm` | `6px` | Base border radius fallback |
124
+ | `--cv-font-size-sm` | `13px` | Small font size |
125
+ | `--cv-font-size-base` | `14px` | Base font size |
126
+ | `--cv-font-size-md` | `16px` | Medium font size |
127
+ | `--cv-space-2` | `8px` | Spacing scale: small |
128
+ | `--cv-space-3` | `12px` | Spacing scale: medium |
129
+ | `--cv-space-4` | `16px` | Spacing scale: large |
130
+
131
+ ## Visual States
132
+
133
+ | Host selector | Description |
134
+ | ----------------------------- | --------------------------------------------------------------------------- |
135
+ | `:host([disabled])` | Reduced opacity (`0.55`), `cursor: not-allowed`, no interaction |
136
+ | `:host([read-only])` | Normal opacity, `cursor: default`, input text not editable |
137
+ | `:host([required])` | No visual change by default (can be styled via part selectors) |
138
+ | `:host([focused])` | Focus ring applied via `--cv-number-focus-ring` |
139
+ | `:host([filled])` | Indicates value differs from default (e.g., for floating label transitions) |
140
+ | `:host([clearable])` | Clear button space reserved in layout |
141
+ | `:host([stepper])` | Stepper buttons rendered and visible |
142
+ | `:host([size="small"])` | Small size overrides |
143
+ | `:host([size="large"])` | Large size overrides |
144
+ | `:host([variant="outlined"])` | Visible border, transparent background |
145
+ | `:host([variant="filled"])` | Subtle background (`--cv-color-surface`), no visible border |
146
+
147
+ ## Reactive State Mapping
148
+
149
+ `cv-number` is a visual adapter over headless `createNumber`.
150
+
151
+ ### UIKit properties to headless actions
152
+
153
+ | UIKit Property | Direction | Headless Binding |
154
+ | ------------------ | ------------------- | ------------------------------------------------------ |
155
+ | `value` | attr/prop -> action | `actions.setValue(value)` |
156
+ | `disabled` | attr -> action | `actions.setDisabled(value)` |
157
+ | `read-only` | attr -> action | `actions.setReadOnly(value)` |
158
+ | `required` | attr -> action | `actions.setRequired(value)` |
159
+ | `placeholder` | attr -> action | `actions.setPlaceholder(value)` |
160
+ | `clearable` | attr -> action | `actions.setClearable(value)` |
161
+ | `stepper` | attr -> action | `actions.setStepper(value)` |
162
+ | `min` | attr -> option | passed to `createNumber(options)` |
163
+ | `max` | attr -> option | passed to `createNumber(options)` |
164
+ | `step` | attr -> option | passed to `createNumber(options)` |
165
+ | `large-step` | attr -> option | passed to `createNumber(options)` |
166
+ | `default-value` | attr -> option | passed as `defaultValue` to `createNumber(options)` |
167
+ | `aria-label` | attr -> option | passed as `ariaLabel` to `createNumber(options)` |
168
+ | `aria-labelledby` | attr -> option | passed as `ariaLabelledBy` to `createNumber(options)` |
169
+ | `aria-describedby` | attr -> option | passed as `ariaDescribedBy` to `createNumber(options)` |
170
+
171
+ ### Headless state to DOM reflection
172
+
173
+ | Headless State | Direction | DOM Reflection |
174
+ | ----------------------------------- | ------------- | ------------------------------------------------------------------------------------ |
175
+ | `state.isDisabled()` | state -> attr | `[disabled]` host attribute |
176
+ | `state.isReadOnly()` | state -> attr | `[read-only]` host attribute |
177
+ | `state.required()` | state -> attr | `[required]` host attribute |
178
+ | `state.focused()` | state -> attr | `[focused]` host attribute |
179
+ | `state.filled()` | state -> attr | `[filled]` host attribute |
180
+ | `state.showClearButton()` | state -> DOM | shows/hides the clear button element |
181
+ | `state.stepper()` | state -> DOM | shows/hides the stepper buttons |
182
+ | `state.draftText()` | state -> DOM | when non-null, displayed in the input; when null, displays formatted `String(value)` |
183
+ | `state.value()` | state -> DOM | displayed in the input when `draftText` is null |
184
+ | `state.placeholder()` | state -> DOM | applied as placeholder on the native input |
185
+ | `state.hasMin()` / `state.hasMax()` | state -> DOM | for conditional styling or rendering hints |
186
+
187
+ ### Contract props spreading
188
+
189
+ - `contracts.getInputProps()` is spread onto the `[part="input"]` native `<input>` element to apply `id`, `role`, `tabindex`, `inputmode`, `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, `aria-valuetext`, `aria-disabled`, `aria-readonly`, `aria-required`, `aria-label`, `aria-labelledby`, `aria-describedby`, `placeholder`, and `autocomplete`.
190
+ - `contracts.getIncrementButtonProps()` is spread onto the `[part="increment"]` button to apply `id`, `tabindex`, `aria-label`, `aria-disabled`, `hidden`, `aria-hidden`, and `onClick`.
191
+ - `contracts.getDecrementButtonProps()` is spread onto the `[part="decrement"]` button to apply `id`, `tabindex`, `aria-label`, `aria-disabled`, `hidden`, `aria-hidden`, and `onClick`.
192
+ - `contracts.getClearButtonProps()` is spread onto the `[part="clear-button"]` element to apply `role`, `aria-label`, `tabindex`, `hidden`, `aria-hidden`, and `onClick`.
193
+
194
+ ### Event wiring
195
+
196
+ - Native `<input>` `input` event -> `actions.handleInput(e.target.value)` (updates draft text)
197
+ - Native `<input>` `keydown` event -> `actions.handleKeyDown(e)` (handles ArrowUp/Down, PageUp/Down, Home/End, Enter, Escape)
198
+ - Native `<input>` `focus` event -> `actions.setFocused(true)` -> dispatches `cv-focus` CustomEvent
199
+ - Native `<input>` `blur` event -> `actions.setFocused(false)` (triggers draft commit) -> dispatches `cv-blur` CustomEvent; if value changed since focus, dispatches `cv-change` CustomEvent
200
+ - Clear button `click` -> `actions.clear()` -> dispatches `cv-clear` CustomEvent
201
+ - Increment button `click` -> `actions.increment()` -> dispatches `cv-change` CustomEvent
202
+ - Decrement button `click` -> `actions.decrement()` -> dispatches `cv-change` CustomEvent
203
+
204
+ ### Input display logic (UIKit responsibility)
205
+
206
+ UIKit reads `state.draftText()` and `state.value()` to determine what to display in the native `<input>`:
207
+
208
+ - When `draftText !== null`: display `draftText` (user is actively editing)
209
+ - When `draftText === null`: display formatted `String(value)` (committed state)
210
+
211
+ UIKit does not own value management, clamping, snapping, draft commit logic, or ARIA computation; headless state is the source of truth.
212
+
213
+ ## Events
214
+
215
+ | Event | Detail | Description |
216
+ | ----------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
217
+ | `cv-change` | `{ value: number }` | Fires on committed value change from user interaction (stepper click, keyboard step, draft commit on blur/Enter). Does not fire from programmatic `setValue`. |
218
+ | `cv-clear` | `{ }` | Fires when the value is cleared via the clear button or `Escape` key |
219
+ | `cv-focus` | `{ }` | Fires when the input receives focus |
220
+ | `cv-blur` | `{ }` | Fires when the input loses focus |
221
+
222
+ ## Usage
223
+
224
+ ```html
225
+ <!-- Basic number input -->
226
+ <cv-number value="0" placeholder="Enter a number"></cv-number>
227
+
228
+ <!-- With label and help text -->
229
+ <cv-number>
230
+ <span slot="label">Quantity</span>
231
+ <span slot="help-text">Enter a value between 1 and 100</span>
232
+ </cv-number>
233
+
234
+ <!-- With min, max, and step -->
235
+ <cv-number value="5" min="0" max="100" step="5" large-step="25"></cv-number>
236
+
237
+ <!-- Stepper buttons visible -->
238
+ <cv-number value="1" min="0" max="10" stepper></cv-number>
239
+
240
+ <!-- Clearable -->
241
+ <cv-number value="42" clearable></cv-number>
242
+
243
+ <!-- With prefix (currency symbol) and suffix (unit) -->
244
+ <cv-number>
245
+ <span slot="prefix">$</span>
246
+ <span slot="suffix">.00</span>
247
+ </cv-number>
248
+
249
+ <!-- Filled variant, small size -->
250
+ <cv-number variant="filled" size="small" placeholder="0"></cv-number>
251
+
252
+ <!-- Large size, outlined variant with stepper and clearable -->
253
+ <cv-number size="large" stepper clearable value="10" min="0" max="99"></cv-number>
254
+
255
+ <!-- Disabled -->
256
+ <cv-number disabled value="50"></cv-number>
257
+
258
+ <!-- Read-only -->
259
+ <cv-number read-only value="100"></cv-number>
260
+
261
+ <!-- Required with label -->
262
+ <cv-number required>
263
+ <span slot="label">Age</span>
264
+ </cv-number>
265
+
266
+ <!-- With accessible label -->
267
+ <cv-number aria-label="Quantity" value="1" min="1" max="99" stepper></cv-number>
268
+ ```
@@ -0,0 +1,167 @@
1
+ # cv-option
2
+
3
+ Individual selectable option for use as a direct child of `cv-listbox` or `cv-listbox-group`. All ARIA attributes are managed exclusively by the parent `cv-listbox` via headless `contracts.getOptionProps(id)`; `cv-option` itself sets no ARIA.
4
+
5
+ > **Note:** `cv-option` has no headless module of its own. It is a purely presentational element. See the [Parent-managed ARIA Contract](#parent-managed-aria-contract) section for the full attribute contract imposed by the parent.
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-option> (host)
11
+ └── <div part="base">
12
+ ├── <span part="prefix">
13
+ │ └── <slot name="prefix">
14
+ ├── <span part="label">
15
+ │ └── <slot>
16
+ └── <span part="suffix">
17
+ └── <slot name="suffix">
18
+ ```
19
+
20
+ ## Attributes
21
+
22
+ | Attribute | Type | Default | Description |
23
+ | ---------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
24
+ | `value` | String | `""` | Unique identifier for this option. The parent `cv-listbox` reads this to register the option in the headless model. Auto-generated as `option-{n}` if omitted. |
25
+ | `disabled` | Boolean | `false` | Whether the option is disabled. Prevents selection and keyboard activation. Also reflected as `aria-disabled` by the parent via `getOptionProps`. |
26
+ | `selected` | Boolean | `false` | Whether the option is currently selected. Set by the parent `cv-listbox` as part of state synchronisation. |
27
+ | `active` | Boolean | `false` | Whether the option is the active (highlighted) option. Set by the parent `cv-listbox` as part of state synchronisation. |
28
+
29
+ ## Slots
30
+
31
+ | Slot | Description |
32
+ | ----------- | -------------------------------------------------------------------------------------------------- |
33
+ | `(default)` | Option label text. Also used as the source text for typeahead matching by the parent `cv-listbox`. |
34
+ | `prefix` | Icon or element placed before the label. |
35
+ | `suffix` | Icon or element placed after the label. |
36
+
37
+ ## CSS Parts
38
+
39
+ | Part | Element | Description |
40
+ | -------- | -------- | ----------------------------------------------------------------- |
41
+ | `base` | `<div>` | Root wrapper; receives layout, background, and transition styles. |
42
+ | `label` | `<span>` | Wrapper around the default slot (label text). |
43
+ | `prefix` | `<span>` | Wrapper around the `prefix` named slot. |
44
+ | `suffix` | `<span>` | Wrapper around the `suffix` named slot. |
45
+
46
+ ## CSS Custom Properties
47
+
48
+ | Property | Default | Description |
49
+ | --------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------- |
50
+ | `--cv-option-padding-block` | `var(--cv-space-2, 8px)` | Vertical padding inside `[part="base"]`. |
51
+ | `--cv-option-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding inside `[part="base"]`. |
52
+ | `--cv-option-border-radius` | `var(--cv-radius-sm, 6px)` | Border radius of `[part="base"]`. |
53
+ | `--cv-option-active-background` | `color-mix(in oklab, var(--cv-color-primary, #65d7ff) 22%, transparent)` | Background applied when the option is active (highlighted). |
54
+ | `--cv-option-selected-background` | `color-mix(in oklab, var(--cv-color-primary, #65d7ff) 34%, transparent)` | Background applied when the option is selected. |
55
+ | `--cv-option-disabled-opacity` | `0.55` | Opacity applied when the option is disabled. |
56
+ | `--cv-option-focus-outline-color` | `var(--cv-color-primary, #65d7ff)` | Outline color for `:focus-visible` (roving-tabindex focus strategy). |
57
+
58
+ Additionally, component styles depend on theme tokens through fallback values:
59
+
60
+ | Theme Property | Default | Description |
61
+ | ---------------------- | --------- | ---------------------------------------------------------------------------- |
62
+ | `--cv-color-text` | `#e8ecf6` | Default text color. |
63
+ | `--cv-color-primary` | `#65d7ff` | Primary accent color used for active/selected backgrounds and focus outline. |
64
+ | `--cv-duration-fast` | `120ms` | Background and color transition duration. |
65
+ | `--cv-easing-standard` | `ease` | Transition timing function. |
66
+ | `--cv-space-2` | `8px` | Fallback for vertical padding. |
67
+ | `--cv-space-3` | `12px` | Fallback for horizontal padding. |
68
+ | `--cv-radius-sm` | `6px` | Fallback for border radius. |
69
+
70
+ ## Visual States
71
+
72
+ | Host selector | Description |
73
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
74
+ | `:host([active])` | Active/highlighted option; applies `--cv-option-active-background` to `[part="base"]`. |
75
+ | `:host([selected])` | Selected option; applies `--cv-option-selected-background` to `[part="base"]`. |
76
+ | `:host([disabled])` | Disabled option; applies `--cv-option-disabled-opacity` to `[part="base"]`. |
77
+ | `:host([hidden])` | Hidden option; `display: none` on the host. |
78
+ | `:host(:focus-visible)` | Focus ring when the option receives DOM focus (roving-tabindex strategy); 2px solid outline using `--cv-option-focus-outline-color`. |
79
+
80
+ ## Events
81
+
82
+ `cv-option` emits no events. All interaction events (`input`, `change`) are dispatched by the parent `cv-listbox`.
83
+
84
+ ## Parent-managed ARIA Contract
85
+
86
+ `cv-option` sets no ARIA attributes itself. The parent `cv-listbox` spreads `contracts.getOptionProps(id)` directly onto each `cv-option` host element. The following attributes are applied by the parent:
87
+
88
+ | Attribute | Source | Description |
89
+ | --------------- | -------------------- | --------------------------------------------------------------------------------------------------------- |
90
+ | `id` | `getOptionProps(id)` | Unique DOM id used by `aria-activedescendant` on the listbox root. |
91
+ | `role` | `getOptionProps(id)` | Always `"option"`. |
92
+ | `tabindex` | `getOptionProps(id)` | `"0"` for the active option (roving-tabindex) or `"-1"` for all options (aria-activedescendant strategy). |
93
+ | `aria-selected` | `getOptionProps(id)` | `"true"` when the option is selected; `"false"` otherwise. |
94
+ | `aria-disabled` | `getOptionProps(id)` | `"true"` when `[disabled]` is present; omitted otherwise. |
95
+ | `aria-setsize` | `getOptionProps(id)` | Total number of options in the listbox (supports virtual scrolling). |
96
+ | `aria-posinset` | `getOptionProps(id)` | 1-based position of this option within the full option list. |
97
+ | `data-active` | `getOptionProps(id)` | Present when the option is the active option; used as a CSS hook. |
98
+
99
+ These attributes must not be set directly on `cv-option` by the consumer. They are owned entirely by the parent and will be overwritten on each render cycle.
100
+
101
+ ## Usage
102
+
103
+ ```html
104
+ <!-- Basic usage inside a listbox -->
105
+ <cv-listbox aria-label="Fruits">
106
+ <cv-option value="apple">Apple</cv-option>
107
+ <cv-option value="banana">Banana</cv-option>
108
+ <cv-option value="cherry" disabled>Cherry (unavailable)</cv-option>
109
+ </cv-listbox>
110
+
111
+ <!-- With prefix icon -->
112
+ <cv-listbox aria-label="Connections">
113
+ <cv-option value="wifi">
114
+ <icon-wifi slot="prefix"></icon-wifi>
115
+ Wi-Fi
116
+ </cv-option>
117
+ <cv-option value="ethernet">
118
+ <icon-ethernet slot="prefix"></icon-ethernet>
119
+ Ethernet
120
+ </cv-option>
121
+ </cv-listbox>
122
+
123
+ <!-- With suffix badge -->
124
+ <cv-listbox aria-label="Plans">
125
+ <cv-option value="free">
126
+ Free
127
+ <cv-badge slot="suffix">current</cv-badge>
128
+ </cv-option>
129
+ <cv-option value="pro">
130
+ Pro
131
+ <cv-badge slot="suffix" variant="primary">upgrade</cv-badge>
132
+ </cv-option>
133
+ </cv-listbox>
134
+
135
+ <!-- With both prefix and suffix -->
136
+ <cv-listbox aria-label="Files">
137
+ <cv-option value="doc">
138
+ <icon-file slot="prefix"></icon-file>
139
+ document.pdf
140
+ <span slot="suffix">12 KB</span>
141
+ </cv-option>
142
+ <cv-option value="img">
143
+ <icon-image slot="prefix"></icon-image>
144
+ photo.png
145
+ <span slot="suffix">4.2 MB</span>
146
+ </cv-option>
147
+ </cv-listbox>
148
+
149
+ <!-- Pre-selected option -->
150
+ <cv-listbox aria-label="Theme">
151
+ <cv-option value="light">Light</cv-option>
152
+ <cv-option value="dark" selected>Dark</cv-option>
153
+ <cv-option value="system">System</cv-option>
154
+ </cv-listbox>
155
+
156
+ <!-- Inside a group -->
157
+ <cv-listbox aria-label="City">
158
+ <cv-listbox-group label="North America">
159
+ <cv-option value="nyc">New York</cv-option>
160
+ <cv-option value="la">Los Angeles</cv-option>
161
+ </cv-listbox-group>
162
+ <cv-listbox-group label="Europe">
163
+ <cv-option value="lon">London</cv-option>
164
+ <cv-option value="par">Paris</cv-option>
165
+ </cv-listbox-group>
166
+ </cv-listbox>
167
+ ```