@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.
- package/LICENSE +8 -0
- package/README.md +96 -0
- package/dist/components/cv-accordion-item.d.ts +69 -0
- package/dist/components/cv-accordion-item.js +176 -0
- package/dist/components/cv-accordion.d.ts +79 -0
- package/dist/components/cv-accordion.js +310 -0
- package/dist/components/cv-alert-dialog.d.ts +86 -0
- package/dist/components/cv-alert-dialog.js +393 -0
- package/dist/components/cv-alert.d.ts +48 -0
- package/dist/components/cv-alert.js +156 -0
- package/dist/components/cv-badge.d.ts +56 -0
- package/dist/components/cv-badge.js +280 -0
- package/dist/components/cv-breadcrumb-item.d.ts +35 -0
- package/dist/components/cv-breadcrumb-item.js +64 -0
- package/dist/components/cv-breadcrumb.d.ts +39 -0
- package/dist/components/cv-breadcrumb.js +160 -0
- package/dist/components/cv-button.d.ts +83 -0
- package/dist/components/cv-button.js +541 -0
- package/dist/components/cv-callout.d.ts +32 -0
- package/dist/components/cv-callout.js +221 -0
- package/dist/components/cv-card.d.ts +48 -0
- package/dist/components/cv-card.js +269 -0
- package/dist/components/cv-carousel-slide.d.ts +25 -0
- package/dist/components/cv-carousel-slide.js +51 -0
- package/dist/components/cv-carousel.d.ts +96 -0
- package/dist/components/cv-carousel.js +457 -0
- package/dist/components/cv-checkbox.d.ts +84 -0
- package/dist/components/cv-checkbox.js +274 -0
- package/dist/components/cv-combobox-group.d.ts +15 -0
- package/dist/components/cv-combobox-group.js +34 -0
- package/dist/components/cv-combobox-option.d.ts +30 -0
- package/dist/components/cv-combobox-option.js +66 -0
- package/dist/components/cv-combobox.d.ts +135 -0
- package/dist/components/cv-combobox.js +834 -0
- package/dist/components/cv-command-item.d.ts +30 -0
- package/dist/components/cv-command-item.js +68 -0
- package/dist/components/cv-command-palette.d.ts +105 -0
- package/dist/components/cv-command-palette.js +578 -0
- package/dist/components/cv-context-menu.d.ts +98 -0
- package/dist/components/cv-context-menu.js +515 -0
- package/dist/components/cv-copy-button.d.ts +61 -0
- package/dist/components/cv-copy-button.js +318 -0
- package/dist/components/cv-date-picker.d.ts +161 -0
- package/dist/components/cv-date-picker.js +803 -0
- package/dist/components/cv-dialog.d.ts +89 -0
- package/dist/components/cv-dialog.js +459 -0
- package/dist/components/cv-disclosure.d.ts +57 -0
- package/dist/components/cv-disclosure.js +241 -0
- package/dist/components/cv-drawer.d.ts +102 -0
- package/dist/components/cv-drawer.js +595 -0
- package/dist/components/cv-feed-article.d.ts +26 -0
- package/dist/components/cv-feed-article.js +52 -0
- package/dist/components/cv-feed.d.ts +62 -0
- package/dist/components/cv-feed.js +310 -0
- package/dist/components/cv-grid-cell.d.ts +30 -0
- package/dist/components/cv-grid-cell.js +57 -0
- package/dist/components/cv-grid-column.d.ts +30 -0
- package/dist/components/cv-grid-column.js +43 -0
- package/dist/components/cv-grid-row.d.ts +30 -0
- package/dist/components/cv-grid-row.js +42 -0
- package/dist/components/cv-grid.d.ts +119 -0
- package/dist/components/cv-grid.js +567 -0
- package/dist/components/cv-icon.d.ts +57 -0
- package/dist/components/cv-icon.js +352 -0
- package/dist/components/cv-input.d.ts +127 -0
- package/dist/components/cv-input.js +482 -0
- package/dist/components/cv-landmark.d.ts +32 -0
- package/dist/components/cv-landmark.js +62 -0
- package/dist/components/cv-link.d.ts +22 -0
- package/dist/components/cv-link.js +99 -0
- package/dist/components/cv-listbox-group.d.ts +15 -0
- package/dist/components/cv-listbox-group.js +42 -0
- package/dist/components/cv-listbox.d.ts +81 -0
- package/dist/components/cv-listbox.js +388 -0
- package/dist/components/cv-menu-button.d.ts +118 -0
- package/dist/components/cv-menu-button.js +822 -0
- package/dist/components/cv-menu-group.d.ts +20 -0
- package/dist/components/cv-menu-group.js +48 -0
- package/dist/components/cv-menu-item.d.ts +52 -0
- package/dist/components/cv-menu-item.js +105 -0
- package/dist/components/cv-menu.d.ts +62 -0
- package/dist/components/cv-menu.js +414 -0
- package/dist/components/cv-meter.d.ts +66 -0
- package/dist/components/cv-meter.js +154 -0
- package/dist/components/cv-number.d.ts +139 -0
- package/dist/components/cv-number.js +553 -0
- package/dist/components/cv-option.d.ts +30 -0
- package/dist/components/cv-option.js +84 -0
- package/dist/components/cv-popover.d.ts +87 -0
- package/dist/components/cv-popover.js +373 -0
- package/dist/components/cv-progress-ring.d.ts +45 -0
- package/dist/components/cv-progress-ring.js +169 -0
- package/dist/components/cv-progress.d.ts +45 -0
- package/dist/components/cv-progress.js +148 -0
- package/dist/components/cv-radio-group.d.ts +79 -0
- package/dist/components/cv-radio-group.js +398 -0
- package/dist/components/cv-radio.d.ts +36 -0
- package/dist/components/cv-radio.js +123 -0
- package/dist/components/cv-select-group.d.ts +15 -0
- package/dist/components/cv-select-group.js +44 -0
- package/dist/components/cv-select-option.d.ts +30 -0
- package/dist/components/cv-select-option.js +66 -0
- package/dist/components/cv-select.d.ts +128 -0
- package/dist/components/cv-select.js +666 -0
- package/dist/components/cv-sidebar-item.d.ts +26 -0
- package/dist/components/cv-sidebar-item.js +142 -0
- package/dist/components/cv-sidebar.d.ts +171 -0
- package/dist/components/cv-sidebar.js +767 -0
- package/dist/components/cv-slider-multi-thumb.d.ts +73 -0
- package/dist/components/cv-slider-multi-thumb.js +374 -0
- package/dist/components/cv-slider.d.ts +84 -0
- package/dist/components/cv-slider.js +328 -0
- package/dist/components/cv-spinbutton.d.ts +121 -0
- package/dist/components/cv-spinbutton.js +486 -0
- package/dist/components/cv-spinner.d.ts +18 -0
- package/dist/components/cv-spinner.js +95 -0
- package/dist/components/cv-switch.d.ts +81 -0
- package/dist/components/cv-switch.js +285 -0
- package/dist/components/cv-tab-panel.d.ts +20 -0
- package/dist/components/cv-tab-panel.js +37 -0
- package/dist/components/cv-tab.d.ts +40 -0
- package/dist/components/cv-tab.js +132 -0
- package/dist/components/cv-table-cell.d.ts +31 -0
- package/dist/components/cv-table-cell.js +49 -0
- package/dist/components/cv-table-column.d.ts +37 -0
- package/dist/components/cv-table-column.js +63 -0
- package/dist/components/cv-table-row.d.ts +30 -0
- package/dist/components/cv-table-row.js +45 -0
- package/dist/components/cv-table.d.ts +147 -0
- package/dist/components/cv-table.js +607 -0
- package/dist/components/cv-tabs.d.ts +70 -0
- package/dist/components/cv-tabs.js +524 -0
- package/dist/components/cv-textarea.d.ts +108 -0
- package/dist/components/cv-textarea.js +328 -0
- package/dist/components/cv-toast-region.d.ts +39 -0
- package/dist/components/cv-toast-region.js +162 -0
- package/dist/components/cv-toast.d.ts +67 -0
- package/dist/components/cv-toast.js +315 -0
- package/dist/components/cv-toolbar-item.d.ts +25 -0
- package/dist/components/cv-toolbar-item.js +72 -0
- package/dist/components/cv-toolbar-separator.d.ts +25 -0
- package/dist/components/cv-toolbar-separator.js +45 -0
- package/dist/components/cv-toolbar.d.ts +63 -0
- package/dist/components/cv-toolbar.js +295 -0
- package/dist/components/cv-tooltip.d.ts +83 -0
- package/dist/components/cv-tooltip.js +455 -0
- package/dist/components/cv-treegrid-cell.d.ts +30 -0
- package/dist/components/cv-treegrid-cell.js +57 -0
- package/dist/components/cv-treegrid-column.d.ts +37 -0
- package/dist/components/cv-treegrid-column.js +53 -0
- package/dist/components/cv-treegrid-row.d.ts +55 -0
- package/dist/components/cv-treegrid-row.js +90 -0
- package/dist/components/cv-treegrid.d.ts +96 -0
- package/dist/components/cv-treegrid.js +632 -0
- package/dist/components/cv-treeitem.d.ts +58 -0
- package/dist/components/cv-treeitem.js +144 -0
- package/dist/components/cv-treeview.d.ts +70 -0
- package/dist/components/cv-treeview.js +396 -0
- package/dist/components/cv-window-splitter.d.ts +79 -0
- package/dist/components/cv-window-splitter.js +316 -0
- package/dist/components/index.d.ts +94 -0
- package/dist/components/index.js +79 -0
- package/dist/dialog/create-dialog-controller.d.ts +31 -0
- package/dist/dialog/create-dialog-controller.js +320 -0
- package/dist/dialog/index.d.ts +2 -0
- package/dist/dialog/index.js +1 -0
- package/dist/form-associated/FormAssociatedReatomElement.d.ts +25 -0
- package/dist/form-associated/FormAssociatedReatomElement.js +70 -0
- package/dist/form-associated/withFormAssociated.d.ts +5 -0
- package/dist/form-associated/withFormAssociated.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/reatom-lit/ReatomLitElement.d.ts +27 -0
- package/dist/reatom-lit/ReatomLitElement.js +118 -0
- package/dist/reatom-lit/html.d.ts +4 -0
- package/dist/reatom-lit/html.js +10 -0
- package/dist/reatom-lit/index.d.ts +4 -0
- package/dist/reatom-lit/index.js +4 -0
- package/dist/reatom-lit/watch.d.ts +15 -0
- package/dist/reatom-lit/watch.js +40 -0
- package/dist/reatom-lit/withReatomElement.d.ts +4 -0
- package/dist/reatom-lit/withReatomElement.js +57 -0
- package/dist/register.d.ts +1 -0
- package/dist/register.js +84 -0
- package/dist/styles/component-styles.d.ts +4 -0
- package/dist/styles/component-styles.js +78 -0
- package/dist/theme/cv-theme-provider.d.ts +32 -0
- package/dist/theme/cv-theme-provider.js +110 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +2 -0
- package/dist/theme/theme-engine.d.ts +4 -0
- package/dist/theme/theme-engine.js +67 -0
- package/dist/theme/tokens.css +265 -0
- package/dist/theme/types.d.ts +7 -0
- package/dist/theme/types.js +1 -0
- package/dist/toast/create-toast-controller.d.ts +12 -0
- package/dist/toast/create-toast-controller.js +12 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +1 -0
- package/package.json +146 -0
- package/specs/_template.md +110 -0
- package/specs/components/accordion.md +207 -0
- package/specs/components/alert.md +83 -0
- package/specs/components/badge.md +183 -0
- package/specs/components/breadcrumb.md +152 -0
- package/specs/components/button.md +227 -0
- package/specs/components/callout.md +153 -0
- package/specs/components/card.md +192 -0
- package/specs/components/carousel.md +232 -0
- package/specs/components/checkbox.md +141 -0
- package/specs/components/combobox.md +427 -0
- package/specs/components/context-menu.md +375 -0
- package/specs/components/copy-button.md +236 -0
- package/specs/components/date-picker.md +290 -0
- package/specs/components/dialog.md +184 -0
- package/specs/components/disclosure.md +151 -0
- package/specs/components/drawer.md +216 -0
- package/specs/components/feed.md +266 -0
- package/specs/components/grid.md +423 -0
- package/specs/components/input.md +237 -0
- package/specs/components/landmark.md +92 -0
- package/specs/components/link.md +117 -0
- package/specs/components/listbox.md +327 -0
- package/specs/components/menu.md +508 -0
- package/specs/components/meter.md +148 -0
- package/specs/components/number.md +268 -0
- package/specs/components/option.md +167 -0
- package/specs/components/popover.md +207 -0
- package/specs/components/progress-ring.md +134 -0
- package/specs/components/progress.md +110 -0
- package/specs/components/radio.md +208 -0
- package/specs/components/select.md +305 -0
- package/specs/components/sidebar.md +204 -0
- package/specs/components/spinbutton.md +157 -0
- package/specs/components/spinner.md +83 -0
- package/specs/components/switch.md +145 -0
- package/specs/components/table.md +372 -0
- package/specs/components/tabs.md +242 -0
- package/specs/components/textarea.md +166 -0
- package/specs/components/theme.md +364 -0
- package/specs/components/toast.md +198 -0
- package/specs/components/toolbar.md +258 -0
- package/specs/components/tooltip.md +152 -0
- package/specs/components/treegrid.md +363 -0
- package/specs/components/treeview.md +263 -0
- 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
|
+
```
|