@agent-ui-kit/components 0.0.3

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 (271) hide show
  1. package/dist/base-styles.css +8 -0
  2. package/dist/component-styles.css +147 -0
  3. package/dist/controls/_base/index.d.ts +3 -0
  4. package/dist/controls/_base/index.js +9 -0
  5. package/dist/controls/_base/indicator-element.d.ts +132 -0
  6. package/dist/controls/_base/indicator-element.js +158 -0
  7. package/dist/controls/_base/listbox-element.d.ts +70 -0
  8. package/dist/controls/_base/listbox-element.js +74 -0
  9. package/dist/controls/_base/range-element.d.ts +120 -0
  10. package/dist/controls/_base/range-element.js +158 -0
  11. package/dist/controls/_surface/container-box.css +301 -0
  12. package/dist/controls/_surface/container.css +156 -0
  13. package/dist/controls/_token-surface/token-surface.d.ts +35 -0
  14. package/dist/controls/_token-surface/token-surface.js +97 -0
  15. package/dist/controls/attachment/attachment-meta.d.ts +8 -0
  16. package/dist/controls/attachment/attachment-meta.js +84 -0
  17. package/dist/controls/attachment/attachment.css +91 -0
  18. package/dist/controls/attachment/attachment.d.ts +40 -0
  19. package/dist/controls/attachment/attachment.js +70 -0
  20. package/dist/controls/avatar/avatar-initials.d.ts +4 -0
  21. package/dist/controls/avatar/avatar-initials.js +30 -0
  22. package/dist/controls/avatar/avatar.css +87 -0
  23. package/dist/controls/avatar/avatar.d.ts +40 -0
  24. package/dist/controls/avatar/avatar.js +77 -0
  25. package/dist/controls/badge/badge.css +175 -0
  26. package/dist/controls/badge/badge.d.ts +36 -0
  27. package/dist/controls/badge/badge.js +71 -0
  28. package/dist/controls/bar-chart/bar-chart.css +110 -0
  29. package/dist/controls/bar-chart/bar-chart.d.ts +26 -0
  30. package/dist/controls/bar-chart/bar-chart.js +80 -0
  31. package/dist/controls/bar-chart/bar-math.d.ts +31 -0
  32. package/dist/controls/bar-chart/bar-math.js +106 -0
  33. package/dist/controls/button/button.css +263 -0
  34. package/dist/controls/button/button.d.ts +61 -0
  35. package/dist/controls/button/button.js +115 -0
  36. package/dist/controls/calendar/calendar.css +495 -0
  37. package/dist/controls/calendar/calendar.d.ts +161 -0
  38. package/dist/controls/calendar/calendar.js +1004 -0
  39. package/dist/controls/card/card-content.d.ts +24 -0
  40. package/dist/controls/card/card-content.js +133 -0
  41. package/dist/controls/card/card-footer.d.ts +3 -0
  42. package/dist/controls/card/card-footer.js +16 -0
  43. package/dist/controls/card/card-header.d.ts +3 -0
  44. package/dist/controls/card/card-header.js +18 -0
  45. package/dist/controls/card/card.css +445 -0
  46. package/dist/controls/card/card.d.ts +51 -0
  47. package/dist/controls/card/card.js +61 -0
  48. package/dist/controls/checkbox/checkbox.css +204 -0
  49. package/dist/controls/checkbox/checkbox.d.ts +114 -0
  50. package/dist/controls/checkbox/checkbox.js +130 -0
  51. package/dist/controls/code/code.css +72 -0
  52. package/dist/controls/code/code.d.ts +23 -0
  53. package/dist/controls/code/code.js +40 -0
  54. package/dist/controls/color-picker/color-picker.css +209 -0
  55. package/dist/controls/color-picker/color-picker.d.ts +84 -0
  56. package/dist/controls/color-picker/color-picker.js +545 -0
  57. package/dist/controls/color-picker/color.d.ts +40 -0
  58. package/dist/controls/color-picker/color.js +159 -0
  59. package/dist/controls/column/column.css +151 -0
  60. package/dist/controls/column/column.d.ts +107 -0
  61. package/dist/controls/column/column.js +57 -0
  62. package/dist/controls/combo-box/combo-box.css +397 -0
  63. package/dist/controls/combo-box/combo-box.d.ts +133 -0
  64. package/dist/controls/combo-box/combo-box.js +680 -0
  65. package/dist/controls/command-modal/command-modal.css +226 -0
  66. package/dist/controls/command-modal/command-modal.d.ts +42 -0
  67. package/dist/controls/command-modal/command-modal.js +315 -0
  68. package/dist/controls/disclosure/disclosure.css +162 -0
  69. package/dist/controls/disclosure/disclosure.d.ts +37 -0
  70. package/dist/controls/disclosure/disclosure.js +207 -0
  71. package/dist/controls/field/field.css +140 -0
  72. package/dist/controls/field/field.d.ts +27 -0
  73. package/dist/controls/field/field.js +228 -0
  74. package/dist/controls/form-provider/form-provider.css +40 -0
  75. package/dist/controls/form-provider/form-provider.d.ts +42 -0
  76. package/dist/controls/form-provider/form-provider.js +105 -0
  77. package/dist/controls/grid/grid.css +72 -0
  78. package/dist/controls/grid/grid.d.ts +60 -0
  79. package/dist/controls/grid/grid.js +56 -0
  80. package/dist/controls/icon/icon.css +37 -0
  81. package/dist/controls/icon/icon.d.ts +25 -0
  82. package/dist/controls/icon/icon.js +55 -0
  83. package/dist/controls/index.d.ts +57 -0
  84. package/dist/controls/index.js +136 -0
  85. package/dist/controls/ladder/ladder.css +89 -0
  86. package/dist/controls/ladder/ladder.d.ts +27 -0
  87. package/dist/controls/ladder/ladder.js +78 -0
  88. package/dist/controls/list/list.css +118 -0
  89. package/dist/controls/list/list.d.ts +84 -0
  90. package/dist/controls/list/list.js +48 -0
  91. package/dist/controls/menu/menu.css +195 -0
  92. package/dist/controls/menu/menu.d.ts +44 -0
  93. package/dist/controls/menu/menu.js +248 -0
  94. package/dist/controls/modal/modal.css +99 -0
  95. package/dist/controls/modal/modal.d.ts +61 -0
  96. package/dist/controls/modal/modal.js +155 -0
  97. package/dist/controls/popover/popover.css +83 -0
  98. package/dist/controls/popover/popover.d.ts +44 -0
  99. package/dist/controls/popover/popover.js +164 -0
  100. package/dist/controls/progress/progress.css +106 -0
  101. package/dist/controls/progress/progress.d.ts +28 -0
  102. package/dist/controls/progress/progress.js +80 -0
  103. package/dist/controls/radio/radio-group.css +84 -0
  104. package/dist/controls/radio/radio-group.d.ts +152 -0
  105. package/dist/controls/radio/radio-group.js +350 -0
  106. package/dist/controls/radio/radio.css +219 -0
  107. package/dist/controls/radio/radio.d.ts +19 -0
  108. package/dist/controls/radio/radio.js +75 -0
  109. package/dist/controls/ramp/ramp.css +75 -0
  110. package/dist/controls/ramp/ramp.d.ts +29 -0
  111. package/dist/controls/ramp/ramp.js +72 -0
  112. package/dist/controls/row/row.css +152 -0
  113. package/dist/controls/row/row.d.ts +95 -0
  114. package/dist/controls/row/row.js +45 -0
  115. package/dist/controls/segment/segment.css +66 -0
  116. package/dist/controls/segment/segment.d.ts +3 -0
  117. package/dist/controls/segment/segment.js +26 -0
  118. package/dist/controls/segmented-control/segmented-control.css +189 -0
  119. package/dist/controls/segmented-control/segmented-control.d.ts +20 -0
  120. package/dist/controls/segmented-control/segmented-control.js +64 -0
  121. package/dist/controls/select/select.css +467 -0
  122. package/dist/controls/select/select.d.ts +147 -0
  123. package/dist/controls/select/select.js +538 -0
  124. package/dist/controls/slider/slider.css +231 -0
  125. package/dist/controls/slider/slider.d.ts +111 -0
  126. package/dist/controls/slider/slider.js +60 -0
  127. package/dist/controls/slider-multi/slider-multi.css +197 -0
  128. package/dist/controls/slider-multi/slider-multi.d.ts +149 -0
  129. package/dist/controls/slider-multi/slider-multi.js +380 -0
  130. package/dist/controls/sparkline/sparkline-math.d.ts +23 -0
  131. package/dist/controls/sparkline/sparkline-math.js +79 -0
  132. package/dist/controls/sparkline/sparkline.css +61 -0
  133. package/dist/controls/sparkline/sparkline.d.ts +27 -0
  134. package/dist/controls/sparkline/sparkline.js +74 -0
  135. package/dist/controls/split/constrain.d.ts +44 -0
  136. package/dist/controls/split/constrain.js +126 -0
  137. package/dist/controls/split/split-pane.css +63 -0
  138. package/dist/controls/split/split-pane.d.ts +49 -0
  139. package/dist/controls/split/split-pane.js +42 -0
  140. package/dist/controls/split/split.css +127 -0
  141. package/dist/controls/split/split.d.ts +42 -0
  142. package/dist/controls/split/split.js +364 -0
  143. package/dist/controls/stat/stat-model.d.ts +22 -0
  144. package/dist/controls/stat/stat-model.js +69 -0
  145. package/dist/controls/stat/stat.css +101 -0
  146. package/dist/controls/stat/stat.d.ts +30 -0
  147. package/dist/controls/stat/stat.js +73 -0
  148. package/dist/controls/status-stream/status-stream.css +151 -0
  149. package/dist/controls/status-stream/status-stream.d.ts +108 -0
  150. package/dist/controls/status-stream/status-stream.js +417 -0
  151. package/dist/controls/swatch/swatch.css +66 -0
  152. package/dist/controls/swatch/swatch.d.ts +28 -0
  153. package/dist/controls/swatch/swatch.js +74 -0
  154. package/dist/controls/swiper/swiper-item.css +17 -0
  155. package/dist/controls/swiper/swiper-item.d.ts +31 -0
  156. package/dist/controls/swiper/swiper-item.js +47 -0
  157. package/dist/controls/swiper/swiper-label.css +10 -0
  158. package/dist/controls/swiper/swiper-label.d.ts +4 -0
  159. package/dist/controls/swiper/swiper-label.js +20 -0
  160. package/dist/controls/swiper/swiper-paddles.css +26 -0
  161. package/dist/controls/swiper/swiper-paddles.d.ts +10 -0
  162. package/dist/controls/swiper/swiper-paddles.js +62 -0
  163. package/dist/controls/swiper/swiper-pagination.css +60 -0
  164. package/dist/controls/swiper/swiper-pagination.d.ts +31 -0
  165. package/dist/controls/swiper/swiper-pagination.js +78 -0
  166. package/dist/controls/swiper/swiper.css +153 -0
  167. package/dist/controls/swiper/swiper.d.ts +158 -0
  168. package/dist/controls/swiper/swiper.js +659 -0
  169. package/dist/controls/switch/switch.css +194 -0
  170. package/dist/controls/switch/switch.d.ts +5 -0
  171. package/dist/controls/switch/switch.js +18 -0
  172. package/dist/controls/table/table-model.d.ts +43 -0
  173. package/dist/controls/table/table-model.js +124 -0
  174. package/dist/controls/table/table.css +90 -0
  175. package/dist/controls/table/table.d.ts +29 -0
  176. package/dist/controls/table/table.js +178 -0
  177. package/dist/controls/tabs/tab-panel.d.ts +10 -0
  178. package/dist/controls/tabs/tab-panel.js +89 -0
  179. package/dist/controls/tabs/tab.d.ts +26 -0
  180. package/dist/controls/tabs/tab.js +67 -0
  181. package/dist/controls/tabs/tabs.css +181 -0
  182. package/dist/controls/tabs/tabs.d.ts +61 -0
  183. package/dist/controls/tabs/tabs.js +176 -0
  184. package/dist/controls/text/href.d.ts +26 -0
  185. package/dist/controls/text/href.js +48 -0
  186. package/dist/controls/text/text.css +328 -0
  187. package/dist/controls/text/text.d.ts +85 -0
  188. package/dist/controls/text/text.js +273 -0
  189. package/dist/controls/text-field/text-field.css +498 -0
  190. package/dist/controls/text-field/text-field.d.ts +226 -0
  191. package/dist/controls/text-field/text-field.js +905 -0
  192. package/dist/controls/textarea/textarea.css +251 -0
  193. package/dist/controls/textarea/textarea.d.ts +136 -0
  194. package/dist/controls/textarea/textarea.js +312 -0
  195. package/dist/controls/theme-provider/theme-provider.css +48 -0
  196. package/dist/controls/theme-provider/theme-provider.d.ts +59 -0
  197. package/dist/controls/theme-provider/theme-provider.js +40 -0
  198. package/dist/controls/timeline/timeline.css +33 -0
  199. package/dist/controls/timeline/timeline.d.ts +39 -0
  200. package/dist/controls/timeline/timeline.js +52 -0
  201. package/dist/controls/timeline-item/timeline-item.css +495 -0
  202. package/dist/controls/timeline-item/timeline-item.d.ts +79 -0
  203. package/dist/controls/timeline-item/timeline-item.js +360 -0
  204. package/dist/controls/toast/toast-region.css +56 -0
  205. package/dist/controls/toast/toast-region.d.ts +20 -0
  206. package/dist/controls/toast/toast-region.js +113 -0
  207. package/dist/controls/toast/toast.css +96 -0
  208. package/dist/controls/toast/toast.d.ts +28 -0
  209. package/dist/controls/toast/toast.js +191 -0
  210. package/dist/controls/toolbar/toolbar.css +125 -0
  211. package/dist/controls/toolbar/toolbar.d.ts +108 -0
  212. package/dist/controls/toolbar/toolbar.js +68 -0
  213. package/dist/controls/tooltip/tooltip.css +80 -0
  214. package/dist/controls/tooltip/tooltip.d.ts +57 -0
  215. package/dist/controls/tooltip/tooltip.js +205 -0
  216. package/dist/descriptor/component-descriptor.d.ts +112 -0
  217. package/dist/descriptor/component-descriptor.js +492 -0
  218. package/dist/descriptor/index.d.ts +1 -0
  219. package/dist/descriptor/index.js +5 -0
  220. package/dist/dom/container.d.ts +45 -0
  221. package/dist/dom/container.js +60 -0
  222. package/dist/dom/element.d.ts +98 -0
  223. package/dist/dom/element.js +221 -0
  224. package/dist/dom/form.d.ts +240 -0
  225. package/dist/dom/form.js +399 -0
  226. package/dist/dom/index.d.ts +10 -0
  227. package/dist/dom/index.js +31 -0
  228. package/dist/dom/props.d.ts +56 -0
  229. package/dist/dom/props.js +231 -0
  230. package/dist/dom/repeat.d.ts +10 -0
  231. package/dist/dom/repeat.js +202 -0
  232. package/dist/dom/template.d.ts +161 -0
  233. package/dist/dom/template.js +634 -0
  234. package/dist/dom/watch.d.ts +11 -0
  235. package/dist/dom/watch.js +82 -0
  236. package/dist/foundation-styles.css +9 -0
  237. package/dist/index.d.ts +2 -0
  238. package/dist/index.js +3 -0
  239. package/dist/reactive/graph.d.ts +65 -0
  240. package/dist/reactive/graph.js +310 -0
  241. package/dist/reactive/index.d.ts +3 -0
  242. package/dist/reactive/index.js +4 -0
  243. package/dist/reactive/scheduler.d.ts +13 -0
  244. package/dist/reactive/scheduler.js +49 -0
  245. package/dist/traits/area-drag.d.ts +12 -0
  246. package/dist/traits/area-drag.js +70 -0
  247. package/dist/traits/form-registry.d.ts +36 -0
  248. package/dist/traits/form-registry.js +108 -0
  249. package/dist/traits/index.d.ts +24 -0
  250. package/dist/traits/index.js +19 -0
  251. package/dist/traits/overlay.d.ts +50 -0
  252. package/dist/traits/overlay.js +277 -0
  253. package/dist/traits/pane-resize.d.ts +26 -0
  254. package/dist/traits/pane-resize.js +103 -0
  255. package/dist/traits/press-activation.d.ts +11 -0
  256. package/dist/traits/press-activation.js +55 -0
  257. package/dist/traits/roving-focus.d.ts +52 -0
  258. package/dist/traits/roving-focus.js +214 -0
  259. package/dist/traits/scroll-fade.d.ts +28 -0
  260. package/dist/traits/scroll-fade.js +120 -0
  261. package/dist/traits/selection-commit.d.ts +30 -0
  262. package/dist/traits/selection-commit.js +146 -0
  263. package/dist/traits/tabbable.d.ts +12 -0
  264. package/dist/traits/tabbable.js +59 -0
  265. package/dist/traits/track-user-invalid.d.ts +36 -0
  266. package/dist/traits/track-user-invalid.js +52 -0
  267. package/dist/traits/value-codec.d.ts +120 -0
  268. package/dist/traits/value-codec.js +497 -0
  269. package/dist/traits/value-drag.d.ts +18 -0
  270. package/dist/traits/value-drag.js +84 -0
  271. package/package.json +263 -0
