@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,508 @@
1
+ # cv-menu
2
+
3
+ Menu panel that displays a list of actionable items, supporting checkable items (checkbox/radio), submenus, groups, and typeahead navigation.
4
+
5
+ **Headless:** [`createMenu`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/menu.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-menu> (host)
11
+ └── <div part="base" role="menu">
12
+ └── <slot> ← cv-menu-item / cv-menu-group children
13
+ ```
14
+
15
+ ## Attributes
16
+
17
+ | Attribute | Type | Default | Description |
18
+ | ----------------- | ------- | ------- | ------------------------------------------------------------------------------------ |
19
+ | `value` | String | `""` | Last selected item value |
20
+ | `open` | Boolean | `false` | Whether the menu panel is visible |
21
+ | `close-on-select` | Boolean | `true` | Close the menu after an item is selected (overridden to `false` for checkable items) |
22
+ | `aria-label` | String | `""` | Accessible label for the menu |
23
+
24
+ ## Slots
25
+
26
+ | Slot | Description |
27
+ | ----------- | ------------------------------------------- |
28
+ | `(default)` | `cv-menu-item` and `cv-menu-group` children |
29
+
30
+ ## CSS Parts
31
+
32
+ | Part | Element | Description |
33
+ | ------ | ------- | -------------------------------------- |
34
+ | `base` | `<div>` | Root menu container with `role="menu"` |
35
+
36
+ ## CSS Custom Properties
37
+
38
+ | Property | Default | Description |
39
+ | --------------------------- | --------------------------------------------------- | ----------------------------------------------------- |
40
+ | `--cv-menu-padding` | `var(--cv-space-1, 4px)` | Padding inside the menu |
41
+ | `--cv-menu-gap` | `var(--cv-space-1, 4px)` | Gap between menu items |
42
+ | `--cv-menu-border-radius` | `var(--cv-radius-md, 10px)` | Border radius of the menu |
43
+ | `--cv-menu-background` | `var(--cv-color-surface-elevated, #1d2432)` | Background color of the menu |
44
+ | `--cv-menu-border-color` | `var(--cv-color-border, #2a3245)` | Border color of the menu |
45
+ | `--cv-menu-shadow` | `var(--cv-shadow-1, 0 2px 8px rgba(0, 0, 0, 0.24))` | Box shadow of the menu |
46
+ | `--cv-menu-max-height` | `none` | Maximum height of the menu (scrollable when exceeded) |
47
+ | `--cv-menu-min-inline-size` | `180px` | Minimum inline size of the menu |
48
+
49
+ ## Visual States
50
+
51
+ | Host selector | Description |
52
+ | --------------------- | --------------------- |
53
+ | `:host([open])` | Menu panel is visible |
54
+ | `:host(:not([open]))` | Menu panel is hidden |
55
+
56
+ ## Events
57
+
58
+ | Event | Detail | Description |
59
+ | ----------- | ------------------------- | --------------------------------------------------- |
60
+ | `cv-input` | `{value, activeId, open}` | Fires on any state change (selection, active, open) |
61
+ | `cv-change` | `{value, activeId, open}` | Fires only when the selected `value` changes |
62
+
63
+ Event detail type:
64
+
65
+ ```ts
66
+ interface CVMenuEventDetail {
67
+ value: string | null
68
+ activeId: string | null
69
+ open: boolean
70
+ }
71
+ ```
72
+
73
+ ## Reactive State Mapping
74
+
75
+ `cv-menu` is a visual adapter over headless `createMenu`.
76
+
77
+ | UIKit Property | Direction | Headless Binding |
78
+ | ----------------- | -------------- | ------------------------------------------------------------ |
79
+ | `value` | attr -> action | `actions.select(value)` when value changes |
80
+ | `open` | attr -> action | `actions.open()` when `true`; `actions.close()` when `false` |
81
+ | `close-on-select` | attr -> option | passed as `closeOnSelect` in `createMenu(options)` |
82
+ | `aria-label` | attr -> option | passed as `ariaLabel` in `createMenu(options)` |
83
+
84
+ | Headless State | Direction | DOM Reflection |
85
+ | ------------------------- | ------------- | -------------------------------------------------- |
86
+ | `state.isOpen()` | state -> attr | `[open]` host attribute, menu `[hidden]` |
87
+ | `state.activeId()` | state -> DOM | `[data-active]` on item elements, focus management |
88
+ | `state.selectedId()` | state -> attr | `[value]` host attribute |
89
+ | `state.checkedIds()` | state -> DOM | `[aria-checked]` on checkbox/radio item elements |
90
+ | `state.openSubmenuId()` | state -> DOM | submenu container `[hidden]` state |
91
+ | `state.submenuActiveId()` | state -> DOM | `[data-active]` on submenu child items |
92
+
93
+ Contracts applied to DOM elements:
94
+
95
+ - `contracts.getMenuProps()` -> menu container (`[part="base"]`): provides `id`, `role`, `tabindex`, `aria-label`, `aria-activedescendant`
96
+ - `contracts.getItemProps(id)` -> each item element: provides `id`, `role`, `tabindex`, `aria-disabled`, `data-active`, `aria-checked`, `aria-haspopup`, `aria-expanded`
97
+ - `contracts.getGroupProps(groupId)` -> group container elements: provides `id`, `role`, `aria-label`
98
+ - `contracts.getSubmenuProps(parentItemId)` -> submenu containers: provides `id`, `role`, `tabindex`, `hidden`
99
+ - `contracts.getSubmenuItemProps(parentItemId, childId)` -> submenu item elements: provides `id`, `role`, `tabindex`, `aria-disabled`, `data-active`, `aria-checked`
100
+
101
+ UIKit does not own activation, navigation, check toggle, or submenu logic; headless state is the source of truth.
102
+
103
+ ## Usage
104
+
105
+ ```html
106
+ <!-- Basic menu -->
107
+ <cv-menu open aria-label="Actions">
108
+ <cv-menu-item value="cut">Cut</cv-menu-item>
109
+ <cv-menu-item value="copy">Copy</cv-menu-item>
110
+ <cv-menu-item value="paste">Paste</cv-menu-item>
111
+ </cv-menu>
112
+
113
+ <!-- With disabled item -->
114
+ <cv-menu open aria-label="Edit">
115
+ <cv-menu-item value="undo">Undo</cv-menu-item>
116
+ <cv-menu-item value="redo" disabled>Redo</cv-menu-item>
117
+ </cv-menu>
118
+
119
+ <!-- With checkbox group -->
120
+ <cv-menu open aria-label="View options">
121
+ <cv-menu-group type="checkbox" label="Panels">
122
+ <cv-menu-item value="toolbar" checked>Toolbar</cv-menu-item>
123
+ <cv-menu-item value="sidebar">Sidebar</cv-menu-item>
124
+ <cv-menu-item value="statusbar" checked>Status Bar</cv-menu-item>
125
+ </cv-menu-group>
126
+ </cv-menu>
127
+
128
+ <!-- With radio group -->
129
+ <cv-menu open aria-label="Sort order">
130
+ <cv-menu-group type="radio" label="Sort by">
131
+ <cv-menu-item value="name" checked>Name</cv-menu-item>
132
+ <cv-menu-item value="date">Date</cv-menu-item>
133
+ <cv-menu-item value="size">Size</cv-menu-item>
134
+ </cv-menu-group>
135
+ </cv-menu>
136
+
137
+ <!-- With submenu -->
138
+ <cv-menu open aria-label="File">
139
+ <cv-menu-item value="new">New</cv-menu-item>
140
+ <cv-menu-item value="share">
141
+ Share
142
+ <cv-menu slot="submenu">
143
+ <cv-menu-item value="email">Email</cv-menu-item>
144
+ <cv-menu-item value="link">Copy Link</cv-menu-item>
145
+ </cv-menu>
146
+ </cv-menu-item>
147
+ </cv-menu>
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Child Elements
153
+
154
+ ### cv-menu-item
155
+
156
+ Actionable item within a menu. Supports standard, checkbox, and radio types, as well as hosting a submenu.
157
+
158
+ #### Anatomy
159
+
160
+ ```
161
+ <cv-menu-item> (host)
162
+ └── <div part="base" class="item">
163
+ ├── <span part="checkmark"> ← only for checkbox/radio items
164
+ ├── <span part="prefix">
165
+ │ └── <slot name="prefix">
166
+ ├── <span part="label">
167
+ │ └── <slot>
168
+ ├── <span part="suffix">
169
+ │ └── <slot name="suffix">
170
+ └── <span part="submenu-icon"> ← only when item has submenu
171
+ ```
172
+
173
+ #### Attributes
174
+
175
+ | Attribute | Type | Default | Description |
176
+ | ---------- | ------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
177
+ | `value` | String | `""` | Identifier for the item (used as selection value and typeahead matching) |
178
+ | `disabled` | Boolean | `false` | Prevents selection and skips during navigation |
179
+ | `type` | String | `"normal"` | Item type: `normal` \| `checkbox` \| `radio` (inherited from parent `cv-menu-group` when not explicitly set) |
180
+ | `checked` | Boolean | `false` | Checked state for checkbox/radio items |
181
+ | `active` | Boolean | `false` | Reflects keyboard-active (highlighted) state (managed by parent) |
182
+ | `selected` | Boolean | `false` | Reflects whether this item is the last selected value (managed by parent) |
183
+ | `label` | String | `""` | Explicit label for typeahead matching (defaults to text content if not set) |
184
+
185
+ #### Slots
186
+
187
+ | Slot | Description |
188
+ | ----------- | -------------------------------------------------------------- |
189
+ | `(default)` | Item label text |
190
+ | `prefix` | Icon or element before the label |
191
+ | `suffix` | Icon or element after the label (e.g., keyboard shortcut text) |
192
+ | `submenu` | Nested `cv-menu` for submenu content |
193
+
194
+ #### CSS Parts
195
+
196
+ | Part | Element | Description |
197
+ | -------------- | -------- | --------------------------------------------------------------------------------------------- |
198
+ | `base` | `<div>` | Item root wrapper |
199
+ | `checkmark` | `<span>` | Check indicator for checkbox/radio items (rendered only when `type` is `checkbox` or `radio`) |
200
+ | `prefix` | `<span>` | Wrapper around the `prefix` slot |
201
+ | `label` | `<span>` | Wrapper around the default slot |
202
+ | `suffix` | `<span>` | Wrapper around the `suffix` slot |
203
+ | `submenu-icon` | `<span>` | Arrow indicator for items with submenu (rendered only when submenu slot is populated) |
204
+
205
+ #### CSS Custom Properties
206
+
207
+ | Property | Default | Description |
208
+ | ------------------------------- | -------------------------- | --------------------------------------------------------------------------- |
209
+ | `--cv-menu-item-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding of the item |
210
+ | `--cv-menu-item-padding-block` | `var(--cv-space-2, 8px)` | Vertical padding of the item |
211
+ | `--cv-menu-item-border-radius` | `var(--cv-radius-sm, 6px)` | Border radius of the item |
212
+ | `--cv-menu-item-gap` | `var(--cv-space-2, 8px)` | Gap between internal parts (checkmark, prefix, label, suffix, submenu-icon) |
213
+
214
+ #### Visual States
215
+
216
+ | Host selector | Description |
217
+ | ---------------------- | ----------------------------------------------------- |
218
+ | `:host([active])` | Item has keyboard focus (primary tint at 24%) |
219
+ | `:host([selected])` | Item is the last selected value (primary tint at 32%) |
220
+ | `:host([disabled])` | Item is non-selectable (opacity 0.5) |
221
+ | `:host([hidden])` | Item is hidden when menu is closed |
222
+ | `:host([checked])` | Checkbox/radio item is checked (checkmark visible) |
223
+ | `:host([has-submenu])` | Item hosts a submenu (submenu-icon visible) |
224
+
225
+ #### ARIA Contract
226
+
227
+ | Item type | `role` | Additional attributes |
228
+ | ------------------ | --------------------- | --------------------------------------------------------------- |
229
+ | `normal` (default) | `menuitem` | `tabindex="-1"`, `aria-disabled` (when disabled), `data-active` |
230
+ | `checkbox` | `menuitemcheckbox` | `tabindex="-1"`, `aria-disabled`, `data-active`, `aria-checked` |
231
+ | `radio` | `menuitemradio` | `tabindex="-1"`, `aria-disabled`, `data-active`, `aria-checked` |
232
+ | any with submenu | adds to existing role | `aria-haspopup="menu"`, `aria-expanded` |
233
+
234
+ ---
235
+
236
+ ### cv-menu-group
237
+
238
+ Groups related menu items under a label. Children inherit the `type` attribute for checkbox/radio behavior.
239
+
240
+ #### Anatomy
241
+
242
+ ```
243
+ <cv-menu-group> (host)
244
+ ├── <div part="label" role="presentation"> ← from label attribute or label slot
245
+ └── <div part="base" role="group">
246
+ └── <slot> ← cv-menu-item children
247
+ ```
248
+
249
+ #### Attributes
250
+
251
+ | Attribute | Type | Default | Description |
252
+ | --------- | ------ | ------- | ------------------------------------------------------------------- |
253
+ | `type` | String | `""` | Checkable type propagated to children: `checkbox` \| `radio` |
254
+ | `label` | String | `""` | Group accessible name (used as `aria-label` on the group container) |
255
+
256
+ #### Slots
257
+
258
+ | Slot | Description |
259
+ | ----------- | ---------------------------------------------------------- |
260
+ | `(default)` | `cv-menu-item` children |
261
+ | `label` | Custom group heading content (overrides `label` attribute) |
262
+
263
+ #### CSS Parts
264
+
265
+ | Part | Element | Description |
266
+ | ------- | ------- | ----------------------------------- |
267
+ | `base` | `<div>` | Group container with `role="group"` |
268
+ | `label` | `<div>` | Group label text element |
269
+
270
+ #### CSS Custom Properties
271
+
272
+ | Property | Default | Description |
273
+ | -------------------------------------- | ------------------------- | ------------------------------------- |
274
+ | `--cv-menu-group-label-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding of the group label |
275
+ | `--cv-menu-group-label-font-size` | `0.75em` | Font size of the group label |
276
+ | `--cv-menu-group-gap` | `var(--cv-space-1, 4px)` | Gap between items within the group |
277
+
278
+ #### Visual States
279
+
280
+ None. The group itself has no interactive visual states.
281
+
282
+ #### ARIA Contract
283
+
284
+ | Attribute | Value |
285
+ | ------------ | ---------------------------- |
286
+ | `role` | `group` (on `[part="base"]`) |
287
+ | `aria-label` | group label text |
288
+
289
+ ---
290
+
291
+ ### cv-menu-button
292
+
293
+ Button that opens a menu popup. Supports standard and split-button patterns.
294
+
295
+ #### Anatomy
296
+
297
+ ```
298
+ <cv-menu-button> (host)
299
+ └── <div part="base">
300
+ ├── <button part="trigger"> ← standard mode: single trigger
301
+ │ ├── <span part="prefix">
302
+ │ │ └── <slot name="prefix">
303
+ │ ├── <span part="label">
304
+ │ │ └── <slot>
305
+ │ ├── <span part="suffix">
306
+ │ │ └── <slot name="suffix">
307
+ │ └── <span part="dropdown-icon">
308
+ └── <div part="menu" role="menu">
309
+ └── <slot name="menu"> ← cv-menu-item children
310
+ ```
311
+
312
+ Split-button mode (`[split]`):
313
+
314
+ ```
315
+ <cv-menu-button split> (host)
316
+ └── <div part="base">
317
+ ├── <button part="action"> ← primary action
318
+ │ ├── <span part="prefix">
319
+ │ │ └── <slot name="prefix">
320
+ │ ├── <span part="label">
321
+ │ │ └── <slot>
322
+ │ └── <span part="suffix">
323
+ │ └── <slot name="suffix">
324
+ ├── <button part="dropdown"> ← opens menu
325
+ │ └── <span part="dropdown-icon">
326
+ └── <div part="menu" role="menu">
327
+ └── <slot name="menu">
328
+ ```
329
+
330
+ #### Attributes
331
+
332
+ | Attribute | Type | Default | Description |
333
+ | ----------------- | ------- | ----------- | ----------------------------------------------------------------- |
334
+ | `value` | String | `""` | Last selected menu item value |
335
+ | `open` | Boolean | `false` | Whether the menu popup is visible |
336
+ | `disabled` | Boolean | `false` | Prevents all interaction |
337
+ | `split` | Boolean | `false` | Enables split-button mode with separate action and dropdown areas |
338
+ | `size` | String | `"medium"` | Size: `small` \| `medium` \| `large` |
339
+ | `variant` | String | `"default"` | Visual variant: `default` \| `primary` \| `danger` \| `ghost` |
340
+ | `close-on-select` | Boolean | `true` | Close the menu after an item is selected |
341
+ | `aria-label` | String | `""` | Accessible label for the trigger/dropdown |
342
+
343
+ #### Sizes
344
+
345
+ | Size | `--cv-menu-button-min-height` | `--cv-menu-button-padding-inline` | `--cv-menu-button-padding-block` |
346
+ | -------- | ----------------------------- | --------------------------------- | -------------------------------- |
347
+ | `small` | `30px` | `var(--cv-space-2, 8px)` | `var(--cv-space-1, 4px)` |
348
+ | `medium` | `36px` | `var(--cv-space-3, 12px)` | `var(--cv-space-2, 8px)` |
349
+ | `large` | `42px` | `var(--cv-space-4, 16px)` | `var(--cv-space-2, 8px)` |
350
+
351
+ #### Variants
352
+
353
+ | Variant | Description |
354
+ | --------- | -------------------------------------- |
355
+ | `default` | Default surface background with border |
356
+ | `primary` | Primary-tinted background and border |
357
+ | `danger` | Danger-tinted background and border |
358
+ | `ghost` | Transparent background and border |
359
+
360
+ #### Slots
361
+
362
+ | Slot | Description |
363
+ | ----------- | --------------------------------------------- |
364
+ | `(default)` | Button label text |
365
+ | `prefix` | Icon or element before the label |
366
+ | `suffix` | Icon or element after the label |
367
+ | `menu` | `cv-menu-item` children for the dropdown menu |
368
+
369
+ #### CSS Parts
370
+
371
+ | Part | Element | Description |
372
+ | --------------- | ---------- | ---------------------------------------- |
373
+ | `base` | `<div>` | Root layout wrapper |
374
+ | `trigger` | `<button>` | Full trigger button (standard mode only) |
375
+ | `action` | `<button>` | Primary action button (split mode only) |
376
+ | `dropdown` | `<button>` | Dropdown arrow button (split mode only) |
377
+ | `label` | `<span>` | Wrapper around the default slot |
378
+ | `prefix` | `<span>` | Wrapper around the `prefix` slot |
379
+ | `suffix` | `<span>` | Wrapper around the `suffix` slot |
380
+ | `dropdown-icon` | `<span>` | Dropdown arrow indicator |
381
+ | `menu` | `<div>` | Menu popup container |
382
+
383
+ #### CSS Custom Properties
384
+
385
+ | Property | Default | Description |
386
+ | --------------------------------------- | -------------------------- | ------------------------------------- |
387
+ | `--cv-menu-button-min-height` | `36px` | Minimum block size of the trigger |
388
+ | `--cv-menu-button-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding of the trigger |
389
+ | `--cv-menu-button-padding-block` | `var(--cv-space-2, 8px)` | Vertical padding of the trigger |
390
+ | `--cv-menu-button-border-radius` | `var(--cv-radius-sm, 6px)` | Border radius of the trigger |
391
+ | `--cv-menu-button-gap` | `var(--cv-space-2, 8px)` | Gap between trigger content parts |
392
+ | `--cv-menu-button-font-size` | `inherit` | Font size of button content |
393
+ | `--cv-menu-button-menu-offset` | `var(--cv-space-1, 4px)` | Gap between trigger and menu popup |
394
+ | `--cv-menu-button-menu-min-inline-size` | `max(180px, 100%)` | Minimum inline size of the menu popup |
395
+ | `--cv-menu-button-menu-z-index` | `20` | Z-index of the menu popup |
396
+
397
+ #### Events
398
+
399
+ | Event | Detail | Description |
400
+ | ----------- | ------------------------- | ----------------------------------------------------------------------- |
401
+ | `cv-input` | `{value, activeId, open}` | Fires on any state change (selection, active, open) forwarded from menu |
402
+ | `cv-change` | `{value, activeId, open}` | Fires only when the selected `value` changes |
403
+ | `cv-action` | `{}` | Fires when the action button is clicked in split-button mode |
404
+
405
+ #### Visual States
406
+
407
+ | Host selector | Description |
408
+ | ---------------------------- | --------------------------------------------------------------- |
409
+ | `:host([open])` | Menu popup is visible |
410
+ | `:host([disabled])` | Reduced opacity, `cursor: not-allowed`, all interaction blocked |
411
+ | `:host([split])` | Split-button mode with separate action and dropdown areas |
412
+ | `:host([size="small"])` | Small size overrides |
413
+ | `:host([size="large"])` | Large size overrides |
414
+ | `:host([variant="default"])` | Default surface background with border |
415
+ | `:host([variant="primary"])` | Primary-tinted background and border |
416
+ | `:host([variant="danger"])` | Danger-tinted background and border |
417
+ | `:host([variant="ghost"])` | Transparent background and border |
418
+
419
+ #### Reactive State Mapping
420
+
421
+ `cv-menu-button` is a visual adapter over headless `createMenu`.
422
+
423
+ | UIKit Property | Direction | Headless Binding |
424
+ | ----------------- | -------------- | ------------------------------------------------------------ |
425
+ | `value` | attr -> action | `actions.select(value)` when value changes |
426
+ | `open` | attr -> action | `actions.open()` when `true`; `actions.close()` when `false` |
427
+ | `disabled` | attr -> DOM | disables trigger and blocks all interaction |
428
+ | `split` | attr -> option | passed as `splitButton` in `createMenu(options)` |
429
+ | `close-on-select` | attr -> option | passed as `closeOnSelect` in `createMenu(options)` |
430
+ | `aria-label` | attr -> option | passed as `ariaLabel` in `createMenu(options)` |
431
+
432
+ | Headless State | Direction | DOM Reflection |
433
+ | ------------------------- | ------------- | -------------------------------------------------- |
434
+ | `state.isOpen()` | state -> attr | `[open]` host attribute, menu `[hidden]` |
435
+ | `state.activeId()` | state -> DOM | `[data-active]` on item elements, focus management |
436
+ | `state.selectedId()` | state -> attr | `[value]` host attribute |
437
+ | `state.openedBy()` | state -> DOM | focus management strategy (keyboard vs pointer) |
438
+ | `state.restoreTargetId()` | state -> DOM | focus restored to trigger on close |
439
+ | `state.checkedIds()` | state -> DOM | `[aria-checked]` on checkbox/radio item elements |
440
+ | `state.openSubmenuId()` | state -> DOM | submenu container `[hidden]` state |
441
+ | `state.submenuActiveId()` | state -> DOM | `[data-active]` on submenu child items |
442
+
443
+ Contracts applied to DOM elements:
444
+
445
+ - `contracts.getTriggerProps()` -> trigger button (`[part="trigger"]`): provides `id`, `tabindex`, `aria-haspopup`, `aria-expanded`, `aria-controls`, `aria-label`
446
+ - `contracts.getMenuProps()` -> menu container (`[part="menu"]`): provides `id`, `role`, `tabindex`, `aria-label`, `aria-activedescendant`
447
+ - `contracts.getItemProps(id)` -> each item element: provides `id`, `role`, `tabindex`, `aria-disabled`, `data-active`, `aria-checked`, `aria-haspopup`, `aria-expanded`
448
+ - `contracts.getSplitTriggerProps()` -> action button (`[part="action"]`): provides `id`, `tabindex`, `role` (only when `split` is `true`)
449
+ - `contracts.getSplitDropdownProps()` -> dropdown button (`[part="dropdown"]`): provides `id`, `tabindex`, `role`, `aria-haspopup`, `aria-expanded`, `aria-controls`, `aria-label` (only when `split` is `true`)
450
+
451
+ UIKit does not own activation, navigation, toggle, or dismiss logic; headless state is the source of truth.
452
+
453
+ #### ARIA Contract
454
+
455
+ | Element | Attribute | Value |
456
+ | ------------------ | ----------------------- | ----------------------------------------------- |
457
+ | trigger (standard) | `aria-haspopup` | `menu` |
458
+ | trigger (standard) | `aria-expanded` | `true` / `false` |
459
+ | trigger (standard) | `aria-controls` | menu element id |
460
+ | action (split) | `role` | `button` |
461
+ | dropdown (split) | `aria-haspopup` | `menu` |
462
+ | dropdown (split) | `aria-expanded` | `true` / `false` |
463
+ | dropdown (split) | `aria-controls` | menu element id |
464
+ | dropdown (split) | `aria-label` | `"More options"` or from `aria-label` attribute |
465
+ | menu | `role` | `menu` |
466
+ | menu | `tabindex` | `-1` |
467
+ | menu | `aria-activedescendant` | id of active item (when open) |
468
+
469
+ #### Usage
470
+
471
+ ```html
472
+ <!-- Basic menu button -->
473
+ <cv-menu-button>
474
+ Actions
475
+ <cv-menu-item slot="menu" value="cut">Cut</cv-menu-item>
476
+ <cv-menu-item slot="menu" value="copy">Copy</cv-menu-item>
477
+ <cv-menu-item slot="menu" value="paste">Paste</cv-menu-item>
478
+ </cv-menu-button>
479
+
480
+ <!-- With icon prefix -->
481
+ <cv-menu-button variant="primary">
482
+ <icon-plus slot="prefix"></icon-plus>
483
+ Create
484
+ <cv-menu-item slot="menu" value="file">New File</cv-menu-item>
485
+ <cv-menu-item slot="menu" value="folder">New Folder</cv-menu-item>
486
+ </cv-menu-button>
487
+
488
+ <!-- Small size -->
489
+ <cv-menu-button size="small">
490
+ Options
491
+ <cv-menu-item slot="menu" value="a">Option A</cv-menu-item>
492
+ <cv-menu-item slot="menu" value="b">Option B</cv-menu-item>
493
+ </cv-menu-button>
494
+
495
+ <!-- Split button -->
496
+ <cv-menu-button split variant="primary">
497
+ Save
498
+ <cv-menu-item slot="menu" value="save-as">Save As...</cv-menu-item>
499
+ <cv-menu-item slot="menu" value="save-copy">Save Copy</cv-menu-item>
500
+ <cv-menu-item slot="menu" value="export">Export</cv-menu-item>
501
+ </cv-menu-button>
502
+
503
+ <!-- Disabled -->
504
+ <cv-menu-button disabled>
505
+ Disabled
506
+ <cv-menu-item slot="menu" value="a">Option A</cv-menu-item>
507
+ </cv-menu-button>
508
+ ```
@@ -0,0 +1,148 @@
1
+ # cv-meter
2
+
3
+ Graphical display of a numeric value within a known range, such as disk usage or password strength.
4
+
5
+ **Headless:** [`createMeter`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/meter.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-meter> (host)
11
+ └── <div part="base" role="meter">
12
+ └── <div part="indicator" data-status="…">
13
+ └── <span part="label">
14
+ └── <slot>
15
+ ```
16
+
17
+ ## Attributes
18
+
19
+ | Attribute | Type | Default | Description |
20
+ | ------------------ | ------ | ------- | -------------------------------------------- |
21
+ | `value` | Number | `0` | Current measured value |
22
+ | `min` | Number | `0` | Minimum of the range |
23
+ | `max` | Number | `100` | Maximum of the range (percentage convention) |
24
+ | `low` | Number | — | Low threshold boundary |
25
+ | `high` | Number | — | High threshold boundary |
26
+ | `optimum` | Number | — | Optimum value within the range |
27
+ | `value-text` | String | `""` | Custom `aria-valuetext` string |
28
+ | `aria-label` | String | — | Accessible label |
29
+ | `aria-labelledby` | String | — | ID of labelling element |
30
+ | `aria-describedby` | String | — | ID of describing element |
31
+
32
+ ## Slots
33
+
34
+ | Slot | Description |
35
+ | ----------- | -------------------------------------------------- |
36
+ | `(default)` | Custom label content rendered inside the indicator |
37
+
38
+ ## CSS Parts
39
+
40
+ | Part | Element | Description |
41
+ | ----------- | -------- | ------------------------------------------------------ |
42
+ | `base` | `<div>` | Root meter element with `role="meter"` |
43
+ | `indicator` | `<div>` | Fill bar reflecting current percentage and status zone |
44
+ | `label` | `<span>` | Wrapper around the default slot inside the indicator |
45
+
46
+ ## CSS Custom Properties
47
+
48
+ | Property | Default | Description |
49
+ | -------------------------------- | ---------------------------------- | --------------------------------------------------- |
50
+ | `--cv-meter-height` | `10px` | Block size of the meter track |
51
+ | `--cv-meter-border-radius` | `999px` | Border radius of the track and indicator |
52
+ | `--cv-meter-transition-duration` | `var(--cv-duration-normal, 220ms)` | Transition duration for indicator width |
53
+ | `--cv-meter-optimum-color` | `var(--cv-color-success, #6ef7c8)` | Indicator color when status is `optimum` |
54
+ | `--cv-meter-suboptimum-color` | `var(--cv-color-warning, #ffbe65)` | Indicator color when status is `low` (sub-optimum) |
55
+ | `--cv-meter-danger-color` | `var(--cv-color-danger, #ff7a8a)` | Indicator color when status is `high` (danger zone) |
56
+
57
+ Additionally, component styles depend on theme tokens through fallback values:
58
+
59
+ | Theme Property | Default | Description |
60
+ | ---------------------- | --------- | --------------------------------------- |
61
+ | `--cv-color-border` | `#2a3245` | Track border color |
62
+ | `--cv-color-surface` | `#141923` | Track background color |
63
+ | `--cv-color-primary` | `#65d7ff` | Default indicator color (normal status) |
64
+ | `--cv-color-success` | `#6ef7c8` | Optimum zone color fallback |
65
+ | `--cv-color-warning` | `#ffbe65` | Sub-optimum (low) zone color fallback |
66
+ | `--cv-color-danger` | `#ff7a8a` | Danger (high) zone color fallback |
67
+ | `--cv-duration-normal` | `220ms` | Transition duration fallback |
68
+ | `--cv-easing-standard` | `ease` | Transition timing function |
69
+
70
+ ## Visual States
71
+
72
+ | Host selector | Description |
73
+ | ------------------------- | -------------------------------------------------- |
74
+ | `[data-status="normal"]` | Default indicator color using `--cv-color-primary` |
75
+ | `[data-status="optimum"]` | Indicator uses `--cv-meter-optimum-color` |
76
+ | `[data-status="low"]` | Indicator uses `--cv-meter-suboptimum-color` |
77
+ | `[data-status="high"]` | Indicator uses `--cv-meter-danger-color` |
78
+
79
+ Note: `data-status` is set on the `[part="indicator"]` element, not on the host. The status value is derived entirely from the headless model's `state.status()` computed signal.
80
+
81
+ ## Reactive State Mapping
82
+
83
+ `cv-meter` is a visual adapter over headless `createMeter`.
84
+
85
+ | UIKit Property | Direction | Headless Binding |
86
+ | ------------------ | ------------- | -------------------------------------------------------------- |
87
+ | `value` | attr → action | `actions.setValue(value)` |
88
+ | `min` | attr → option | passed to `createMeter(options)` |
89
+ | `max` | attr → option | passed to `createMeter(options)` |
90
+ | `low` | attr → option | passed to `createMeter(options)` |
91
+ | `high` | attr → option | passed to `createMeter(options)` |
92
+ | `optimum` | attr → option | passed to `createMeter(options)` |
93
+ | `value-text` | attr → option | passed as `formatValueText` callback to `createMeter(options)` |
94
+ | `aria-label` | attr → option | passed as `ariaLabel` to `createMeter(options)` |
95
+ | `aria-labelledby` | attr → option | passed as `ariaLabelledBy` to `createMeter(options)` |
96
+ | `aria-describedby` | attr → option | passed as `ariaDescribedBy` to `createMeter(options)` |
97
+
98
+ | Headless State | Direction | DOM Reflection |
99
+ | -------------------- | ------------- | ------------------------------------------------------- |
100
+ | `state.percentage()` | state → style | `--cv-meter-width` inline style on `[part="indicator"]` |
101
+ | `state.status()` | state → attr | `data-status` attribute on `[part="indicator"]` |
102
+
103
+ - `contracts.getMeterProps()` is spread onto the `[part="base"]` element to apply `role`, `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, `aria-valuetext`, `aria-label`, `aria-labelledby`, and `aria-describedby`.
104
+ - When `min`, `max`, `low`, `high`, `optimum`, `value-text`, or ARIA attributes change, the headless model is recreated with new options.
105
+ - When only `value` changes, `actions.setValue(value)` is called without recreating the model.
106
+ - UIKit does not compute percentage, status, or ARIA attributes itself. All derived state comes from the headless model.
107
+
108
+ ## Events
109
+
110
+ None. Meter is an output-only (read-only) component with no user interaction.
111
+
112
+ ## ARIA
113
+
114
+ All accessibility semantics are provided by the headless contract `getMeterProps()`:
115
+
116
+ - `role="meter"` on `[part="base"]`
117
+ - `aria-valuenow` reflecting current value
118
+ - `aria-valuemin` reflecting minimum
119
+ - `aria-valuemax` reflecting maximum
120
+ - `aria-valuetext` when `value-text` attribute is set (via `formatValueText` callback)
121
+ - `aria-label`, `aria-labelledby`, `aria-describedby` pass-through when provided
122
+
123
+ The UIKit layer does not construct any ARIA attributes directly.
124
+
125
+ ## Usage
126
+
127
+ ```html
128
+ <!-- Basic percentage meter -->
129
+ <cv-meter value="75"></cv-meter>
130
+
131
+ <!-- With explicit range -->
132
+ <cv-meter value="6" min="0" max="10"></cv-meter>
133
+
134
+ <!-- With thresholds for status zones -->
135
+ <cv-meter value="30" low="25" high="75" optimum="50"></cv-meter>
136
+
137
+ <!-- With accessible label -->
138
+ <cv-meter value="80" aria-label="Disk usage"></cv-meter>
139
+
140
+ <!-- With custom value text -->
141
+ <cv-meter value="80" value-text="80% used"></cv-meter>
142
+
143
+ <!-- With custom label content in default slot -->
144
+ <cv-meter value="65"> 65% </cv-meter>
145
+
146
+ <!-- Danger zone example (value exceeds high threshold) -->
147
+ <cv-meter value="92" low="20" high="80" optimum="50"> Critical </cv-meter>
148
+ ```