@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,208 @@
|
|
|
1
|
+
# cv-radio-group
|
|
2
|
+
|
|
3
|
+
Set of mutually exclusive options where only one can be selected at a time.
|
|
4
|
+
|
|
5
|
+
**Headless:** [`createRadioGroup`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/radio-group.md)
|
|
6
|
+
|
|
7
|
+
## Cross-Spec Consistency
|
|
8
|
+
|
|
9
|
+
This document is the UIKit surface contract for Radio Group.
|
|
10
|
+
|
|
11
|
+
- The canonical state model, invariants, and user-driven transitions are defined by the headless spec.
|
|
12
|
+
- Any intentional divergence between UIKit and headless MUST be explicitly documented in both specs to prevent drift.
|
|
13
|
+
|
|
14
|
+
## Anatomy
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
<cv-radio-group> (host)
|
|
18
|
+
└── <div part="base" role="radiogroup">
|
|
19
|
+
└── <slot> ← accepts <cv-radio> children
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Attributes
|
|
23
|
+
|
|
24
|
+
| Attribute | Type | Default | Description |
|
|
25
|
+
| ------------- | ------- | -------------- | ------------------------------------- |
|
|
26
|
+
| `value` | String | `""` | Value of the currently selected radio |
|
|
27
|
+
| `orientation` | String | `"horizontal"` | Layout: `horizontal` \| `vertical` |
|
|
28
|
+
| `disabled` | Boolean | `false` | Prevents interaction for all radios |
|
|
29
|
+
| `aria-label` | String | `""` | Accessible label for the group |
|
|
30
|
+
|
|
31
|
+
## Slots
|
|
32
|
+
|
|
33
|
+
| Slot | Description |
|
|
34
|
+
| ----------- | --------------------- |
|
|
35
|
+
| `(default)` | `<cv-radio>` children |
|
|
36
|
+
|
|
37
|
+
## CSS Parts
|
|
38
|
+
|
|
39
|
+
| Part | Element | Description |
|
|
40
|
+
| ------ | ------- | ---------------------------------------------- |
|
|
41
|
+
| `base` | `<div>` | Root layout container with `role="radiogroup"` |
|
|
42
|
+
|
|
43
|
+
## CSS Custom Properties
|
|
44
|
+
|
|
45
|
+
| Property | Default | Description |
|
|
46
|
+
| ---------------------- | ------------------------ | --------------------------- |
|
|
47
|
+
| `--cv-radio-group-gap` | `var(--cv-space-2, 8px)` | Spacing between radio items |
|
|
48
|
+
|
|
49
|
+
## Visual States
|
|
50
|
+
|
|
51
|
+
| Host selector | Description |
|
|
52
|
+
| --------------------------------- | ------------------------------------ |
|
|
53
|
+
| `:host([disabled])` | All child radios are non-interactive |
|
|
54
|
+
| `:host([orientation="vertical"])` | Items stacked vertically |
|
|
55
|
+
|
|
56
|
+
## Reactive State Mapping
|
|
57
|
+
|
|
58
|
+
`cv-radio-group` is a visual adapter over headless `createRadioGroup`.
|
|
59
|
+
|
|
60
|
+
| UIKit Property | Direction | Headless Binding |
|
|
61
|
+
| -------------- | ------------- | -------------------------------------------------------------------------- |
|
|
62
|
+
| `value` | attr → action | `actions.select(value)` on change; initial passed as `initialValue` option |
|
|
63
|
+
| `disabled` | attr → action | `actions.setDisabled(value)` |
|
|
64
|
+
| `orientation` | attr → option | passed as `orientation` in `createRadioGroup(options)` |
|
|
65
|
+
| `aria-label` | attr → option | passed as `ariaLabel` in `createRadioGroup(options)` |
|
|
66
|
+
|
|
67
|
+
| Headless State | Direction | DOM Reflection |
|
|
68
|
+
| -------------------- | ------------ | ------------------------------------------------------------ |
|
|
69
|
+
| `state.value()` | state → attr | `[value]` host attribute; reflected onto `cv-radio[checked]` |
|
|
70
|
+
| `state.activeId()` | state → attr | reflected onto `cv-radio[active]` |
|
|
71
|
+
| `state.isDisabled()` | state → attr | `[disabled]` host attribute |
|
|
72
|
+
|
|
73
|
+
- `contracts.getRootProps()` is spread onto the inner `[part="base"]` element to apply `role`, `aria-label`, `aria-disabled`, `aria-orientation`, and `onKeyDown` handler.
|
|
74
|
+
- `contracts.getRadioProps(id)` is spread onto each `cv-radio` child to apply `role`, `tabindex`, `aria-checked`, `aria-disabled`, `aria-describedby`, `data-active`, `onClick`, and `onKeyDown`.
|
|
75
|
+
- UIKit dispatches `cv-input` and `cv-change` events by observing `state.value()` changes triggered by user activation (not by controlled attribute updates).
|
|
76
|
+
- UIKit does not own selection or navigation logic; headless state is the source of truth.
|
|
77
|
+
|
|
78
|
+
## Events
|
|
79
|
+
|
|
80
|
+
| Event | Detail | Description |
|
|
81
|
+
| ----------- | ----------------------------------- | ----------------------------------- |
|
|
82
|
+
| `cv-input` | `{value: string, activeId: string}` | Fires on user selection interaction |
|
|
83
|
+
| `cv-change` | `{value: string, activeId: string}` | Fires when selected value commits |
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<cv-radio-group value="opt-1">
|
|
89
|
+
<cv-radio value="opt-1">Option 1</cv-radio>
|
|
90
|
+
<cv-radio value="opt-2">Option 2</cv-radio>
|
|
91
|
+
<cv-radio value="opt-3" disabled>Option 3</cv-radio>
|
|
92
|
+
</cv-radio-group>
|
|
93
|
+
|
|
94
|
+
<cv-radio-group orientation="vertical" aria-label="Payment method">
|
|
95
|
+
<cv-radio value="card">Credit card</cv-radio>
|
|
96
|
+
<cv-radio value="paypal">PayPal</cv-radio>
|
|
97
|
+
<cv-radio value="bank">Bank transfer</cv-radio>
|
|
98
|
+
</cv-radio-group>
|
|
99
|
+
|
|
100
|
+
<cv-radio-group disabled>
|
|
101
|
+
<cv-radio value="a">Disabled A</cv-radio>
|
|
102
|
+
<cv-radio value="b">Disabled B</cv-radio>
|
|
103
|
+
</cv-radio-group>
|
|
104
|
+
|
|
105
|
+
<cv-radio-group value="med">
|
|
106
|
+
<cv-radio value="sm" size="small">Small radio</cv-radio>
|
|
107
|
+
<cv-radio value="med" size="medium">Medium radio</cv-radio>
|
|
108
|
+
<cv-radio value="lg" size="large">Large radio</cv-radio>
|
|
109
|
+
</cv-radio-group>
|
|
110
|
+
|
|
111
|
+
<cv-radio-group value="with-desc">
|
|
112
|
+
<cv-radio value="with-desc">
|
|
113
|
+
Primary option
|
|
114
|
+
<span slot="description">Additional details about this option</span>
|
|
115
|
+
</cv-radio>
|
|
116
|
+
<cv-radio value="other">Other option</cv-radio>
|
|
117
|
+
</cv-radio-group>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Child Elements
|
|
121
|
+
|
|
122
|
+
### cv-radio
|
|
123
|
+
|
|
124
|
+
Individual radio option within a radio group. Purely presentational — all state and ARIA are managed by the parent `cv-radio-group`.
|
|
125
|
+
|
|
126
|
+
#### Anatomy
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
<cv-radio> (host)
|
|
130
|
+
└── <div part="base">
|
|
131
|
+
├── <span part="indicator">
|
|
132
|
+
│ └── <span part="dot">
|
|
133
|
+
├── <span part="label">
|
|
134
|
+
│ └── <slot>
|
|
135
|
+
└── <span part="description">
|
|
136
|
+
└── <slot name="description">
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Attributes
|
|
140
|
+
|
|
141
|
+
| Attribute | Type | Default | Description |
|
|
142
|
+
| ---------- | ------- | ---------- | ------------------------------------------------------ |
|
|
143
|
+
| `value` | String | `""` | Unique identifier for this radio option |
|
|
144
|
+
| `disabled` | Boolean | `false` | Prevents interaction for this radio |
|
|
145
|
+
| `checked` | Boolean | `false` | Whether this radio is selected (managed by group) |
|
|
146
|
+
| `active` | Boolean | `false` | Whether this radio has roving focus (managed by group) |
|
|
147
|
+
| `size` | String | `"medium"` | Size: `small` \| `medium` \| `large` |
|
|
148
|
+
|
|
149
|
+
#### Sizes
|
|
150
|
+
|
|
151
|
+
| Size | `--cv-radio-indicator-size` | `--cv-radio-dot-size` |
|
|
152
|
+
| -------- | --------------------------- | --------------------- |
|
|
153
|
+
| `small` | `16px` | `6px` |
|
|
154
|
+
| `medium` | `20px` | `8px` |
|
|
155
|
+
| `large` | `24px` | `10px` |
|
|
156
|
+
|
|
157
|
+
#### Slots
|
|
158
|
+
|
|
159
|
+
| Slot | Description |
|
|
160
|
+
| ------------- | ---------------------------------------- |
|
|
161
|
+
| `(default)` | Label text for the radio option |
|
|
162
|
+
| `description` | Secondary text displayed below the label |
|
|
163
|
+
|
|
164
|
+
#### CSS Parts
|
|
165
|
+
|
|
166
|
+
| Part | Element | Description |
|
|
167
|
+
| ------------- | -------- | ------------------------------------------ |
|
|
168
|
+
| `base` | `<div>` | Root layout wrapper |
|
|
169
|
+
| `indicator` | `<span>` | Circular border container for the dot |
|
|
170
|
+
| `dot` | `<span>` | Inner filled circle (visible when checked) |
|
|
171
|
+
| `label` | `<span>` | Wrapper around the default slot |
|
|
172
|
+
| `description` | `<span>` | Wrapper around the `description` slot |
|
|
173
|
+
|
|
174
|
+
#### CSS Custom Properties
|
|
175
|
+
|
|
176
|
+
| Property | Default | Description |
|
|
177
|
+
| --------------------------- | ------------------------ | ----------------------------------- |
|
|
178
|
+
| `--cv-radio-indicator-size` | `20px` | Outer size of the radio circle |
|
|
179
|
+
| `--cv-radio-dot-size` | `8px` | Inner dot size when checked |
|
|
180
|
+
| `--cv-radio-gap` | `var(--cv-space-2, 8px)` | Spacing between indicator and label |
|
|
181
|
+
|
|
182
|
+
#### Visual States
|
|
183
|
+
|
|
184
|
+
| Host selector | Description |
|
|
185
|
+
| ----------------------- | ----------------------------------------------- |
|
|
186
|
+
| `:host([checked])` | Primary-tinted indicator border, dot visible |
|
|
187
|
+
| `:host([disabled])` | Reduced opacity (`0.55`), `cursor: not-allowed` |
|
|
188
|
+
| `:host([active])` | Focused radio in roving tabindex model |
|
|
189
|
+
| `:host(:focus-visible)` | Focus ring on the host element |
|
|
190
|
+
| `:host([size="small"])` | Small size overrides |
|
|
191
|
+
| `:host([size="large"])` | Large size overrides |
|
|
192
|
+
|
|
193
|
+
#### Events
|
|
194
|
+
|
|
195
|
+
None. All events are dispatched by the parent `cv-radio-group`.
|
|
196
|
+
|
|
197
|
+
## Parity Matrix (Headless vs UIKit)
|
|
198
|
+
|
|
199
|
+
| Surface | Headless | UIKit |
|
|
200
|
+
| ------------------- | ----------------------------------- | ------------------------------------------------- |
|
|
201
|
+
| Selection model | single selection via `value` atom | `value` attribute on group |
|
|
202
|
+
| Focus model | roving tabindex via `activeId` atom | `active` attribute on radio |
|
|
203
|
+
| Disabled semantics | group-level + per-item | `disabled` on group + individual radio |
|
|
204
|
+
| Navigation | arrow keys with wrapping, Home/End | delegated to headless `handleKeyDown` |
|
|
205
|
+
| Description linkage | `describedBy` on `RadioGroupItem` | `description` slot with `aria-describedby` |
|
|
206
|
+
| Size | not applicable | `small` \| `medium` \| `large` on radio |
|
|
207
|
+
| Orientation | `orientation` option | `orientation` attribute on group |
|
|
208
|
+
| Events | N/A (actions/state API) | `cv-input` / `cv-change` with `{value, activeId}` |
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# cv-select
|
|
2
|
+
|
|
3
|
+
Single or multi-selection dropdown that composes a combobox trigger with a listbox popup, following the W3C APG Select-Only Combobox pattern.
|
|
4
|
+
|
|
5
|
+
**Headless:** [`createSelect`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/select.md)
|
|
6
|
+
|
|
7
|
+
## Anatomy
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<cv-select> (host)
|
|
11
|
+
└── <div part="base">
|
|
12
|
+
├── <div part="trigger" role="combobox">
|
|
13
|
+
│ ├── <slot name="trigger"> ← fallback: selected label / placeholder
|
|
14
|
+
│ ├── <button part="clear-button" aria-hidden="true"> ← only when clearable + has value
|
|
15
|
+
│ └── <span part="chevron" aria-hidden="true">
|
|
16
|
+
└── <div part="listbox" role="listbox">
|
|
17
|
+
└── <slot> ← cv-select-option / cv-select-group children
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Attributes
|
|
21
|
+
|
|
22
|
+
| Attribute | Type | Default | Description |
|
|
23
|
+
| ----------------- | ------- | ---------- | ----------------------------------------------- |
|
|
24
|
+
| `value` | String | `""` | Currently selected option value (single-select) |
|
|
25
|
+
| `open` | Boolean | `false` | Whether the listbox popup is visible |
|
|
26
|
+
| `selection-mode` | String | `"single"` | Selection mode: `single` \| `multiple` |
|
|
27
|
+
| `aria-label` | String | `""` | Accessible label for the trigger |
|
|
28
|
+
| `close-on-select` | Boolean | `true` | Close popup after an option is selected |
|
|
29
|
+
| `placeholder` | String | `""` | Hint text when no option is selected |
|
|
30
|
+
| `disabled` | Boolean | `false` | Prevents all interaction |
|
|
31
|
+
| `required` | Boolean | `false` | Marks the field as required for form validation |
|
|
32
|
+
| `clearable` | Boolean | `false` | Shows a clear button when a value is selected |
|
|
33
|
+
| `size` | String | `"medium"` | Size: `small` \| `medium` \| `large` |
|
|
34
|
+
|
|
35
|
+
Non-reflected properties:
|
|
36
|
+
|
|
37
|
+
| Property | Type | Default | Description |
|
|
38
|
+
| ---------------- | ---------- | ------- | --------------------------------------------------------- |
|
|
39
|
+
| `selectedValues` | `string[]` | `[]` | Array of selected option values (useful in multiple mode) |
|
|
40
|
+
|
|
41
|
+
## Sizes
|
|
42
|
+
|
|
43
|
+
| Size | `--cv-select-min-height` | `--cv-select-padding-inline` | `--cv-select-padding-block` |
|
|
44
|
+
| -------- | ------------------------ | ---------------------------- | --------------------------- |
|
|
45
|
+
| `small` | `30px` | `var(--cv-space-2, 8px)` | `var(--cv-space-1, 4px)` |
|
|
46
|
+
| `medium` | `36px` | `var(--cv-space-3, 12px)` | `var(--cv-space-2, 8px)` |
|
|
47
|
+
| `large` | `42px` | `var(--cv-space-4, 16px)` | `var(--cv-space-2, 8px)` |
|
|
48
|
+
|
|
49
|
+
## Slots
|
|
50
|
+
|
|
51
|
+
| Slot | Description |
|
|
52
|
+
| ----------- | ------------------------------------------------------------- |
|
|
53
|
+
| `(default)` | `cv-select-option` and `cv-select-group` children |
|
|
54
|
+
| `trigger` | Custom trigger content (replaces default selected label text) |
|
|
55
|
+
|
|
56
|
+
## CSS Parts
|
|
57
|
+
|
|
58
|
+
| Part | Element | Description |
|
|
59
|
+
| -------------- | ---------- | -------------------------------------------------------------------- |
|
|
60
|
+
| `base` | `<div>` | Root layout wrapper |
|
|
61
|
+
| `trigger` | `<div>` | Combobox trigger that opens/closes the listbox |
|
|
62
|
+
| `chevron` | `<span>` | Dropdown arrow indicator |
|
|
63
|
+
| `clear-button` | `<button>` | Clear value button (only rendered when `clearable` and value is set) |
|
|
64
|
+
| `listbox` | `<div>` | Popup container holding options |
|
|
65
|
+
|
|
66
|
+
## CSS Custom Properties
|
|
67
|
+
|
|
68
|
+
| Property | Default | Description |
|
|
69
|
+
| ---------------------------- | ------------------------- | --------------------------------- |
|
|
70
|
+
| `--cv-select-inline-size` | `260px` | Inline size of the host element |
|
|
71
|
+
| `--cv-select-min-height` | `36px` | Minimum block size of the trigger |
|
|
72
|
+
| `--cv-select-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding of the trigger |
|
|
73
|
+
| `--cv-select-padding-block` | `var(--cv-space-2, 8px)` | Vertical padding of the trigger |
|
|
74
|
+
|
|
75
|
+
## Visual States
|
|
76
|
+
|
|
77
|
+
| Host selector | Description |
|
|
78
|
+
| ------------------------------------ | --------------------------------------------------------------- |
|
|
79
|
+
| `:host([open])` | Listbox popup is visible |
|
|
80
|
+
| `:host([selection-mode="multiple"])` | Multiple selection mode active |
|
|
81
|
+
| `:host([disabled])` | Reduced opacity, `cursor: not-allowed`, all interaction blocked |
|
|
82
|
+
| `:host([required])` | Field is required |
|
|
83
|
+
| `:host([clearable])` | Clear button visible when value is set |
|
|
84
|
+
| `:host([size="small"])` | Small size overrides |
|
|
85
|
+
| `:host([size="large"])` | Large size overrides |
|
|
86
|
+
|
|
87
|
+
## Events
|
|
88
|
+
|
|
89
|
+
| Event | Detail | Description |
|
|
90
|
+
| ----------- | ------------------------------------------------------------------------------------ | --------------------------------------------------- |
|
|
91
|
+
| `cv-input` | `{value: string \| null, values: string[], activeId: string \| null, open: boolean}` | Fires on any state change (selection, active, open) |
|
|
92
|
+
| `cv-change` | `{value: string \| null, values: string[], activeId: string \| null, open: boolean}` | Fires only when selected value(s) change |
|
|
93
|
+
|
|
94
|
+
## Keyboard Interaction
|
|
95
|
+
|
|
96
|
+
### Trigger focused (listbox closed)
|
|
97
|
+
|
|
98
|
+
| Key | Action |
|
|
99
|
+
| -------------------- | ----------------------------------- |
|
|
100
|
+
| `ArrowDown` / `Home` | Open listbox and focus first option |
|
|
101
|
+
| `ArrowUp` / `End` | Open listbox and focus last option |
|
|
102
|
+
| `Enter` / `Space` | Toggle listbox open/close |
|
|
103
|
+
|
|
104
|
+
### Listbox open (DOM focus remains on trigger)
|
|
105
|
+
|
|
106
|
+
| Key | Action |
|
|
107
|
+
| ----------------- | ------------------------------------------------- |
|
|
108
|
+
| `ArrowDown` | Move visual focus to next option |
|
|
109
|
+
| `ArrowUp` | Move visual focus to previous option |
|
|
110
|
+
| `Home` | Move visual focus to first option |
|
|
111
|
+
| `End` | Move visual focus to last option |
|
|
112
|
+
| `Enter` / `Space` | Select active option (close if `close-on-select`) |
|
|
113
|
+
| `Escape` / `Tab` | Close listbox without changing selection |
|
|
114
|
+
|
|
115
|
+
### When disabled
|
|
116
|
+
|
|
117
|
+
All keyboard handlers are no-ops.
|
|
118
|
+
|
|
119
|
+
## ARIA Contract
|
|
120
|
+
|
|
121
|
+
| Element | Attribute | Value |
|
|
122
|
+
| ------- | ----------------------- | ----------------------------------------- |
|
|
123
|
+
| trigger | `role` | `combobox` |
|
|
124
|
+
| trigger | `tabindex` | `0` |
|
|
125
|
+
| trigger | `aria-haspopup` | `listbox` |
|
|
126
|
+
| trigger | `aria-expanded` | `true` / `false` |
|
|
127
|
+
| trigger | `aria-controls` | listbox element id |
|
|
128
|
+
| trigger | `aria-activedescendant` | id of visually focused option (when open) |
|
|
129
|
+
| trigger | `aria-disabled` | `true` (when disabled) |
|
|
130
|
+
| trigger | `aria-required` | `true` (when required) |
|
|
131
|
+
| trigger | `aria-label` | accessible label text |
|
|
132
|
+
| listbox | `role` | `listbox` |
|
|
133
|
+
| listbox | `aria-activedescendant` | id of focused option |
|
|
134
|
+
| listbox | `aria-multiselectable` | `true` (when `selection-mode="multiple"`) |
|
|
135
|
+
| option | `role` | `option` |
|
|
136
|
+
| option | `aria-selected` | `true` / `false` |
|
|
137
|
+
| option | `aria-disabled` | `true` (when disabled) |
|
|
138
|
+
|
|
139
|
+
## Reactive State Mapping
|
|
140
|
+
|
|
141
|
+
| UIKit Property | Direction | Headless Binding |
|
|
142
|
+
| ---------------- | --------- | ------------------------------------- |
|
|
143
|
+
| `value` | → | `actions.select(id)` on change |
|
|
144
|
+
| `disabled` | → | `actions.setDisabled(value)` |
|
|
145
|
+
| `required` | → | `actions.setRequired(value)` |
|
|
146
|
+
| `open` | ← | `state.isOpen()` |
|
|
147
|
+
| `selectedValues` | ← | `state.selectedIds()` |
|
|
148
|
+
| trigger ARIA | ← | `contracts.getTriggerProps()` spread |
|
|
149
|
+
| listbox ARIA | ← | `contracts.getListboxProps()` spread |
|
|
150
|
+
| option ARIA | ← | `contracts.getOptionProps(id)` spread |
|
|
151
|
+
| trigger label | ← | `contracts.getValueText()` |
|
|
152
|
+
|
|
153
|
+
## Usage
|
|
154
|
+
|
|
155
|
+
```html
|
|
156
|
+
<!-- Basic single-select -->
|
|
157
|
+
<cv-select placeholder="Choose a fruit">
|
|
158
|
+
<cv-select-option value="apple">Apple</cv-select-option>
|
|
159
|
+
<cv-select-option value="banana">Banana</cv-select-option>
|
|
160
|
+
<cv-select-option value="cherry">Cherry</cv-select-option>
|
|
161
|
+
</cv-select>
|
|
162
|
+
|
|
163
|
+
<!-- Pre-selected value -->
|
|
164
|
+
<cv-select value="banana">
|
|
165
|
+
<cv-select-option value="apple">Apple</cv-select-option>
|
|
166
|
+
<cv-select-option value="banana">Banana</cv-select-option>
|
|
167
|
+
<cv-select-option value="cherry">Cherry</cv-select-option>
|
|
168
|
+
</cv-select>
|
|
169
|
+
|
|
170
|
+
<!-- With size variant -->
|
|
171
|
+
<cv-select size="small" placeholder="Small select">
|
|
172
|
+
<cv-select-option value="a">Option A</cv-select-option>
|
|
173
|
+
<cv-select-option value="b">Option B</cv-select-option>
|
|
174
|
+
</cv-select>
|
|
175
|
+
|
|
176
|
+
<!-- Disabled -->
|
|
177
|
+
<cv-select disabled placeholder="Cannot interact">
|
|
178
|
+
<cv-select-option value="a">Option A</cv-select-option>
|
|
179
|
+
</cv-select>
|
|
180
|
+
|
|
181
|
+
<!-- Clearable -->
|
|
182
|
+
<cv-select clearable value="apple">
|
|
183
|
+
<cv-select-option value="apple">Apple</cv-select-option>
|
|
184
|
+
<cv-select-option value="banana">Banana</cv-select-option>
|
|
185
|
+
</cv-select>
|
|
186
|
+
|
|
187
|
+
<!-- Required -->
|
|
188
|
+
<cv-select required placeholder="Required field">
|
|
189
|
+
<cv-select-option value="yes">Yes</cv-select-option>
|
|
190
|
+
<cv-select-option value="no">No</cv-select-option>
|
|
191
|
+
</cv-select>
|
|
192
|
+
|
|
193
|
+
<!-- Grouped options -->
|
|
194
|
+
<cv-select placeholder="Choose a color">
|
|
195
|
+
<cv-select-group label="Warm">
|
|
196
|
+
<cv-select-option value="red">Red</cv-select-option>
|
|
197
|
+
<cv-select-option value="orange">Orange</cv-select-option>
|
|
198
|
+
</cv-select-group>
|
|
199
|
+
<cv-select-group label="Cool">
|
|
200
|
+
<cv-select-option value="blue">Blue</cv-select-option>
|
|
201
|
+
<cv-select-option value="green">Green</cv-select-option>
|
|
202
|
+
</cv-select-group>
|
|
203
|
+
</cv-select>
|
|
204
|
+
|
|
205
|
+
<!-- Multiple selection -->
|
|
206
|
+
<cv-select selection-mode="multiple" placeholder="Select tags">
|
|
207
|
+
<cv-select-option value="a11y">Accessibility</cv-select-option>
|
|
208
|
+
<cv-select-option value="perf">Performance</cv-select-option>
|
|
209
|
+
<cv-select-option value="ux">UX</cv-select-option>
|
|
210
|
+
</cv-select>
|
|
211
|
+
|
|
212
|
+
<!-- Disabled option -->
|
|
213
|
+
<cv-select>
|
|
214
|
+
<cv-select-option value="active">Active</cv-select-option>
|
|
215
|
+
<cv-select-option value="archived" disabled>Archived</cv-select-option>
|
|
216
|
+
</cv-select>
|
|
217
|
+
|
|
218
|
+
<!-- Keep open after selection -->
|
|
219
|
+
<cv-select selection-mode="multiple" close-on-select="false">
|
|
220
|
+
<cv-select-option value="a">Option A</cv-select-option>
|
|
221
|
+
<cv-select-option value="b">Option B</cv-select-option>
|
|
222
|
+
</cv-select>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Child Elements
|
|
226
|
+
|
|
227
|
+
### cv-select-option
|
|
228
|
+
|
|
229
|
+
Selectable item within a `cv-select` or `cv-select-group`.
|
|
230
|
+
|
|
231
|
+
#### Anatomy
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
<cv-select-option> (host)
|
|
235
|
+
└── <div part="base" class="option">
|
|
236
|
+
└── <slot>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### Attributes
|
|
240
|
+
|
|
241
|
+
| Attribute | Type | Default | Description |
|
|
242
|
+
| ---------- | ------- | ------- | ------------------------------------------------- |
|
|
243
|
+
| `value` | String | `""` | Option value submitted to the parent select |
|
|
244
|
+
| `disabled` | Boolean | `false` | Prevents selection |
|
|
245
|
+
| `selected` | Boolean | `false` | Reflects selected state (managed by parent) |
|
|
246
|
+
| `active` | Boolean | `false` | Reflects active/focused state (managed by parent) |
|
|
247
|
+
|
|
248
|
+
#### Slots
|
|
249
|
+
|
|
250
|
+
| Slot | Description |
|
|
251
|
+
| ----------- | ----------------- |
|
|
252
|
+
| `(default)` | Option label text |
|
|
253
|
+
|
|
254
|
+
#### CSS Parts
|
|
255
|
+
|
|
256
|
+
| Part | Element | Description |
|
|
257
|
+
| ------ | ------- | ------------------- |
|
|
258
|
+
| `base` | `<div>` | Option root wrapper |
|
|
259
|
+
|
|
260
|
+
#### Visual States
|
|
261
|
+
|
|
262
|
+
| Host selector | Description |
|
|
263
|
+
| ------------------- | ----------------------------------------------- |
|
|
264
|
+
| `:host([active])` | Option has keyboard focus (primary tint at 24%) |
|
|
265
|
+
| `:host([selected])` | Option is selected (primary tint at 32%) |
|
|
266
|
+
| `:host([disabled])` | Option is non-selectable (opacity 0.5) |
|
|
267
|
+
| `:host([hidden])` | Option is hidden when listbox is closed |
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
### cv-select-group
|
|
272
|
+
|
|
273
|
+
Groups related options under a visible label.
|
|
274
|
+
|
|
275
|
+
#### Anatomy
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
<cv-select-group> (host)
|
|
279
|
+
├── <div part="label" class="label"> ← group label text
|
|
280
|
+
└── <slot> ← cv-select-option children
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### Attributes
|
|
284
|
+
|
|
285
|
+
| Attribute | Type | Default | Description |
|
|
286
|
+
| --------- | ------ | ------- | ---------------- |
|
|
287
|
+
| `label` | String | `""` | Group label text |
|
|
288
|
+
|
|
289
|
+
#### Slots
|
|
290
|
+
|
|
291
|
+
| Slot | Description |
|
|
292
|
+
| ----------- | --------------------------- |
|
|
293
|
+
| `(default)` | `cv-select-option` children |
|
|
294
|
+
|
|
295
|
+
#### CSS Parts
|
|
296
|
+
|
|
297
|
+
| Part | Element | Description |
|
|
298
|
+
| ------- | ------- | ------------------------ |
|
|
299
|
+
| `label` | `<div>` | Group label text element |
|
|
300
|
+
|
|
301
|
+
#### Visual States
|
|
302
|
+
|
|
303
|
+
| Host selector | Description |
|
|
304
|
+
| ----------------- | -------------------------------------- |
|
|
305
|
+
| `:host([hidden])` | Group is hidden when listbox is closed |
|