@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,290 @@
1
+ # cv-date-picker
2
+
3
+ Date-time control with an editable combobox trigger and a popup calendar dialog.
4
+
5
+ **Headless:** [`createDatePicker`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/date-picker.md)
6
+
7
+ ## Cross-Spec Consistency
8
+
9
+ This document is the UIKit surface contract for `cv-date-picker`.
10
+
11
+ - Headless `createDatePicker` is the source of truth for state, transitions, keyboard, and ARIA contracts.
12
+ - UIKit reflects headless state into DOM attributes and spreads contract props.
13
+ - UIKit adds only layout, styling, slots, and event adaptation.
14
+
15
+ ## Anatomy
16
+
17
+ ```
18
+ <cv-date-picker> (host)
19
+ └── <div part="base">
20
+ ├── <div part="input-wrap">
21
+ │ ├── <span part="prefix">
22
+ │ │ └── <slot name="prefix">
23
+ │ ├── <span part="label">
24
+ │ │ └── <input part="input">
25
+ │ ├── <span part="suffix">
26
+ │ │ └── <slot name="suffix">
27
+ │ └── <button part="clear-button">
28
+ └── <div part="dialog" hidden>
29
+ ├── <div part="calendar-shell">
30
+ │ ├── <button part="year-nav-button" data-dir="prev">
31
+ │ ├── <button part="month-nav-button" data-dir="prev">
32
+ │ ├── <span part="month-label">
33
+ │ ├── <button part="month-nav-button" data-dir="next">
34
+ │ └── <button part="year-nav-button" data-dir="next">
35
+ ├── <div part="calendar-grid">
36
+ │ └── <button part="calendar-day"> ...42 cells from contracts.getVisibleDays()...</button>
37
+ ├── <div part="time-row">
38
+ │ ├── <input part="hour-input" inputmode="numeric" maxlength="2">
39
+ │ ├── <span part="time-separator">:</span>
40
+ │ └── <input part="minute-input" inputmode="numeric" maxlength="2">
41
+ ├── <div part="actions">
42
+ │ ├── <button part="apply-button">
43
+ │ └── <button part="cancel-button">
44
+ └── <span part="dialog-caption">Apply/CANCEL + keyboard hints</span>
45
+ ```
46
+
47
+ ## Attributes
48
+
49
+ | Attribute | Type | Default | Description |
50
+ | ----------------- | ------- | ------------------------ | ------------------------------------------------------------- |
51
+ | `value` | String | `""` | Current committed ISO value (`YYYY-MM-DDTHH:mm`) when present |
52
+ | `open` | Boolean | `false` | Popup dialog open state |
53
+ | `disabled` | Boolean | `false` | Blocks all interaction |
54
+ | `readonly` | Boolean | `false` | Input editing blocked; calendar/time actions blocked |
55
+ | `required` | Boolean | `false` | Required marker for form/validation |
56
+ | `placeholder` | String | `"Select date and time"` | Placeholder shown in the input |
57
+ | `size` | String | `"medium"` | Size: `small` \| `medium` \| `large` |
58
+ | `locale` | String | `"en-US"` | Locale for formatting/parsing hook context |
59
+ | `time-zone` | String | `"local"` | Time basis: `local` \| `utc` |
60
+ | `min` | String | `""` | Minimum accepted date-time (ISO) |
61
+ | `max` | String | `""` | Maximum accepted date-time (ISO) |
62
+ | `minute-step` | Number | `1` | Minute granularity for draft editing |
63
+ | `hour-cycle` | Number | `24` | Time input style: `12` \| `24` |
64
+ | `close-on-escape` | Boolean | `true` | Closes dialog on `Escape` |
65
+ | `aria-label` | String | `""` | Input and dialog accessible label |
66
+ | `input-invalid` | Boolean | `false` | Read-only derived state: input parse/validation failed |
67
+ | `has-value` | Boolean | `false` | Read-only derived state: committed value present |
68
+
69
+ ## Sizes
70
+
71
+ | Size | `--cv-date-picker-input-min-height` | `--cv-date-picker-input-padding-inline` | `--cv-date-picker-input-padding-block` |
72
+ | -------- | ----------------------------------- | --------------------------------------- | -------------------------------------- |
73
+ | `small` | `30px` | `var(--cv-space-2, 8px)` | `var(--cv-space-1, 4px)` |
74
+ | `medium` | `36px` | `var(--cv-space-3, 12px)` | `var(--cv-space-2, 8px)` |
75
+ | `large` | `42px` | `var(--cv-space-4, 16px)` | `var(--cv-space-2, 8px)` |
76
+
77
+ ## Slots
78
+
79
+ | Slot | Description |
80
+ | -------- | ---------------------------- |
81
+ | `prefix` | Icon or element before input |
82
+ | `suffix` | Icon or element after input |
83
+
84
+ ## CSS Parts
85
+
86
+ | Part | Element | Description |
87
+ | ------------------ | ---------- | ---------------------------------------- |
88
+ | `base` | `<div>` | Root layout wrapper |
89
+ | `input-wrap` | `<div>` | Input + optional action row container |
90
+ | `prefix` | `<span>` | Prefix icon/element wrapper |
91
+ | `label` | `<span>` | Main text wrapper around the input value |
92
+ | `cv-input` | `<input>` | Editable combobox input |
93
+ | `suffix` | `<span>` | Suffix icon/element wrapper |
94
+ | `clear-button` | `<button>` | Clear committed value |
95
+ | `dialog` | `<div>` | Popup shell for calendar/time panel |
96
+ | `calendar-shell` | `<div>` | Calendar header + grid wrapper |
97
+ | `month-label` | `<span>` | Visible month/year label |
98
+ | `month-nav-button` | `<button>` | Previous/next month navigation button |
99
+ | `year-nav-button` | `<button>` | Previous/next year navigation button |
100
+ | `calendar-grid` | `<div>` | Calendar grid surface |
101
+ | `calendar-day` | `<button>` | Day cell for each visible calendar day |
102
+ | `time-row` | `<div>` | Time editing row |
103
+ | `hour-input` | `<input>` | Hour segment editor |
104
+ | `minute-input` | `<input>` | Minute segment editor |
105
+ | `time-separator` | `<span>` | Time separator between segments |
106
+ | `actions` | `<div>` | Footer actions area |
107
+ | `apply-button` | `<button>` | Commit draft selection |
108
+ | `cancel-button` | `<button>` | Discard draft selection |
109
+ | `dialog-caption` | `<span>` | Optional helper text/status label |
110
+
111
+ ## CSS Custom Properties
112
+
113
+ | Property | Default | Description |
114
+ | --------------------------------------- | -------------------------------- | -------------------------------- |
115
+ | `--cv-date-picker-min-width` | `260px` | Host minimum width |
116
+ | `--cv-date-picker-input-min-height` | `36px` | Input control minimum block size |
117
+ | `--cv-date-picker-input-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal input padding |
118
+ | `--cv-date-picker-input-padding-block` | `var(--cv-space-2, 8px)` | Vertical input padding |
119
+ | `--cv-date-picker-dialog-width` | `min(560px, calc(100vw - 32px))` | Dialog inline size |
120
+ | `--cv-date-picker-calendar-size` | `304px` | Calendar body block size |
121
+ | `--cv-date-picker-day-size` | `34px` | Calendar cell size |
122
+ | `--cv-date-picker-day-gap` | `var(--cv-space-1, 4px)` | Calendar grid gap |
123
+ | `--cv-date-picker-font-size` | `inherit` | Component font size |
124
+ | `--cv-date-picker-border-radius` | `var(--cv-radius-md, 10px)` | Dialog/calendar border radius |
125
+ | `--cv-date-picker-button-gap` | `var(--cv-space-2, 8px)` | Spacing between footer actions |
126
+
127
+ Additionally, styles may rely on theme tokens (no separate listing required here if already defined globally):
128
+ `--cv-color-border`, `--cv-color-surface`, `--cv-color-text`, `--cv-color-text-muted`, `--cv-color-primary`, `--cv-color-danger`, `--cv-space-2`, `--cv-space-3`, `--cv-space-4`, `--cv-duration-fast`, `--cv-easing-standard`, `--cv-radius-sm`, `--cv-radius-md`.
129
+
130
+ ## Visual States
131
+
132
+ | Host selector | Description |
133
+ | ------------------------ | ------------------------- |
134
+ | `:host([disabled])` | Disabled, non-interactive |
135
+ | `:host([readonly])` | Read-only input mode |
136
+ | `:host([required])` | Required marker active |
137
+ | `:host([open])` | Popup dialog visible |
138
+ | `:host([size="small"])` | Small size tokens used |
139
+ | `:host([size="large"])` | Large size tokens used |
140
+ | `:host([input-invalid])` | Input value invalid state |
141
+ | `:host([has-value])` | Committed value present |
142
+
143
+ ## ARIA Contract
144
+
145
+ | Element | Attribute | Value |
146
+ | ---------------- | ----------------------- | -------------------------------------------------- |
147
+ | Input | `role` | `combobox` |
148
+ | Input | `aria-haspopup` | `dialog` |
149
+ | Input | `aria-expanded` | `true` / `false` |
150
+ | Input | `aria-controls` | Dialog id |
151
+ | Input | `aria-activedescendant` | Focused day id when dialog is open |
152
+ | Input | `aria-invalid` | `true` when input cannot be parsed or out of range |
153
+ | Input | `aria-required` | `true` when `required` |
154
+ | Input | `aria-label` | Provided `aria-label` string |
155
+ | Dialog | `role` | `dialog` |
156
+ | Dialog | `aria-modal` | `true` |
157
+ | Dialog | `aria-label` | Provided `aria-label` or default text |
158
+ | Calendar grid | `role` | `grid` |
159
+ | Calendar day | `role` | `gridcell` |
160
+ | Calendar day | `tabindex` | `0` / `-1` |
161
+ | Calendar day | `aria-selected` | `true` / `false` |
162
+ | Calendar day | `aria-disabled` | `true` when out of range / blocked |
163
+ | Calendar day | `aria-current` | `date` when current day |
164
+ | Month nav button | `role` | `button` |
165
+ | Year nav button | `role` | `button` |
166
+ | Time inputs | `type` | `text` |
167
+ | Time inputs | `inputmode` | `numeric` |
168
+ | Action buttons | `role` | `button` |
169
+
170
+ ## Events
171
+
172
+ | Event | Detail | Description |
173
+ | ----------- | ------------------------------------------------------------------------ | ------------------------------------------ |
174
+ | `cv-input` | `{ value: string, inputValue: string, open: boolean, invalid: boolean }` | Fires when the user changes the input text |
175
+ | `cv-change` | `{ value: string, previousValue: string, source: "input" \| "dialog" }` | Fires when committed value changes |
176
+
177
+ `cv-input` and `cv-change` are only emitted for user-modifiable state updates.
178
+
179
+ ## Reactive State Mapping
180
+
181
+ `cv-date-picker` is a visual adapter over headless `createDatePicker`.
182
+
183
+ ### UIKit properties to headless actions/options
184
+
185
+ | UIKit Property | Direction | Headless Binding |
186
+ | ----------------- | ------------------ | -------------------------------------------------------------- | --- | ------ |
187
+ | `value` | attr → action | `actions.setInputValue(value)` then `actions.commitInput()` |
188
+ | `open` | attr → action | `actions.open()` / `actions.close()` |
189
+ | `disabled` | attr → action | `actions.setDisabled(value)` |
190
+ | `readonly` | attr → action | `actions.setReadonly(value)` |
191
+ | `required` | attr → action | `actions.setRequired(value)` |
192
+ | `placeholder` | attr → action | `actions.setPlaceholder(value)` |
193
+ | `locale` | attr → action | `actions.setLocale(value)` |
194
+ | `time-zone` | attr → action | `actions.setTimeZone(value)` |
195
+ | `min` | attr → action | `actions.setMin(value | | null)` |
196
+ | `max` | attr → action | `actions.setMax(value | | null)` |
197
+ | `minute-step` | attr → action | `actions.setMinuteStep(value)` |
198
+ | `hour-cycle` | attr → action | `actions.setHourCycle(value as 12 \/ 24)` |
199
+ | `close-on-escape` | attr → option | Passed as `closeOnEscape` during `createDatePicker(...)` setup |
200
+ | `aria-label` | attr → option | Passed as `ariaLabel` during `createDatePicker(...)` setup |
201
+ | `size` | attr → DOM/styling | Local styling tokens only |
202
+
203
+ ### Headless state to DOM reflection
204
+
205
+ | Headless State | Direction | DOM Reflection |
206
+ | ------------------------------------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------- |
207
+ | `state.committedValue()` | state → attr | `[value]` and input value text |
208
+ | `state.isOpen()` | state → attr | `[open]` |
209
+ | `state.disabled()` | state → attr | `[disabled]` |
210
+ | `state.readonly()` | state → attr | `[readonly]` |
211
+ | `state.required()` | state → attr | `[required]` |
212
+ | `state.hasCommittedSelection()` | state → attr | `[has-value]` |
213
+ | `state.inputInvalid()` | state → attr | `[input-invalid]` |
214
+ | `state.min()`, `state.max()`, `state.locale()`, `state.timeZone()`, `state.hourCycle()`, `state.minuteStep()` | state → render | Applied to internal dialog/time controls and contract props |
215
+ | `state.visibleDays()` | state → render | Drives `[part="calendar-day"]` list |
216
+ | `state.focusedDate()`, `state.displayedMonth()`, `state.displayedYear()` | state → render | Calendar focus/visible range rendering |
217
+
218
+ ### Contract props spreading
219
+
220
+ - `contracts.getInputProps()` is spread onto `[part="input"]`.
221
+ - `contracts.getDialogProps()` is spread onto `[part="dialog"]`.
222
+ - `contracts.getCalendarGridProps()` is spread onto `[part="calendar-grid"]`.
223
+ - `contracts.getMonthNavButtonProps()` and `contracts.getYearNavButtonProps()` are spread onto corresponding nav buttons.
224
+ - `contracts.getCalendarDayProps(date)` is spread onto each `[part="calendar-day"]`.
225
+ - `contracts.getHourInputProps()` / `contracts.getMinuteInputProps()` are spread onto `[part="hour-input"]` / `[part="minute-input"]`.
226
+ - `contracts.getApplyButtonProps()` and `contracts.getCancelButtonProps()` are spread onto `[part="apply-button"]` / `[part="cancel-button"]`.
227
+ - `contracts.getClearButtonProps()` is spread onto `[part="clear-button"]`.
228
+ - `contracts.getVisibleDays()` provides the day matrix for rendering.
229
+
230
+ ### UIKit-only concerns
231
+
232
+ UIKit owns rendering and animation only.
233
+
234
+ - Layout and spacing between input, calendar, and footer.
235
+ - Visual-only styling (`:host` states and CSS parts).
236
+ - Optional `dialog-caption` content text.
237
+
238
+ ## Behavioral Contract
239
+
240
+ ### Combobox + Dialog pattern
241
+
242
+ - Input opens/closes popup dialog with `ArrowDown`, `ArrowUp`, or `Space`.
243
+ - Dialog remains a separate popup surface bound via `aria-controls` / `aria-expanded`.
244
+ - Calendar selection updates draft state only while dialog is open.
245
+ - `Apply` commits draft, `Cancel` restores committed state.
246
+
247
+ ### Dual Commit (editable input + calendar)
248
+
249
+ - `commitInput()` and `commitDraft()` are the only ways to mutate committed value.
250
+ - Typing and `Enter` in closed state only affects input text until input commit succeeds.
251
+ - Calendar/time edits while open are isolated draft updates and are not committed until explicit `Apply`.
252
+ - `clear-button` clears committed value and input text via headless `clear()` action.
253
+
254
+ ### Keyboard in open dialog
255
+
256
+ - `Escape` closes dialog (respecting `close-on-escape`).
257
+ - Calendar arrows/page keys use grid navigation as provided by headless contracts.
258
+ - `Enter`/`Space` on calendar day selects draft day.
259
+ - `Enter` in minute/hour inputs commits draft.
260
+
261
+ ### Disabled / read-only
262
+
263
+ - `disabled` and `readonly` block all mutating actions and interactions.
264
+
265
+ ## Usage
266
+
267
+ ```html
268
+ <cv-date-picker
269
+ aria-label="Flight date"
270
+ placeholder="Select departure date and time"
271
+ locale="en-US"
272
+ minute-step="15"
273
+ open
274
+ >
275
+ <icon-calendar slot="prefix"></icon-calendar>
276
+ <icon-clock slot="suffix"></icon-clock>
277
+ </cv-date-picker>
278
+
279
+ <cv-date-picker
280
+ time-zone="utc"
281
+ hour-cycle="24"
282
+ min="2026-01-01T00:00"
283
+ max="2026-12-31T23:59"
284
+ disabled
285
+ ></cv-date-picker>
286
+
287
+ <cv-date-picker locale="ru-RU" size="large">
288
+ <icon-schedule slot="suffix"></icon-schedule>
289
+ </cv-date-picker>
290
+ ```
@@ -0,0 +1,184 @@
1
+ # cv-dialog
2
+
3
+ Modal or non-modal dialog overlay for presenting focused content, confirmations, or alerts.
4
+
5
+ **Headless:** [`createDialog`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/dialog.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-dialog> (host)
11
+ ├── <button part="trigger">
12
+ │ └── <slot name="trigger">
13
+ └── <div part="overlay"> (hidden when closed)
14
+ └── <section part="content" role="dialog|alertdialog">
15
+ ├── <header part="header">
16
+ │ ├── <h2 part="title" id="...">
17
+ │ │ └── <slot name="title">
18
+ │ ├── <p part="description" id="...">
19
+ │ │ └── <slot name="description">
20
+ │ └── <button part="header-close" aria-label="Close">
21
+ │ └── <slot name="header-close">
22
+ ├── <div part="body">
23
+ │ └── <slot>
24
+ └── <footer part="footer">
25
+ └── <slot name="footer">
26
+ ```
27
+
28
+ ## Attributes
29
+
30
+ | Attribute | Type | Default | Description |
31
+ | -------------------------- | ------- | ---------- | ---------------------------------------------------------- |
32
+ | `open` | Boolean | `false` | Whether the dialog is visible |
33
+ | `modal` | Boolean | `true` | Enables modal behavior (focus trap, scroll lock, backdrop) |
34
+ | `type` | String | `"dialog"` | ARIA role type: `dialog` \| `alertdialog` |
35
+ | `close-on-escape` | Boolean | `true` | Whether Escape key closes the dialog |
36
+ | `close-on-outside-pointer` | Boolean | `true` | Whether clicking outside closes the dialog |
37
+ | `close-on-outside-focus` | Boolean | `true` | Whether focusing outside closes the dialog |
38
+ | `initial-focus-id` | String | — | Id of element to focus when dialog opens |
39
+ | `no-header` | Boolean | `false` | Hides the header (title, description, header close button) |
40
+
41
+ ## Slots
42
+
43
+ | Slot | Description |
44
+ | -------------- | -------------------------------------------------------- |
45
+ | `(default)` | Dialog body content |
46
+ | `trigger` | Content for the trigger button |
47
+ | `title` | Dialog title text |
48
+ | `description` | Description text below the title |
49
+ | `header-close` | Icon content for the header close button (defaults to X) |
50
+ | `footer` | Footer content (action buttons, etc.) |
51
+
52
+ ## CSS Parts
53
+
54
+ | Part | Element | Description |
55
+ | -------------- | ----------- | ----------------------------------------------------------------- |
56
+ | `trigger` | `<button>` | Trigger button that opens the dialog |
57
+ | `overlay` | `<div>` | Backdrop/overlay container |
58
+ | `content` | `<section>` | Dialog content panel with `role="dialog"` or `role="alertdialog"` |
59
+ | `header` | `<header>` | Header area containing title, description, and close button |
60
+ | `title` | `<h2>` | Dialog title element |
61
+ | `description` | `<p>` | Dialog description element |
62
+ | `header-close` | `<button>` | Header close icon button |
63
+ | `body` | `<div>` | Body content area |
64
+ | `footer` | `<footer>` | Footer area for user-provided action buttons |
65
+
66
+ ## CSS Custom Properties
67
+
68
+ | Property | Default | Description |
69
+ | ---------------------------- | --------------------------------------------- | ----------------------------------- |
70
+ | `--cv-dialog-width` | `min(560px, calc(100vw - 32px))` | Preferred dialog inline size |
71
+ | `--cv-dialog-max-height` | `calc(100dvh - 32px)` | Maximum block size before scrolling |
72
+ | `--cv-dialog-header-spacing` | `var(--cv-space-4, 16px)` | Header padding |
73
+ | `--cv-dialog-body-spacing` | `var(--cv-space-4, 16px)` | Body padding |
74
+ | `--cv-dialog-footer-spacing` | `var(--cv-space-4, 16px)` | Footer padding |
75
+ | `--cv-dialog-overlay-color` | `color-mix(in oklab, black 56%, transparent)` | Backdrop overlay color |
76
+ | `--cv-dialog-border-radius` | `var(--cv-radius-lg, 14px)` | Panel border radius |
77
+
78
+ ## Visual States
79
+
80
+ | Host selector | Description |
81
+ | ----------------------------- | ----------------------------------------------------- |
82
+ | `:host([open])` | Dialog visible, overlay shown |
83
+ | `:host([modal])` | Modal mode active (focus trap, scroll lock, backdrop) |
84
+ | `:host([type="alertdialog"])` | Alert dialog mode with `role="alertdialog"` |
85
+ | `:host([no-header])` | Header section hidden |
86
+
87
+ ## Events
88
+
89
+ | Event | Detail | Description |
90
+ | --------------- | ----------------- | -------------------------------------------------- |
91
+ | `cv-input` | `{open: boolean}` | Fires when open state changes via user interaction |
92
+ | `cv-change` | `{open: boolean}` | Fires when open state commits |
93
+ | `cv-show` | — | Fires when dialog begins to open |
94
+ | `cv-after-show` | — | Fires after dialog open animation completes |
95
+ | `cv-hide` | — | Fires when dialog begins to close |
96
+ | `cv-after-hide` | — | Fires after dialog close animation completes |
97
+
98
+ `cv-input` and `cv-change` fire only for user-initiated state changes (trigger click, Escape, outside pointer, outside focus, header close). Programmatic `open` attribute changes do not emit these events.
99
+
100
+ ## Reactive State Mapping
101
+
102
+ `cv-dialog` is a visual adapter over headless `createDialog`.
103
+
104
+ | UIKit Property | Direction | Headless Binding |
105
+ | -------------------------- | ------------- | ------------------------------------------------------------ |
106
+ | `open` | attr → action | `actions.open()` / `actions.close()` |
107
+ | `modal` | attr → option | passed as `isModal` in `createDialog(options)` |
108
+ | `type` | attr → option | passed as `type` in `createDialog(options)` |
109
+ | `close-on-escape` | attr → option | passed as `closeOnEscape` in `createDialog(options)` |
110
+ | `close-on-outside-pointer` | attr → option | passed as `closeOnOutsidePointer` in `createDialog(options)` |
111
+ | `close-on-outside-focus` | attr → option | passed as `closeOnOutsideFocus` in `createDialog(options)` |
112
+ | `initial-focus-id` | attr → option | passed as `initialFocusId` in `createDialog(options)` |
113
+ | `no-header` | attr → DOM | controls header visibility (UIKit-only, no headless binding) |
114
+
115
+ | Headless State | Direction | DOM Reflection |
116
+ | ------------------------------ | -------------- | --------------------------------------------- |
117
+ | `state.isOpen()` | state → attr | `[open]` host attribute |
118
+ | `state.isModal()` | state → attr | `[modal]` host attribute |
119
+ | `state.type()` | state → attr | `[type]` host attribute |
120
+ | `state.isFocusTrapped()` | state → effect | activates focus trap within the dialog |
121
+ | `state.shouldLockScroll()` | state → effect | applies `overflow: hidden` to `document.body` |
122
+ | `state.restoreTargetId()` | state → effect | focuses the trigger element on close |
123
+ | `state.initialFocusTargetId()` | state → effect | focuses the specified element on open |
124
+
125
+ - `contracts.getTriggerProps()` is spread onto `[part="trigger"]` to apply `role`, `aria-haspopup`, `aria-expanded`, `aria-controls`, `tabindex`, and click/keydown handlers.
126
+ - `contracts.getOverlayProps()` is spread onto `[part="overlay"]` to apply `hidden`, `data-open`, and outside pointer/focus handlers.
127
+ - `contracts.getContentProps()` is spread onto `[part="content"]` to apply `role` (`dialog` or `alertdialog`), `aria-modal`, `aria-labelledby`, `aria-describedby`, `tabindex`, and keydown handler.
128
+ - `contracts.getTitleProps()` is spread onto `[part="title"]` to apply the `id` for `aria-labelledby`.
129
+ - `contracts.getDescriptionProps()` is spread onto `[part="description"]` to apply the `id` for `aria-describedby`.
130
+ - `contracts.getHeaderCloseButtonProps()` is spread onto `[part="header-close"]` to apply `role`, `tabindex`, `aria-label: 'Close'`, and click handler.
131
+ - UIKit dispatches `cv-input` and `cv-change` events by observing `isOpen` changes triggered by user interaction (not by controlled `open` attribute changes).
132
+ - UIKit dispatches `cv-show`/`cv-after-show`/`cv-hide`/`cv-after-hide` lifecycle events to bracket CSS transitions.
133
+ - UIKit owns scroll lock implementation, focus trap implementation, focus restoration, backdrop rendering, and CSS transitions — headless provides signals, UIKit applies side effects.
134
+
135
+ ## Usage
136
+
137
+ ```html
138
+ <!-- Basic dialog -->
139
+ <cv-dialog>
140
+ <span slot="trigger">Open</span>
141
+ <span slot="title">Confirm action</span>
142
+ <span slot="description">Are you sure you want to proceed?</span>
143
+ <p>This action cannot be undone.</p>
144
+ <div slot="footer">
145
+ <cv-button variant="ghost">Cancel</cv-button>
146
+ <cv-button variant="primary">Confirm</cv-button>
147
+ </div>
148
+ </cv-dialog>
149
+
150
+ <!-- Alert dialog -->
151
+ <cv-dialog type="alertdialog">
152
+ <span slot="trigger">Delete</span>
153
+ <span slot="title">Delete item?</span>
154
+ <span slot="description">This will permanently delete the item.</span>
155
+ <div slot="footer">
156
+ <cv-button variant="ghost">Cancel</cv-button>
157
+ <cv-button variant="danger">Delete</cv-button>
158
+ </div>
159
+ </cv-dialog>
160
+
161
+ <!-- Non-modal dialog -->
162
+ <cv-dialog modal="false">
163
+ <span slot="trigger">Show info</span>
164
+ <span slot="title">Information</span>
165
+ <p>This dialog does not block the page.</p>
166
+ </cv-dialog>
167
+
168
+ <!-- Without header -->
169
+ <cv-dialog no-header>
170
+ <span slot="trigger">Quick action</span>
171
+ <p>Minimal dialog with body content only.</p>
172
+ <div slot="footer">
173
+ <cv-button variant="primary">OK</cv-button>
174
+ </div>
175
+ </cv-dialog>
176
+
177
+ <!-- Custom header close icon -->
178
+ <cv-dialog>
179
+ <span slot="trigger">Open</span>
180
+ <span slot="title">Settings</span>
181
+ <icon-close slot="header-close"></icon-close>
182
+ <p>Dialog content here.</p>
183
+ </cv-dialog>
184
+ ```
@@ -0,0 +1,151 @@
1
+ # cv-disclosure
2
+
3
+ Expandable panel that controls the visibility of a content area, with support for exclusive accordion-like grouping via a shared name.
4
+
5
+ **Headless:** [`createDisclosure`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/disclosure.md)
6
+
7
+ ## Anatomy
8
+
9
+ ```
10
+ <cv-disclosure> (host)
11
+ └── <div part="base">
12
+ ├── <div part="trigger" role="button">
13
+ │ ├── <slot name="trigger">
14
+ │ └── <span part="trigger-icon" aria-hidden="true">
15
+ └── <div part="panel">
16
+ └── <slot>
17
+ ```
18
+
19
+ ## Attributes
20
+
21
+ | Attribute | Type | Default | Description |
22
+ | ---------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
23
+ | `open` | Boolean | `false` | Whether the panel content is visible |
24
+ | `disabled` | Boolean | `false` | Prevents user interaction with the trigger |
25
+ | `name` | String | `""` | Group name for exclusive accordion-like behavior; when set, opening this disclosure closes all others sharing the same `name` |
26
+
27
+ ## Slots
28
+
29
+ | Slot | Description |
30
+ | ----------- | --------------------------------------------------- |
31
+ | `(default)` | Panel content displayed when the disclosure is open |
32
+ | `trigger` | Label content rendered inside the trigger |
33
+
34
+ ## CSS Parts
35
+
36
+ | Part | Element | Description |
37
+ | -------------- | -------- | ------------------------------------------------ |
38
+ | `base` | `<div>` | Root layout wrapper |
39
+ | `trigger` | `<div>` | Interactive trigger element with `role="button"` |
40
+ | `trigger-icon` | `<span>` | Chevron/arrow indicator that rotates when open |
41
+ | `panel` | `<div>` | Collapsible content container |
42
+
43
+ ## CSS Custom Properties
44
+
45
+ | Property | Default | Description |
46
+ | -------------------------- | --------------------------------- | ----------------------------------------------- |
47
+ | `--cv-disclosure-duration` | `var(--cv-duration-fast, 120ms)` | Duration of expand/collapse transitions |
48
+ | `--cv-disclosure-easing` | `var(--cv-easing-standard, ease)` | Easing function for expand/collapse transitions |
49
+
50
+ Additionally, component styles depend on theme tokens through fallback values:
51
+
52
+ | Theme Property | Default | Description |
53
+ | ----------------------------- | --------- | -------------------------------------------------------------- |
54
+ | `--cv-color-border` | `#2a3245` | Border color for trigger and panel |
55
+ | `--cv-color-surface` | `#141923` | Trigger background color |
56
+ | `--cv-color-surface-elevated` | `#1d2432` | Panel background color |
57
+ | `--cv-color-text` | `#e8ecf6` | Default text color |
58
+ | `--cv-color-text-muted` | `#9aa6bf` | Trigger icon color |
59
+ | `--cv-color-primary` | `#65d7ff` | Focus outline color |
60
+ | `--cv-radius-sm` | `6px` | Border radius for trigger and panel |
61
+ | `--cv-space-2` | `8px` | Gap between trigger and panel |
62
+ | `--cv-space-3` | `12px` | Inline padding for trigger; block and inline padding for panel |
63
+
64
+ ## Visual States
65
+
66
+ | Host selector | Description |
67
+ | ------------------- | ----------------------------------------------------------------------- |
68
+ | `:host([open])` | Panel visible; trigger icon rotated 90deg |
69
+ | `:host([disabled])` | Trigger has `opacity: 0.55`, `cursor: not-allowed`; interaction blocked |
70
+
71
+ ## Events
72
+
73
+ | Event | Detail | Description |
74
+ | ----------- | ----------------- | -------------------------------------------------------------- |
75
+ | `cv-input` | `{open: boolean}` | Fires immediately when user interaction changes the open state |
76
+ | `cv-change` | `{open: boolean}` | Fires when the open state commits after user interaction |
77
+
78
+ Events fire only on user-initiated state changes (click, keyboard). Programmatic calls to `show()` / `hide()` or attribute changes do not fire events.
79
+
80
+ ## Imperative API
81
+
82
+ | Method | Return | Description |
83
+ | -------- | ------ | --------------------------------------------------------- |
84
+ | `show()` | `void` | Opens the panel; delegates to headless `actions.open()` |
85
+ | `hide()` | `void` | Closes the panel; delegates to headless `actions.close()` |
86
+
87
+ ## Reactive State Mapping
88
+
89
+ `cv-disclosure` is a visual adapter over headless `createDisclosure`.
90
+
91
+ | UIKit Property | Direction | Headless Binding |
92
+ | -------------- | -------------- | ------------------------------------------------------- |
93
+ | `open` | attr -> action | `actions.open()` / `actions.close()` depending on value |
94
+ | `disabled` | attr -> action | `actions.setDisabled(value)` |
95
+ | `name` | attr -> action | `actions.setName(value)` |
96
+
97
+ | Headless State | Direction | DOM Reflection |
98
+ | -------------------- | ------------- | --------------------------- |
99
+ | `state.isOpen()` | state -> attr | `[open]` host attribute |
100
+ | `state.isDisabled()` | state -> attr | `[disabled]` host attribute |
101
+ | `state.name()` | state -> attr | `[name]` host attribute |
102
+
103
+ - `contracts.getTriggerProps()` is spread onto the inner `[part="trigger"]` element to apply `role`, `tabindex`, `aria-expanded`, `aria-controls`, `aria-disabled`, and keyboard/click handlers.
104
+ - `contracts.getPanelProps()` is spread onto the inner `[part="panel"]` element to apply `id`, `aria-labelledby`, and `hidden`.
105
+ - `show()` and `hide()` delegate directly to `actions.open()` and `actions.close()` without firing `cv-input`/`cv-change` events.
106
+ - `actions.destroy()` must be called in `disconnectedCallback` to unregister from the name group registry.
107
+ - UIKit does not own toggle, keyboard, or grouping logic; headless state is the source of truth.
108
+
109
+ ## Usage
110
+
111
+ ```html
112
+ <!-- Basic disclosure -->
113
+ <cv-disclosure>
114
+ <span slot="trigger">More details</span>
115
+ Hidden content revealed on expand.
116
+ </cv-disclosure>
117
+
118
+ <!-- Initially open -->
119
+ <cv-disclosure open>
120
+ <span slot="trigger">Section</span>
121
+ This content is visible by default.
122
+ </cv-disclosure>
123
+
124
+ <!-- Disabled -->
125
+ <cv-disclosure disabled>
126
+ <span slot="trigger">Locked section</span>
127
+ Cannot be toggled.
128
+ </cv-disclosure>
129
+
130
+ <!-- Accordion group (exclusive) -->
131
+ <cv-disclosure name="faq">
132
+ <span slot="trigger">Question 1</span>
133
+ Answer 1.
134
+ </cv-disclosure>
135
+
136
+ <cv-disclosure name="faq">
137
+ <span slot="trigger">Question 2</span>
138
+ Answer 2.
139
+ </cv-disclosure>
140
+
141
+ <cv-disclosure name="faq">
142
+ <span slot="trigger">Question 3</span>
143
+ Answer 3.
144
+ </cv-disclosure>
145
+
146
+ <!-- Custom animation timing -->
147
+ <cv-disclosure style="--cv-disclosure-duration: 300ms; --cv-disclosure-easing: cubic-bezier(0.4, 0, 0.2, 1);">
148
+ <span slot="trigger">Slow reveal</span>
149
+ Content with custom animation.
150
+ </cv-disclosure>
151
+ ```