@@ -0,0 +1,467 @@
1
+ /* select.css — ui-select, the single-file component stylesheet (ADR-0003).
2
+ * Two clearly-SECTIONED blocks, mirroring button.css / text-field.css / popover.css:
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-select)` (specificity 0,0,0) DECLARES the `--ui-select-*`
5
+ * chain from the colour roles (`--md-sys-color-{family}-{role}`; family = neutral for the trigger frame)
6
+ * and the dimensional ramp (`--md-sys-height-{size}` / `--md-sys-font-{size}` / `--md-sys-icon-{size}`).
7
+ * The trigger is a Control-class surface carrying Kim's filled/container state law (TKT-0062,
8
+ * superseding the ADR-0014 cl.2c border-only channel) — background/border/text-ink ALL repoint
9
+ * per state (default/filled/hover/focus/disabled; "filled" = a real selection, via a NEW
10
+ * `[data-empty]` toggle select.ts's label effect now maintains); a caret = font inline
11
+ * affordance, §4.1. The listbox
12
+ * panel is a Container/surface (bg + outline + radius + padding — NOT a control height). The
13
+ * option rows use the legacy item-pad model (§5.1 / ROV-C5: padding-based row height, NOT
14
+ * the --md-sys-compact widget box — option rows are text items, not stand-alone widgets) — but
15
+ * (2026-07-06) the item-pad's own NUMBERS are now DERIVED off the trigger ramp
16
+ * (`--ui-select-height`/`--ui-select-font`), superseding the earlier fixed-px item-pad
17
+ * (6px/12px): panel inset + option padding now scale with `[size]` the same as the trigger,
18
+ * and option text aligns under the trigger label (listbox-padding + option-inline == h/2).
19
+ * `[size]` repoints the geometry (trigger AND, transitively via the shared height/font
20
+ * tokens, the panel/options — no separate [size] block needed for the panel).
21
+ * [2] STYLES BLOCK — `@scope (ui-select)` CONSUMES only `--ui-select-*` for its OWN tokens.
22
+ * The host is `display: inline-block` with a min-inline-size floor (the ADR-0021 lesson:
23
+ * a control that can be empty needs a floor; the trigger inherits full width from the host).
24
+ * The trigger is `display: inline-grid; grid-template-columns: 1fr auto` — always a
25
+ * [label | caret] anatomy (the trailing caret slot is always present, so the grid is fixed
26
+ * at two columns). The caret span is an icon-sized cell (the CELL stays icon-sized for both
27
+ * icon and caret roles; only the GLYPH inside differs — padding: (icon−glyph)/2 centers the
28
+ * font-sized glyph in the icon cell, the §4.1 caret centering law). The listbox panel uses
29
+ * the overlay controller's position:fixed + inset (we do NOT fix position here). Option rows
30
+ * get hover/selected/focus states; focus ring on :focus-visible (real DOM focus via rovingFocus).
31
+ * Forced-colors keeps the trigger frame, label, and panel surface visible as system colours.
32
+ *
33
+ * Standalone stylesheet — NOT injected from select.ts (behaviour-only `.ts`, no runtime injection
34
+ * per plan §2). Linked via the component-styles barrel (s12); the barrel @imports foundation roles
35
+ * FIRST. The listbox's `popover` attribute (set by the overlay controller) gives it `display: none`
36
+ * in the UA stylesheet until `showPopover()` places it in the top layer.
37
+ */
38
+
39
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-select) declares --ui-select-* ════════════════ */
40
+
41
+ :where(ui-select) {
42
+ /* ── Trigger frame — Kim's filled/container state law (TKT-0062), SUPERSEDING the ADR-0014
43
+ cl.2c border-only channel: bg/border/ink ALL repoint per state now (default/filled/hover/
44
+ focus/disabled — same table as text-field/textarea/combo-box). "filled" = the trigger shows a
45
+ real selection, not the placeholder (`[data-empty]`, a NEW toggle this ticket added to
46
+ select.ts's own label effect — select previously had no emptiness hook at all, unlike its
47
+ siblings). Focus = transparent: the shared outline ring is the SOLE focus indicator (ADR-0014
48
+ dev#1; a border-focus step doubles with the ring). */
49
+ --ui-select-bg: var(--md-sys-color-neutral-container-low); /* default */
50
+ --ui-select-bg-filled: var(--md-sys-color-neutral-container);
51
+ --ui-select-bg-hover: var(--md-sys-color-neutral-container);
52
+ --ui-select-bg-focus: var(--md-sys-color-neutral-container-low);
53
+ --ui-select-border: transparent; /* default */
54
+ --ui-select-border-hover: var(--md-sys-color-neutral-outline-variant); /* the ONE visible-border state */
55
+ --ui-select-border-focus: transparent; /* transparent holds the geometry — no layout shift */
56
+ /* user-invalid (ADR-0051) — the danger border, appearing only AFTER the first interaction (the
57
+ trackUserInvalid controller owns that timing; select.ts toggles :state(user-invalid)). The
58
+ text-field precedent, on the trigger's border channel — UNCHANGED, orthogonal to the table above. */
59
+ --ui-select-border-invalid: var(--md-sys-color-danger);
60
+ --ui-select-border-invalid-hover: var(--md-sys-color-danger-high);
61
+ --ui-select-ink: var(--md-sys-color-neutral); /* default — showing the placeholder */
62
+ --ui-select-ink-filled: var(--md-sys-color-neutral-on-surface-variant);
63
+ --ui-select-ink-hover: var(--md-sys-color-neutral-on-surface-variant);
64
+ --ui-select-ink-focus: var(--md-sys-color-neutral-on-surface);
65
+ --ui-select-radius: var(--md-sys-shape-corner-base); /* fixed rounded-rect radius, like text-field */
66
+
67
+ /* ── Trigger geometry (Control class, default size = md) — off the §1-row ramp (ADR-0038).
68
+ height/font/icon from the explicit per-[scale] table; gap = font/2 × density (on `*`).
69
+ The caret (inline affordance) follows the §4.1 law: glyph size = font (RHYTHM family,
70
+ not frame) → glyph is font-sized, centred in the icon cell by padding=(icon−glyph)/2. */
71
+ --ui-select-height: var(--md-sys-height-md);
72
+ --ui-select-font: var(--md-sys-font-md);
73
+ --ui-select-gap: var(--md-sys-gap-md); /* the density-bearing column-gap */
74
+ --ui-select-icon: var(--md-sys-icon-md); /* the icon-cell size (caret CELL is icon-wide) */
75
+ --ui-select-glyph: var(--ui-select-font); /* caret GLYPH = font (§4.1 caret law) */
76
+
77
+ /* ── Sizing floor (ADR-0021 lesson) — a select with no selection or an empty placeholder
78
+ needs a floor so it is hittable and the overlay positions against a real anchor rect. */
79
+ --ui-select-min-inline-size: 10ch;
80
+
81
+ /* ── Disabled state — TKT-0062's disabled row (bg=container-low, border=transparent, ink=neutral-low) */
82
+ --ui-select-border-disabled: transparent;
83
+ --ui-select-bg-disabled: var(--md-sys-color-neutral-container-low);
84
+ --ui-select-ink-disabled: var(--md-sys-color-neutral-low);
85
+
86
+ /* ── Listbox panel — Container/surface geometry (NOT a control height; like modal/card/popover) */
87
+ --ui-select-listbox-bg: var(--md-sys-color-neutral-surface);
88
+ --ui-select-listbox-ink: var(--md-sys-color-neutral-on-surface);
89
+ --ui-select-listbox-outline: var(--md-sys-color-neutral-outline-variant);
90
+ --ui-select-listbox-radius: var(--md-sys-shape-corner-base);
91
+ /* ── The size-carrying derivation (supersedes the fixed "legacy item-pad" px, 2026-07-06) ──────
92
+ ROV-C5/ADR-0043 cl.5 keep options OUT of the h/2 "comfortable control" model and the
93
+ `--md-sys-compact` widget box — that boundary is unchanged. What changes: the item-pad's OWN
94
+ numbers are now DERIVED off the trigger's `--ui-select-height`/`--ui-select-font` (the same
95
+ ramp the trigger already repoints at `[size=sm|lg]`) instead of hand-picked fixed px, so the
96
+ panel inset + option row scale WITH the trigger — no new sm/lg blocks needed; the calc()s
97
+ below repoint for free.
98
+ • Alignment law: listbox-padding + option-inline == h/2, so option text starts at the same
99
+ inline offset from the panel's outer edge as the trigger label does from the trigger's
100
+ edge (h/2) — text lines up under text. Split evenly (h/4 + h/4).
101
+ • Row-height law: option-block == (h − font)/2 — the SAME (height−glyph)/2 centering law
102
+ (geometry.md), applied on the block axis to the option's own text (glyph = font here) —
103
+ so the option row's rendered content-box height == the trigger height exactly, and both
104
+ grow together under [size]. */
105
+ --ui-select-listbox-padding: calc(var(--ui-select-height) / 4); /* panel-edge inset + inter-row gap (feeds --ui-box-inset below) */
106
+ --ui-select-listbox-min-inline-size: 12ch; /* floor: at least as wide as a short option */
107
+
108
+ /* Panel max-block-size (TKT-0027) — public dial, default min(50vh, 12 real option rows): the
109
+ smaller of half the viewport or a scannable dozen rows, replacing the old flat 40vh magic
110
+ number. An option row's rendered height IS --ui-select-height exactly (the row-height law
111
+ above: (h−font)/2 padding × 2 + font == h, line-height:1 makes it exact) — no separate item-
112
+ block token needed, unlike ui-menu (which has no analogous control height to reuse). 12 rows
113
+ = 12 option boxes + 13 inset margins (leading + 11 collapsed inter-row + trailing — the
114
+ listbox is `[data-box]`, a BFC that collapses adjacent siblings' margins to ONE inset, never
115
+ doubled); --ui-select-listbox-padding IS the value fed to --ui-box-inset below, so this reads
116
+ the same source, not the DOM-positional --ui-box-inset itself. Rides [size]/[scale] naturally
117
+ (both terms derive from --ui-select-height); density-invariant, like the trigger height itself
118
+ (geometry.md: height is a frame quantity, not rhythm). + 2px — the panel's border-box:
119
+ max-block-size caps the BORDER box (box-sizing: border-box below), but the row math above
120
+ computed the CONTENT box (12 rows + 13 insets, no border term) — without this the 12th row
121
+ would overflow its own cap by exactly the 1px+1px border width (FIXED 2026-07-12, TKT-0027
122
+ browser-leg finding, same shape as ui-menu). */
123
+ --ui-select-listbox-max-block-size: min(50vh, calc(12 * var(--ui-select-height) + 13 * var(--ui-select-listbox-padding) + 2px));
124
+
125
+ /* ── Option rows — legacy item-pad (§5.1 / ROV-C5): padding-based row height, NOT a fixed
126
+ --md-sys-compact widget box. Options are text list items, not stand-alone widgets. The row
127
+ radius is inset from the listbox shell radius (nested-radius: shell − padding, ADR-0018). */
128
+ --ui-select-option-block: calc((var(--ui-select-height) - var(--ui-select-font)) / 2); /* (h−font)/2 — row height == trigger height */
129
+ --ui-select-option-inline: calc(var(--ui-select-height) / 4); /* h/4 — pairs with listbox-padding to total h/2 */
130
+ --ui-select-option-font: var(--ui-select-font); /* options were size-blind on FONT too (bug fix) — now the ramp */
131
+ --ui-select-option-radius: calc(var(--ui-select-listbox-radius) - var(--ui-select-listbox-padding)); /* nested-radius off the REAL inset (was a hardcoded 4px, ADR-0018) */
132
+ --ui-select-option-bg-hover: var(--md-sys-color-neutral-surface-high);
133
+ --ui-select-option-bg-selected: var(--md-sys-color-primary-container-low);
134
+ --ui-select-option-ink-selected: var(--md-sys-color-primary-high);
135
+ --ui-select-option-bg-focus: var(--md-sys-color-primary-container-low);
136
+ --ui-select-option-ink-focus: var(--md-sys-color-primary-high);
137
+
138
+ /* ── Group header (optgroup parity) — a non-interactive label above a [role=group]'s options.
139
+ Muted, small, slightly emphasised; the option rows below it keep the standard item-pad. */
140
+ --ui-select-group-label-ink: var(--md-sys-color-neutral-on-surface-variant);
141
+ }
142
+
143
+ /* ── size → dimensional ramp repoint ────────────────────────────────────────────────────────── */
144
+
145
+ :where(ui-select[size='sm']) {
146
+ --ui-select-height: var(--md-sys-height-sm);
147
+ --ui-select-font: var(--md-sys-font-sm);
148
+ --ui-select-gap: var(--md-sys-gap-sm);
149
+ --ui-select-icon: var(--md-sys-icon-sm);
150
+ }
151
+
152
+ :where(ui-select[size='lg']) {
153
+ --ui-select-height: var(--md-sys-height-lg);
154
+ --ui-select-font: var(--md-sys-font-lg);
155
+ --ui-select-gap: var(--md-sys-gap-lg);
156
+ --ui-select-icon: var(--md-sys-icon-lg);
157
+ }
158
+
159
+ /* ── disabled → muted neutral roles ────────────────────────────────────────────────────────── */
160
+ /* TKT-0063: widened to `:is([disabled], :state(disabled))` — the own `[disabled]` attribute ALONE
161
+ missed the effective-disabled case (own || ancestor <fieldset disabled>/form-provider); select.ts's
162
+ effectiveDisabled() effect now also sets :state(disabled), mirroring text-field.ts's own precedent. */
163
+ :where(ui-select:is([disabled], :state(disabled))) {
164
+ --ui-select-border: var(--ui-select-border-disabled);
165
+ --ui-select-bg: var(--ui-select-bg-disabled);
166
+ --ui-select-ink: var(--ui-select-ink-disabled);
167
+ }
168
+
169
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-select) consumes ONLY --ui-select-* ════════════════ */
170
+
171
+ @scope (ui-select) {
172
+ /* The host: an inline-block with a min-inline-size floor. The trigger child fills the host
173
+ width (width: 100%) so the select is as wide as its container or at least 10ch. */
174
+ :scope {
175
+ display: inline-block;
176
+ min-inline-size: var(--ui-select-min-inline-size);
177
+ vertical-align: middle; /* align with adjacent form controls (like a native <select>) */
178
+ }
179
+
180
+ /* The trigger button (Control class): a [label | caret] inline-grid. The trigger ALWAYS
181
+ has the caret trailing — unlike button.css which is presence-driven. So the grid is fixed
182
+ at two columns: 1fr (the label grows to fill) + auto (the caret icon cell). The label side
183
+ gets the h/2 start pad; the caret side gets the ½(h−icon) end pad (same as a trailing slot
184
+ in button.css — the icon-cell-edge law). block-size is the Control-class ramp height.
185
+ line-height: 1 (ADR-0036 single-line control standard — the glyph is centred by the frame). */
186
+ :scope > [data-part='trigger'] {
187
+ display: inline-grid;
188
+ grid-template-columns: 1fr auto;
189
+ column-gap: var(--ui-select-gap);
190
+ align-items: center;
191
+ box-sizing: border-box;
192
+ width: 100%; /* fill the host's inline-size */
193
+
194
+ block-size: var(--ui-select-height);
195
+ padding-block: 0; /* NEVER block padding — geometry law */
196
+ padding-inline-start: calc(var(--ui-select-height) / 2); /* h/2 label edge */
197
+ padding-inline-end: calc((var(--ui-select-height) - var(--ui-select-icon)) / 2); /* ½(h−icon) caret edge */
198
+ min-inline-size: 0; /* allow the trigger to shrink within a narrow host (the host sets the floor) */
199
+
200
+ font-size: var(--ui-select-font);
201
+ line-height: var(--md-sys-control-line-height); /* single-line control standard (ADR-0036) */
202
+ background: var(--ui-select-bg);
203
+ color: var(--ui-select-ink);
204
+ border: 1px solid var(--ui-select-border);
205
+ border-radius: var(--ui-select-radius);
206
+ cursor: pointer;
207
+ text-align: start;
208
+ -webkit-user-select: none;
209
+ user-select: none;
210
+ }
211
+
212
+ /* The label span: fills the 1fr column; clipped for overflow. */
213
+ :scope > [data-part='trigger'] > [data-part='label'] {
214
+ overflow: hidden;
215
+ text-overflow: ellipsis;
216
+ white-space: nowrap;
217
+ }
218
+
219
+ /* The aria-label span (ADR-0085): visually-hidden, always present (idempotent-parts precedent) —
220
+ referenced by the trigger's aria-labelledby only when `label` is set and the control is unfielded.
221
+ The standard CLIP technique, not `hidden`/`display:none` — some AT/engine combinations do not
222
+ reliably fold a display:none-referenced node's text into the accessible-name computation for an
223
+ aria-labelledby reference, while the clip approach is the robust cross-engine convention. */
224
+ :scope > [data-part='aria-label'] {
225
+ position: absolute;
226
+ inline-size: 1px;
227
+ block-size: 1px;
228
+ padding: 0;
229
+ margin: -1px;
230
+ overflow: hidden;
231
+ clip: rect(0, 0, 0, 0);
232
+ white-space: nowrap;
233
+ border: 0;
234
+ }
235
+
236
+ /* The caret span: an icon-sized CELL (box-sizing border-box). The glyph inside is font-sized
237
+ and centred by padding = ½(icon − glyph), the §4.1 caret law (same as button.css [data-role='caret']).
238
+ aria-hidden is set in select.ts; the glyph is decorative. */
239
+ :scope > [data-part='trigger'] > [data-part='caret'] {
240
+ inline-size: var(--ui-select-icon);
241
+ block-size: var(--ui-select-icon);
242
+ box-sizing: border-box;
243
+ padding: calc((var(--ui-select-icon) - var(--ui-select-glyph)) / 2);
244
+ display: flex;
245
+ align-items: center;
246
+ justify-content: center;
247
+ font-size: var(--ui-select-glyph); /* the caret glyph = font (§4.1) */
248
+ line-height: 1;
249
+ }
250
+
251
+ /* The injected Phosphor caret-down (setIcon, @agent-ui-kit/icons): sized explicitly to the glyph
252
+ rhythm — not the icon cell — so the §4.1 law holds independent of the padding math above
253
+ (the padding already insets to the same value; this is belt-and-suspenders, not reliance on
254
+ it). Overrides the svg's own width/height="100%" presentational attributes (resolve.ts). */
255
+ :scope > [data-part='trigger'] > [data-part='caret'] svg {
256
+ display: block;
257
+ inline-size: var(--ui-select-glyph);
258
+ block-size: var(--ui-select-glyph);
259
+ }
260
+
261
+ /* Trigger filled/container state law (TKT-0062, Kim's ruling — supersedes the ADR-0014 cl.2c
262
+ border-only channel) — repoints background/border-color/color together. "filled" = the trigger
263
+ shows a real selection (`[data-part='trigger']:not([data-empty])` — the NEW toggle select.ts's
264
+ label effect now maintains). Precedence is enforced by MUTUAL EXCLUSION (`:not()`), never
265
+ source-order/specificity (the lesson from text-field.css's own build: a disabled-guarded `:hover`
266
+ measured HIGHER specificity than an unguarded focus selector, so a mouse-click focus — which also
267
+ leaves the pointer hovering — kept the wrong state painted). `[disabled]` is checked directly on
268
+ the TRIGGER (select.ts already collapses own-disabled OR form-disabled onto the trigger's own
269
+ attribute — `trigger.setAttribute('disabled', ...)` in the effectiveDisabled() effect — so no
270
+ separate `:state(disabled)` check is needed here, unlike text-field/textarea/combo-box). */
271
+ :scope > [data-part='trigger']:not(:hover):not(:focus-visible):not([disabled]):not([data-empty]) {
272
+ background: var(--ui-select-bg-filled);
273
+ color: var(--ui-select-ink-filled);
274
+ }
275
+ :scope > [data-part='trigger']:not(:focus-visible):not([disabled]):hover {
276
+ background: var(--ui-select-bg-hover);
277
+ border-color: var(--ui-select-border-hover);
278
+ color: var(--ui-select-ink-hover);
279
+ }
280
+
281
+ :scope > [data-part='trigger']:focus-within,
282
+ :scope > [data-part='trigger']:focus-visible {
283
+ outline: none; /* the outline ring below replaces the UA default */
284
+ }
285
+
286
+ /* Focus (keyboard-only :focus-visible, the button precedent — a trigger is an action control, not a
287
+ text-entry surface) wins over filled/hover unconditionally; a disabled trigger cannot be focused,
288
+ so no guard is needed here (matching the old law). */
289
+ :scope > [data-part='trigger']:focus-visible {
290
+ background: var(--ui-select-bg-focus);
291
+ border-color: var(--ui-select-border-focus);
292
+ color: var(--ui-select-ink-focus);
293
+ outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
294
+ outline-offset: var(--md-sys-state-focus-ring-offset);
295
+ }
296
+
297
+ /* user-invalid (ADR-0051) — the danger border, appearing only AFTER the first interaction. */
298
+ :scope:state(user-invalid) > [data-part='trigger'] {
299
+ border-color: var(--ui-select-border-invalid);
300
+ }
301
+ :scope:state(user-invalid) > [data-part='trigger']:hover {
302
+ border-color: var(--ui-select-border-invalid-hover);
303
+ }
304
+
305
+ /* Disabled trigger: pointer-inert; :hover/:active become inert. The trigger is a REAL native
306
+ <button> whose own `disabled` attribute select.ts already sets from effectiveDisabled() (own ||
307
+ form-disabled) — a native disabled button is platform-inert regardless of this rule, so
308
+ `pointer-events: none` here is defensive-only; `:state(disabled)` (TKT-0063) widens the match so
309
+ `cursor: default` also applies under the form-disabled-only case, not just the host's own
310
+ `[disabled]` attribute. */
311
+ :scope:is([disabled], :state(disabled)) > [data-part='trigger'] {
312
+ pointer-events: none;
313
+ cursor: default;
314
+ }
315
+
316
+ /* ── The listbox panel (Container/surface in the Popover API top layer) ───────────────────────
317
+ The UA `[popover]` rule hides this with display:none until showPopover(). The overlay
318
+ controller sets position:fixed + inset on open — we do NOT set position here. `margin: 0`
319
+ prevents UA-default centering from conflicting with the controller's explicit placement. */
320
+ :scope > [data-part='listbox'] {
321
+ box-sizing: border-box;
322
+ margin: 0;
323
+ min-inline-size: var(--ui-select-listbox-min-inline-size);
324
+ /* Matching the trigger width is done in JS (see select.ts model→overlay effect): in the
325
+ Popover API top layer `100%` resolves to 100vw (the viewport), NOT the trigger width.
326
+ select.ts stamps `listbox.style.minInlineSize = triggerWidth + 'px'` before showPopover(). */
327
+ max-block-size: var(--ui-select-listbox-max-block-size); /* TKT-0027 — public dial, default min(50vh, 12 option rows) */
328
+ overflow-y: auto;
329
+
330
+ background-color: var(--ui-select-listbox-bg);
331
+ color: var(--ui-select-listbox-ink);
332
+ border: 1px solid var(--ui-select-listbox-outline);
333
+ border-radius: var(--ui-select-listbox-radius);
334
+ padding: 0; /* the box-model child margins provide the inset (container-box.css); no shell padding */
335
+ /* Repoint the shared container-box.css inset LOCALLY to the size-derived value (the panel
336
+ carries [data-box], see select.ts — that rule declares --ui-box-inset: 0.375rem at
337
+ specificity 0; this higher-specificity declaration on the same element overrides it so
338
+ the size-carrying model owns the value fed to the rows' margin + the nested-radius calc
339
+ below, instead of the shared fleet-wide 6px fallback every [data-box] otherwise gets). */
340
+ --ui-box-inset: var(--ui-select-listbox-padding);
341
+ font-size: var(--ui-select-option-font); /* was unset (inherited ambient) — options + the group label now read the ramp */
342
+ }
343
+
344
+ /* ── Option rows (legacy item-pad — §5.1 / ROV-C5) ──────────────────────────────────────────
345
+ Options are text list items with padding-based row height (not a fixed widget-box height).
346
+ rovingFocus manages tabindex (0 = roving current, -1 = others). The focus ring on :focus-visible
347
+ is the fleet ring (the direct --md-sys-color-focus-ring read, the one permitted exception — same as button). */
348
+ :scope > [data-part='listbox'] [role='option'] {
349
+ box-sizing: border-box;
350
+ display: block;
351
+ margin: var(--ui-box-inset, 0.375rem); /* the box-model inset — repointed above to the size-derived value */
352
+ padding-block: var(--ui-select-option-block);
353
+ padding-inline: var(--ui-select-option-inline);
354
+ /* line-height: 1 makes the (h−font)/2 block-pad math exact — font + 2×pad == the trigger
355
+ height, so the row's rendered content-box is literally the SAME height as the control that
356
+ opened it (the row-height law above), not the DoD's Control-class line-height rule (that
357
+ one is scoped to Control-class elements; this is a local derivation choice for item rows). */
358
+ line-height: 1;
359
+ border-radius: var(--ui-select-option-radius);
360
+ cursor: pointer;
361
+ -webkit-user-select: none;
362
+ user-select: none;
363
+ outline: none; /* suppress the default UA outline — :focus-visible provides the ring below */
364
+ }
365
+
366
+ /* Groups are STRUCTURAL, not visible boxes — full-bleed (no inset margin); their options + sticky header
367
+ carry the box rhythm. (Overrides the shared `[data-box] > *` margin for the group wrapper.) */
368
+ :scope > [data-part='listbox'] > [role='group'] {
369
+ margin: 0;
370
+ display: block;
371
+ }
372
+
373
+ /* Group header (optgroup parity) — a non-interactive label; NOT a [role=option], so rovingFocus +
374
+ selectionCommit skip it. STICKY: pins to the panel's scroll top and takes the panel surface so scrolled
375
+ options never show through (the container box-model region pattern). Full-bleed edge-to-edge. */
376
+ :scope > [data-part='listbox'] [data-part='group-label'] {
377
+ box-sizing: border-box;
378
+ display: block;
379
+ position: sticky;
380
+ inset-block-start: 0;
381
+ z-index: 1;
382
+ margin: 0; /* full-bleed — spans the panel width so the sticky surface fully occludes scrolled rows */
383
+ padding-block: var(--ui-select-option-block);
384
+ padding-inline: var(--ui-select-option-inline);
385
+ background-color: var(--ui-select-listbox-bg); /* the panel surface — occludes scrolled content */
386
+ color: var(--ui-select-group-label-ink);
387
+ font-size: 0.85em;
388
+ font-weight: 600;
389
+ letter-spacing: 0.02em;
390
+ cursor: default;
391
+ -webkit-user-select: none;
392
+ user-select: none;
393
+ }
394
+
395
+ /* A group after the first gets a hairline divider above it (visual grouping between sticky sections). */
396
+ :scope > [data-part='listbox'] > [role='group'] + [role='group'] {
397
+ border-block-start: 1px solid var(--ui-select-listbox-outline);
398
+ }
399
+
400
+ :scope > [data-part='listbox'] [role='option']:hover {
401
+ background-color: var(--ui-select-option-bg-hover);
402
+ }
403
+
404
+ :scope > [data-part='listbox'] [role='option'][aria-selected='true'] {
405
+ background-color: var(--ui-select-option-bg-selected);
406
+ color: var(--ui-select-option-ink-selected);
407
+ }
408
+
409
+ :scope > [data-part='listbox'] [role='option']:focus-visible {
410
+ background-color: var(--ui-select-option-bg-focus);
411
+ color: var(--ui-select-option-ink-focus);
412
+ outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
413
+ outline-offset: var(--md-sys-state-focus-ring-offset);
414
+ }
415
+
416
+ /* Disabled option — not interactive; rovingFocus skips disabled items. */
417
+ :scope > [data-part='listbox'] [role='option'][aria-disabled='true'],
418
+ :scope > [data-part='listbox'] [role='option'][disabled] {
419
+ color: var(--ui-select-ink-disabled);
420
+ cursor: default;
421
+ pointer-events: none;
422
+ }
423
+
424
+ /* ── Motion (state-PAINT only; geometry snaps — ADR-0008) ─────────────────────────────────── */
425
+
426
+ :scope > [data-part='trigger']:state(ready) {
427
+ transition:
428
+ background-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
429
+ border-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
430
+ color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
431
+ }
432
+ @media (prefers-reduced-motion: reduce) {
433
+ :scope > [data-part='trigger']:state(ready) {
434
+ transition: none;
435
+ }
436
+ }
437
+
438
+ /* ── forced-colors (WHCM) ─────────────────────────────────────────────────────────────────── */
439
+
440
+ @media (forced-colors: active) {
441
+ /* Trigger: keep ink and border as system ButtonText/ButtonBorder; background stays as
442
+ ButtonFace. The focus ring survives via --md-sys-color-focus-ring → Highlight. */
443
+ :scope > [data-part='trigger'] {
444
+ color: ButtonText;
445
+ border-color: ButtonBorder;
446
+ background: ButtonFace;
447
+ forced-color-adjust: none; /* preserve our explicit border/bg/color from the OS wash */
448
+ }
449
+
450
+ /* Panel: Canvas/CanvasText for the surface; CanvasText for the border. */
451
+ :scope > [data-part='listbox'] {
452
+ background-color: Canvas;
453
+ color: CanvasText;
454
+ border-color: CanvasText;
455
+ forced-color-adjust: none;
456
+ }
457
+
458
+ /* Option selected/hover: Highlight/HighlightText (system selection colours). */
459
+ :scope > [data-part='listbox'] [role='option']:hover,
460
+ :scope > [data-part='listbox'] [role='option'][aria-selected='true'],
461
+ :scope > [data-part='listbox'] [role='option']:focus-visible {
462
+ background-color: Highlight;
463
+ color: HighlightText;
464
+ forced-color-adjust: none;
465
+ }
466
+ }
467
+ }
@@ -0,0 +1,147 @@
1
+ import { type ReactiveProps } from '../../dom/index.js';
2
+ import { UIFormElement } from '../../dom/index.js';
3
+ import type { FormValue, ValidityResult, FieldLabelling } from '../../dom/index.js';
4
+ import { type OverlayHandle } from '../../traits/overlay.js';
5
+ declare const props: {
6
+ value: {
7
+ reflect: true;
8
+ type: import("../../index.js").PropType<string>;
9
+ default: string;
10
+ attribute?: string | false;
11
+ };
12
+ label: {
13
+ reflect: true;
14
+ type: import("../../index.js").PropType<string>;
15
+ default: string;
16
+ attribute?: string | false;
17
+ };
18
+ open: {
19
+ reflect: true;
20
+ type: import("../../index.js").PropType<boolean>;
21
+ default: boolean;
22
+ attribute?: string | false;
23
+ };
24
+ placeholder: {
25
+ type: import("../../index.js").PropType<string>;
26
+ default: string;
27
+ attribute?: string | false;
28
+ reflect?: boolean;
29
+ };
30
+ size: {
31
+ reflect: true;
32
+ type: import("../../index.js").PropType<"sm" | "md" | "lg">;
33
+ default: "sm" | "md" | "lg";
34
+ attribute?: string | false;
35
+ };
36
+ name: {
37
+ reflect: true;
38
+ type: import("../../index.js").PropType<string>;
39
+ default: string;
40
+ attribute?: string | false;
41
+ };
42
+ disabled: {
43
+ reflect: true;
44
+ type: import("../../index.js").PropType<boolean>;
45
+ default: boolean;
46
+ attribute?: string | false;
47
+ };
48
+ required: {
49
+ reflect: true;
50
+ type: import("../../index.js").PropType<boolean>;
51
+ default: boolean;
52
+ attribute?: string | false;
53
+ };
54
+ };
55
+ export interface UISelectElement extends ReactiveProps<typeof props> {
56
+ }
57
+ export declare class UISelectElement extends UIFormElement {
58
+ #private;
59
+ static props: {
60
+ value: {
61
+ reflect: true;
62
+ type: import("../../index.js").PropType<string>;
63
+ default: string;
64
+ attribute?: string | false;
65
+ };
66
+ label: {
67
+ reflect: true;
68
+ type: import("../../index.js").PropType<string>;
69
+ default: string;
70
+ attribute?: string | false;
71
+ };
72
+ open: {
73
+ reflect: true;
74
+ type: import("../../index.js").PropType<boolean>;
75
+ default: boolean;
76
+ attribute?: string | false;
77
+ };
78
+ placeholder: {
79
+ type: import("../../index.js").PropType<string>;
80
+ default: string;
81
+ attribute?: string | false;
82
+ reflect?: boolean;
83
+ };
84
+ size: {
85
+ reflect: true;
86
+ type: import("../../index.js").PropType<"sm" | "md" | "lg">;
87
+ default: "sm" | "md" | "lg";
88
+ attribute?: string | false;
89
+ };
90
+ name: {
91
+ reflect: true;
92
+ type: import("../../index.js").PropType<string>;
93
+ default: string;
94
+ attribute?: string | false;
95
+ };
96
+ disabled: {
97
+ reflect: true;
98
+ type: import("../../index.js").PropType<boolean>;
99
+ default: boolean;
100
+ attribute?: string | false;
101
+ };
102
+ required: {
103
+ reflect: true;
104
+ type: import("../../index.js").PropType<boolean>;
105
+ default: boolean;
106
+ attribute?: string | false;
107
+ };
108
+ };
109
+ /**
110
+ * Protected overlay handle — accessible to test probes (C10 idempotent-cleanup DoD).
111
+ * Replaced on each reconnect (connected() re-runs); the old handle's cleanup fires via the
112
+ * scope effect disposer at disconnect before this is re-assigned.
113
+ */
114
+ protected _overlayHandle: OverlayHandle | null;
115
+ protected formValue(): FormValue;
116
+ protected formValidity(): ValidityResult;
117
+ protected formReset(): void;
118
+ protected disconnected(): void;
119
+ /** Feeds `FormConnectDetail.userInvalid` (ADR-0050) — the `trackUserInvalid` tracker IS the one
120
+ * timing source; this override just exposes its gate. */
121
+ protected formUserInvalid(): boolean;
122
+ /**
123
+ * The part-role override (ADR-0085 cl.4) — the trigger carries no `internals.role` (a logical select
124
+ * wrapper), so the base's guarded internals-reflection default (dom/form.ts) never fires; wire the
125
+ * trigger's `aria-labelledby`/`aria-describedby` content attributes directly instead (the text-field
126
+ * `applyFieldLabelling` precedent, ADR-0051 cl.2/LLD-C2).
127
+ *
128
+ * `aria-labelledby` is the BARE-mode effect's (connected(), below) exclusive concern whenever
129
+ * `fieldLabelling === null` — this override never touches it on dissociation (`refs === null`), so the
130
+ * two effects can never race to write the SAME attribute in the same flush wave (the F3 dual-writer
131
+ * discipline text-field's own override documents). While fielded (`refs !== null`), THIS method is the
132
+ * exclusive writer: MERGE, never clobber — `<refs.label.id> <value-span-id>` (field label + value),
133
+ * the same two-id shape as the bare path, only the name source swaps.
134
+ *
135
+ * `aria-describedby` has NO other owner for this control (the trigger carries no internal
136
+ * validity-message node, unlike text-field's editor) — so unlike text-field's override, THIS one is the
137
+ * describedby's exclusive owner in BOTH directions: written from `[refs.description, refs.error]` when
138
+ * fielded, cleared on dissociation.
139
+ *
140
+ * Guards a not-yet-created trigger (the LLD-C2 override contract) — cannot happen in practice
141
+ * (`#ensureParts()` runs synchronously at the top of `connected()`, before the base's forwarding effect
142
+ * installs), but the guard costs nothing and documents the contract.
143
+ */
144
+ protected applyFieldLabelling(refs: FieldLabelling | null): void;
145
+ protected connected(): void;
146
+ }
147
+ export {};