@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,162 @@
1
+ /* disclosure.css — ui-disclosure, the single-file component stylesheet (ADR-0003; content-family.lld.md
2
+ * LLD-C9 · SPEC-R18). Two clearly-SECTIONED blocks, mirroring button.css / select.css / modal.css:
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-disclosure)` (specificity 0,0,0) DECLARES the `--ui-disclosure-*` chain
5
+ * from the colour roles (`--md-sys-color-neutral-*` — a disclosure carries no accent/family axis)
6
+ * and the dimensional ramp. Pattern-class split (geometry.md "accordion" row): the summary row is
7
+ * the interactive CONTROL-height row (`--md-sys-height-md`/`--md-sys-font-md`, density-INVARIANT frame); the
8
+ * body rhythm rides the `--md-sys-space-*` layout ladder (density-RESPONSIVE). The chevron is an INLINE
9
+ * AFFORDANCE (geometry.md §"Affordance vs content-icon") — sized `= font`, never the icon ramp.
10
+ * [2] STYLES BLOCK — `@scope (ui-disclosure)` CONSUMES only `--ui-disclosure-*` (+ the two shared fleet
11
+ * constants every control reads directly: the focus-ring pair, ADR-0009). The native `::marker`/
12
+ * `::-webkit-details-marker` is replaced by the chevron; its ORIENTATION (not colour, not size)
13
+ * carries the open/closed state — a non-color, non-motion signifier. NO fold animation and NO
14
+ * transition on the chevron rotation (ADR-0113 cl.4 / SPEC-R18 — `::details-content` interpolation
15
+ * is too cross-engine-fresh; a grep-able absence, pinned by disclosure-css.test.ts).
16
+ *
17
+ * Standalone stylesheet — NOT injected from disclosure.ts (behaviour-only `.ts`, no runtime injection per
18
+ * plan §2). Linked via the component-styles barrel at the integration wave (LLD-C11) — this folder ships
19
+ * standalone for the M1-a checkpoint (content-family.lld.md §9).
20
+ */
21
+
22
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-disclosure) declares --ui-disclosure-* ════════════════ */
23
+
24
+ :where(ui-disclosure) {
25
+ /* Summary row — Pattern-class interactive row (geometry.md: "interactive rows take the control
26
+ height"). Fixed at the md ramp step (ui-disclosure exposes no [size] axis — a fold header is not a
27
+ sized entry/action control). Density-INVARIANT (frame family, ∝ height). */
28
+ --ui-disclosure-height: var(--md-sys-height-md);
29
+ --ui-disclosure-font: var(--md-sys-font-md);
30
+ --ui-disclosure-ink: var(--md-sys-color-neutral-on-surface);
31
+ /* Hover (TKT-0047, revised twice): a background-PAINT wash, not an ink-step. The original ink-step
32
+ attempt (matching tabs.css) turned out visually inert — disclosure's idle ink already sits at the
33
+ ceiling of the neutral ramp (`-on-surface`, tokens.css:56), so no hover-ink value could ever show a
34
+ change. A first background-wash attempt reached for the tint ladder (`neutral-tint-dim`) — WRONG:
35
+ component-reviewer caught that the tint ladder was already EVICTED from row-hover fleet-wide (the
36
+ 2026-07-07 menu.css consistency pass, menu.css:87-96 — a 10% tint wash was the ONLY row-hover using
37
+ it, read as visibly weaker/inconsistent, and was converged onto the solid, scheme-inverting
38
+ `--md-sys-color-neutral-surface-high` role every other interactive-row/control hover already uses:
39
+ select's option, button (even the ghost variant, the closest structural analogue to this summary
40
+ row), text-field, checkbox, switch, calendar's cell). `tint-dim` is also near-invisible in dark
41
+ scheme (a scheme-INVARIANT ~5% black wash, tokens.css:1060, vs. `surface-high`'s scheme-inverting
42
+ solid role, tokens.css:86) — it would have silently resurfaced this exact defect in dark mode.
43
+ Converged onto the fleet's actual hover answer instead. Forced-colors: no extra WHCM rule needed —
44
+ the UA forces any background to Canvas regardless of which role is named here (the tabs.css
45
+ precedent: only the SELECTED state gets an explicit forced-colors rule, not hover). */
46
+ --ui-disclosure-bg-hover: var(--md-sys-color-neutral-surface-high);
47
+
48
+ /* The chevron — an INLINE AFFORDANCE (geometry.md §"Affordance vs content-icon"): `= font`, never the
49
+ icon ramp (`--ui-ind`) — the named oversize-caret bug class this law fixes. */
50
+ --ui-disclosure-glyph: var(--ui-disclosure-font);
51
+
52
+ /* Summary row inline layout — the icon↔label gap (rhythm family, ∝ font × density; geometry.md). */
53
+ --ui-disclosure-gap: var(--md-sys-gap-md);
54
+
55
+ /* Body rhythm — the `--md-sys-space-*` LAYOUT ladder (density-RESPONSIVE; the Pattern-class split,
56
+ geometry.md: "the shell uses the space scale"), never the control-height frame. */
57
+ --ui-disclosure-body-pad-block: var(--md-sys-space-sm);
58
+ --ui-disclosure-body-pad-inline: var(--md-sys-space-md);
59
+ }
60
+
61
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-disclosure) consumes ONLY --ui-disclosure-* ════════════════ */
62
+
63
+ @scope (ui-disclosure) {
64
+ :scope {
65
+ display: block;
66
+ color: var(--ui-disclosure-ink); /* the ambient ink a bare instance paints (ADR-0102 Lane A) — body content may override */
67
+ }
68
+
69
+ /* The summary row — a Pattern-class interactive CONTROL-height row: [chevron | label], flex (not the
70
+ host-as-grid anatomy — ui-disclosure is a rendered content cell, ADR-0113 cl.4, not host-as-grid).
71
+ `padding-block: 0` — the geometry law: block-size, never block-padding, is the vertical lever. */
72
+ :scope [data-part='summary'] {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: var(--ui-disclosure-gap);
76
+ box-sizing: border-box;
77
+ block-size: var(--ui-disclosure-height);
78
+ padding-block: 0;
79
+ padding-inline-start: calc(var(--ui-disclosure-height) / 2); /* slotless-edge law, h/2 (geometry.md) */
80
+ padding-inline-end: calc(var(--ui-disclosure-height) / 2);
81
+ font-size: var(--ui-disclosure-font);
82
+ line-height: var(--md-sys-control-line-height); /* single-line control standard (ADR-0036) */
83
+ color: var(--ui-disclosure-ink);
84
+ cursor: pointer;
85
+ list-style: none; /* hide the native ::marker (Chromium) */
86
+ -webkit-user-select: none;
87
+ user-select: none;
88
+ }
89
+
90
+ /* Firefox/older-WebKit render a `::-webkit-details-marker` / native triangle instead of `list-style` —
91
+ hide it explicitly (both engines, SPEC-R18 AC1). */
92
+ :scope [data-part='summary']::-webkit-details-marker {
93
+ display: none;
94
+ }
95
+ :scope [data-part='summary']::marker {
96
+ content: '';
97
+ }
98
+
99
+ /* The chevron — the inline affordance, sized = font (never the icon ramp). Orientation carries the
100
+ open/closed state (a shape/position signifier, not colour-only — ADR-0057); NO transition on the
101
+ rotation (SPEC-R18 — cross-engine + reduced-motion honesty; grep-able absence). */
102
+ :scope [data-part='chevron'] {
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ flex: none;
107
+ inline-size: var(--ui-disclosure-glyph);
108
+ block-size: var(--ui-disclosure-glyph);
109
+ color: currentColor;
110
+ rotate: 0deg;
111
+ }
112
+ :scope [data-part='chevron'] svg {
113
+ display: block;
114
+ inline-size: 100%;
115
+ block-size: 100%;
116
+ }
117
+ :scope > [data-part='details'][open] [data-part='chevron'] {
118
+ rotate: 90deg; /* caret-right → visually caret-down; orientation IS the state, no colour/size change */
119
+ }
120
+
121
+ /* The summary label — the fold's one-line text (the `summary` prop, textContent-only). */
122
+ :scope [data-part='summary-text'] {
123
+ flex: 1 1 auto;
124
+ min-inline-size: 0;
125
+ }
126
+
127
+ /* Hover (TKT-0047, revised) — a background-tint wash (the combo-box.css active-descendant
128
+ precedent), not an ink-step: disclosure's idle ink has no headroom to step to (see the
129
+ TOKEN block comment above). */
130
+ :scope [data-part='summary']:hover {
131
+ background-color: var(--ui-disclosure-bg-hover);
132
+ }
133
+
134
+ /* The body — rides the `--md-sys-space` layout ladder (density-responsive; the Pattern-class split). No
135
+ control-height law applies here (it is a content shell, not an interactive row). */
136
+ :scope [data-part='body'] {
137
+ padding-block: var(--ui-disclosure-body-pad-block);
138
+ padding-inline: var(--ui-disclosure-body-pad-inline);
139
+ }
140
+
141
+ /* Keyboard focus — the ONE shared fleet ring (ADR-0009), read directly (the button.css/select.css
142
+ exception to "consume only --ui-disclosure-*"): identical across every control, keyboard-only,
143
+ layout-neutral. The summary is natively focusable — no tabbable trait needed. */
144
+ :scope [data-part='summary']:focus-visible {
145
+ outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
146
+ outline-offset: var(--md-sys-state-focus-ring-offset);
147
+ }
148
+
149
+ /* forced-colors (WHCM) — summary/body text + the chevron all paint in system inks (SPEC-R18 AC3). Every
150
+ value here already resolves through a role that carries the WHCM mapping at the token layer
151
+ (tokens.md: neutral inks → CanvasText), so this block is belt-and-suspenders, explicit for the
152
+ computed-style probe — not load-bearing new colour. The chevron is `currentColor`, so it inherits the
153
+ summary's CanvasText for free; the focus ring survives via `--md-sys-color-focus-ring → Highlight`. */
154
+ @media (forced-colors: active) {
155
+ :scope {
156
+ color: CanvasText;
157
+ }
158
+ :scope [data-part='summary'] {
159
+ color: CanvasText;
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,37 @@
1
+ import { UIElement, type ReactiveProps } from '../../dom/index.js';
2
+ declare const props: {
3
+ open: {
4
+ reflect: true;
5
+ type: import("../../index.js").PropType<boolean>;
6
+ default: boolean;
7
+ attribute?: string | false;
8
+ };
9
+ summary: {
10
+ reflect: true;
11
+ type: import("../../index.js").PropType<string>;
12
+ default: string;
13
+ attribute?: string | false;
14
+ };
15
+ };
16
+ export interface UIDisclosureElement extends ReactiveProps<typeof props> {
17
+ }
18
+ export declare class UIDisclosureElement extends UIElement {
19
+ #private;
20
+ static props: {
21
+ open: {
22
+ reflect: true;
23
+ type: import("../../index.js").PropType<boolean>;
24
+ default: boolean;
25
+ attribute?: string | false;
26
+ };
27
+ summary: {
28
+ reflect: true;
29
+ type: import("../../index.js").PropType<string>;
30
+ default: string;
31
+ attribute?: string | false;
32
+ };
33
+ };
34
+ protected connected(): void;
35
+ protected disconnected(): void;
36
+ }
37
+ export {};
@@ -0,0 +1,207 @@
1
+ // disclosure.ts — UIDisclosureElement, the content-family M1-a `ui-disclosure` (content-family.lld.md
2
+ // LLD-C8 · SPEC-R14…R18 · ADR-0113 cl.4). BEHAVIOUR + props + the control-owned <details> part + self-
3
+ // define ONLY. Anatomy/geometry per the LLD; styling lives in disclosure.css, the public contract in
4
+ // disclosure.md.
5
+ //
6
+ // The no-native-form-elements law does not bind: a <details> participates in no form (the ADR-0017
7
+ // <dialog> precedent, applied here per ADR-0113's Context). Native <details>/<summary> supplies the
8
+ // toggle behaviour, the summary's button semantics + expanded/collapsed announcement, find-in-page
9
+ // auto-expand of folded content, and the `name` exclusive-accordion substrate — all platform-free; a
10
+ // bespoke button+region reimplements every one against internals-ARIA limits and loses the searchability
11
+ // story (ADR-0113 fork F3). `ui-disclosure` extends UIElement (NOT form-associated).
12
+ //
13
+ // Anatomy (a component-owned part, created ONCE — idempotent across disconnect/reconnect, the
14
+ // modal.ts/select.ts precedent):
15
+ // <ui-disclosure>
16
+ // <details data-part="details">
17
+ // <summary data-part="summary">
18
+ // <span data-part="chevron" aria-hidden="true"><svg>…caret-right (@agent-ui-kit/icons)…</svg></span>
19
+ // <span data-part="summary-text">…the `summary` prop…</span>
20
+ // </summary>
21
+ // <div data-part="body"><!-- the host's light-DOM children, adopted --></div>
22
+ // </details>
23
+ // </ui-disclosure>
24
+ //
25
+ // The anatomy invariant (SPEC-R16): the host's light-DOM children converge into the body part. Children
26
+ // present at connect are adopted in #ensureParts(); parser-streamed children landing on the host AFTER
27
+ // connect, and a destructive `host.textContent` write that detaches the details part, are healed by a
28
+ // childList MutationObserver — the ADR-0078 cl.4 stamp/heal lineage (ui-select's options-move / ui-text's
29
+ // stamp-heal precedent, `text.ts`).
30
+ //
31
+ // `open` is prop-as-source-of-truth under the ADR-0101 always-announce law (the ui-modal/ui-select `open`
32
+ // precedent): a scope-owned effect drives model→platform (`details.open = this.open` — a same-value
33
+ // assignment is a native no-op, no `toggle` event, the loop-breaker's first half); the platform's `toggle`
34
+ // event (fired on a user click, a model-driven write reaching the platform, OR a find-in-page auto-
35
+ // expand) is the SOLE announcer — it settles the prop (if the platform led) and always emits the host
36
+ // `toggle`, with `this.open` already settled at listener time (ADR-0101 mechanic 3).
37
+ //
38
+ // The toggle LISTENER is wired explicitly wherever a `details` part becomes current — once in connected()
39
+ // (covers the initial connect AND every reconnect, since parts persist across disconnect/reconnect but a
40
+ // fresh AbortController is minted each connect — the modal.ts/select.ts discipline) and once more inside
41
+ // the heal observer's rebuild branch (covers a MID-CONNECTION clobber-rebuild, which connected() never
42
+ // reruns for). The OLD part's listener dies with it — abort-owned, freed at the next disconnect regardless
43
+ // (a bounded, non-functional residual across a rare clobber, never a live double-fire).
44
+ //
45
+ // No internals role, no host ARIA (SPEC-R17): the details/summary part IS the semantic element (ADR-0017
46
+ // cl.5 lineage). No focus machinery: the summary is natively focusable. `render()` stays the inherited
47
+ // no-op (the part is imperative DOM, not a template commit). `controls → dom + @agent-ui-kit/icons` — the
48
+ // allowed import direction (icons is a zero-dep sibling package, like select.ts's own caret glyph).
49
+ import { UIElement, prop } from "../../dom/index.js";
50
+ import { setIcon } from '@agent-ui-kit/icons';
51
+ const props = {
52
+ // The fold's two-way state (ADR-0101 + ADR-0019) — prop-as-source-of-truth. Reflected so the [open]
53
+ // attribute mirrors the declared state (inspectable/serializable), the ui-modal/ui-select precedent.
54
+ open: { ...prop.boolean(false), reflect: true },
55
+ // The fold's one-line label — a bindable string prop (NOT a slot, SPEC-R14): keeps the heal invariant
56
+ // "children = body" simple (a rich `slot=summary` child is the named foreseen extension). Reflected so
57
+ // a JS-set value applies identically to an author-set attribute (the fleet reflect precedent).
58
+ summary: { ...prop.string(''), reflect: true },
59
+ };
60
+ export class UIDisclosureElement extends UIElement {
61
+ static props = props;
62
+ // The control-owned parts — created ONCE (idempotent guard in #ensureParts); persist through
63
+ // disconnect/reconnect. `#`-private: nothing outside the host can observe or hold them.
64
+ #details = null;
65
+ #summary = null;
66
+ #chevron = null;
67
+ #summaryText = null;
68
+ #body = null;
69
+ // The childList observer that heals the anatomy invariant (parser streaming / a host.textContent
70
+ // clobber). Disconnected in `disconnected()` — the ui-text `#observer` discipline (a raw platform
71
+ // observer isn't scope-owned, so teardown is by hand).
72
+ #observer = null;
73
+ connected() {
74
+ const { details } = this.#ensureParts();
75
+ // Summary effect — prop-driven, markup-free (SPEC-R16 AC2); never touches `open`. Reads the CURRENT
76
+ // `#summaryText` field (not a closed-over local) so it stays correct even after a heal rebuild
77
+ // replaces the part — though the rebuild itself re-syncs immediately (see #heal), without waiting for
78
+ // this effect to next fire.
79
+ this.effect(() => this.#syncSummary(this.summary));
80
+ // model→platform — `open` drives the platform. A same-value assignment is a native no-op (no `toggle`
81
+ // event fires) — the loop-breaker's first half (SPEC-R15 AC2). Reads `#details` fresh, same reason.
82
+ this.effect(() => this.#syncOpen(this.open));
83
+ // platform→prop→announce — wired for the CURRENT details part; re-wired on every connected() call
84
+ // (the reconnect case: parts persist but the AbortController is fresh) and again from the heal
85
+ // rebuild branch (the mid-connection clobber case, which connected() itself never reruns for).
86
+ this.#wireToggle(details);
87
+ // The heal observer — installed AFTER the initial part creation/effects above (the text.ts
88
+ // discipline: it never observes its own synchronous setup; it only fires for LATER host mutations).
89
+ this.#observer = new MutationObserver(() => this.#heal());
90
+ this.#observer.observe(this, { childList: true });
91
+ }
92
+ disconnected() {
93
+ this.#observer?.disconnect();
94
+ this.#observer = null;
95
+ }
96
+ /**
97
+ * Wire the platform `toggle` listener onto `details` (the SOLE announcer, SPEC-R15). Hears a user click
98
+ * on the summary, a find-in-page auto-expand, AND the open-effect's own platform write:
99
+ * - user/platform-led: `details.open` has ALREADY flipped by the time `toggle` fires, so `now` differs
100
+ * from the (still-stale) prop — sync `this.open = now` (which reflects the attribute synchronously,
101
+ * ADR-0101 mechanic 3), then announce. The open effect's own later re-run assigns the SAME value —
102
+ * a native no-op, no second event.
103
+ * - model-led: the open effect already set `this.open` (and `details.open`) BEFORE the platform's
104
+ * `toggle` fires, so `now === this.open` here — no re-write, no second wake — just the one announce.
105
+ * - a re-assert (`open` written to its current value): the open effect's assignment is a same-value
106
+ * platform no-op — no `toggle` ever fires, so this listener never runs and nothing announces
107
+ * (the loop-breaker's other half).
108
+ */
109
+ #wireToggle(details) {
110
+ this.listen(details, 'toggle', () => {
111
+ const now = details.open;
112
+ if (now !== this.open)
113
+ this.open = now;
114
+ this.emit('toggle');
115
+ });
116
+ }
117
+ /** Apply `text` to the CURRENT summary-text part, if any (a no-op before the part exists). */
118
+ #syncSummary(text) {
119
+ const summaryText = this.#summaryText;
120
+ if (summaryText)
121
+ summaryText.textContent = text;
122
+ }
123
+ /** Apply `open` to the CURRENT details part, if any (a no-op before the part exists). */
124
+ #syncOpen(open) {
125
+ const details = this.#details;
126
+ if (details)
127
+ details.open = open;
128
+ }
129
+ /**
130
+ * Restore the anatomy invariant after a childList mutation lands directly on the host (SPEC-R16). A
131
+ * DETACHED details part (a `host.textContent` clobber — every child, details part included, replaced)
132
+ * is never reused (it holds stale content, the ADR-0106/text.ts lineage): null every part ref, rebuild
133
+ * fresh via #ensureParts, re-wire the toggle listener onto the NEW part, and re-run the summary/open
134
+ * sync immediately (not waiting for the reactive effects to next fire — they read the CURRENT field but
135
+ * only re-run on a future prop CHANGE). Otherwise every stray host child (parser-streamed content
136
+ * arriving after connect) is adopted into the body part, append order. Both branches mutate the host and
137
+ * so re-fire this very observer, but the next delivery finds the invariant satisfied — a no-op — so this
138
+ * self-converges within ≤2 passes; it can never loop forever (SPEC-R16 AC1/AC3).
139
+ */
140
+ #heal() {
141
+ const details = this.#details;
142
+ if (!details)
143
+ return;
144
+ if (details.parentNode !== this) {
145
+ this.#details = null;
146
+ this.#summary = null;
147
+ this.#chevron = null;
148
+ this.#summaryText = null;
149
+ this.#body = null;
150
+ const fresh = this.#ensureParts();
151
+ this.#wireToggle(fresh.details);
152
+ this.#syncSummary(this.summary);
153
+ this.#syncOpen(this.open);
154
+ return;
155
+ }
156
+ const body = this.#body;
157
+ if (!body)
158
+ return;
159
+ for (const node of Array.from(this.childNodes)) {
160
+ if (node !== details)
161
+ body.appendChild(node);
162
+ }
163
+ }
164
+ /**
165
+ * Create the control's FIVE light-DOM parts ONCE (idempotent across disconnect/reconnect — parts
166
+ * persist, the modal.ts/select.ts precedent): the `<details data-part="details">` wrapping a
167
+ * `<summary data-part="summary">` (a `[data-part="chevron"]` glyph + `[data-part="summary-text"]` label)
168
+ * and a `<div data-part="body">`. Pre-existing host children are adopted into the body — MOVED, never
169
+ * cloned (ADR-0022) — before the details part is appended as the host's only element child. Does NOT
170
+ * wire the toggle listener (that is connected()'s / #heal's job — see the class doc) so this helper
171
+ * stays a pure "ensure the DOM shape" primitive, callable from either site. `render()` stays the
172
+ * inherited no-op.
173
+ */
174
+ #ensureParts() {
175
+ if (this.#details && this.#summary && this.#chevron && this.#summaryText && this.#body) {
176
+ return { details: this.#details };
177
+ }
178
+ const details = document.createElement('details');
179
+ details.setAttribute('data-part', 'details');
180
+ const summary = document.createElement('summary');
181
+ summary.setAttribute('data-part', 'summary');
182
+ const chevron = document.createElement('span');
183
+ chevron.setAttribute('data-part', 'chevron');
184
+ chevron.setAttribute('aria-hidden', 'true'); // decorative — disclosure meaning rides the native summary/details, never the glyph (anatomy.md §5)
185
+ setIcon(chevron, 'caret-right'); // Phosphor, via @agent-ui-kit/icons; rotated 90deg under [open] in disclosure.css
186
+ const summaryText = document.createElement('span');
187
+ summaryText.setAttribute('data-part', 'summary-text');
188
+ summary.appendChild(chevron);
189
+ summary.appendChild(summaryText);
190
+ const body = document.createElement('div');
191
+ body.setAttribute('data-part', 'body');
192
+ // Adopt every pre-existing host child into the body — moved, never cloned (ADR-0022).
193
+ while (this.firstChild)
194
+ body.appendChild(this.firstChild);
195
+ details.appendChild(summary);
196
+ details.appendChild(body);
197
+ this.appendChild(details);
198
+ this.#details = details;
199
+ this.#summary = summary;
200
+ this.#chevron = chevron;
201
+ this.#summaryText = summaryText;
202
+ this.#body = body;
203
+ return { details };
204
+ }
205
+ }
206
+ if (!customElements.get('ui-disclosure'))
207
+ customElements.define('ui-disclosure', UIDisclosureElement);
@@ -0,0 +1,140 @@
1
+ /* field.css — ui-field, the label/description/error wrapper stylesheet (ADR-0051 · LLD-C5).
2
+ *
3
+ * [1] TOKEN BLOCK — `:where(ui-field)` (specificity 0,0,0) declares the `--ui-field-*` chain: a single
4
+ * density-bearing spacing token (`--ui-field-gap`, off `--md-sys-space-xs` — the label↔control↔description
5
+ * rhythm) plus the three part inks/fonts (label / description / error), each a ROLE (`--md-sys-color-{family}-{role}`)
6
+ * or a font-ramp read (`--md-sys-font-*`) — never a `color-mix` (colour opinions live in the token layer). The
7
+ * error ink/font MATCH text-field's own message tokens (`--ui-text-field-message-*`) — one visual voice for
8
+ * the ONE error, whichever control renders it. `:has([disabled])` on the slotted control REPOINTS the
9
+ * label/description ink to the muted on-surface-variant role (role REPOINT, NOT opacity — tokens.md); kept
10
+ * in this `:where()` block (specificity 0, declared after the base) so the @scope block below still reads
11
+ * only `--ui-field-*`, same pattern as text-field's/checkbox's own `[disabled]` repoint.
12
+ * [2] STYLES BLOCK — `@scope (ui-field)` consumes ONLY `--ui-field-*`. Geometry class: Container/layout (the
13
+ * decomp's "STRUCTURE" intent) — NO control height anywhere, NO `[size]` prop/ramp; the host is a column
14
+ * flex stack (`row-gap: --ui-field-gap`) and the slotted control brings its own ADR-0021 width floor (the
15
+ * field adds none). Parts styled by `[data-part=label|description|error]`; DOM placement (label prepended,
16
+ * description/error appended — field.ts) already IS the reading order, so no CSS `order` is needed. The
17
+ * error part defaults to `display: none`, shown by `:scope:state(user-invalid) [data-part=error]` PLUS an
18
+ * explicit `[hidden]` guard written AFTER it (both (0,3,0) — source order breaks the tie; the guard must
19
+ * win so a state flip can't out-race the control's own hide — a frozen m4 repair, do not reorder). Motion:
20
+ * an UNCONDITIONAL small transition on the error part (the INDICATOR family pattern — no `:state(ready)`
21
+ * gate, because user-invalid is interaction-gated and the error literally cannot show at first paint, so
22
+ * the first-paint-flash concern :state(ready) exists for elsewhere never applies here), reduced-motion
23
+ * zeroes it. Forced-colors: the inks stay CanvasText (real text, not decorative glyphs — no
24
+ * `forced-color-adjust: none`); the error stays distinguishable by presence, not colour alone (WCAG 1.4.1).
25
+ *
26
+ * Standalone stylesheet — linked via the host page / component-styles barrel (s12); NEVER injected from
27
+ * field.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
28
+ */
29
+
30
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-field) declares --ui-field-* ════════════════ */
31
+
32
+ :where(ui-field) {
33
+ /* Layout rhythm — the ONE density-bearing quantity (label↔control↔description column gap). */
34
+ --ui-field-gap: var(--md-sys-space-xs);
35
+
36
+ /* Label — the field's own name text, above/beside the slotted control. */
37
+ --ui-field-label-ink: var(--md-sys-color-neutral-on-surface);
38
+ --ui-field-label-font: var(--md-sys-font-md);
39
+ --ui-field-label-weight: 500;
40
+
41
+ /* Required marker — the STATIC requiredness affordance appended to the label. Reads the LABEL ink, NOT
42
+ the error ink: requiredness is INVARIANT (it persists through a valid value), so the marker must read
43
+ as part of the label — never as the DYNAMIC validity/error signifier (which owns --ui-field-error-ink
44
+ and its own [data-part=error] text). Painting a required marker in the danger colour made a satisfied
45
+ field still read as "erroring" (the mark stays after a valid pick); that is exactly the intent-by-colour-
46
+ alone conflation ADR-0057 forbids. Following --ui-field-label-ink also mutes the marker with its label
47
+ under [disabled] (the repoint below flows through, var() being lazily substituted). */
48
+ --ui-field-required-ink: var(--ui-field-label-ink);
49
+
50
+ /* Description — the muted helper text under the control. */
51
+ --ui-field-description-ink: var(--md-sys-color-neutral-on-surface-variant);
52
+ --ui-field-description-font: var(--md-sys-font-sm);
53
+
54
+ /* Error — matches text-field's own message voice (--ui-text-field-message-*): one visual voice for
55
+ the ONE error, whichever control renders it. */
56
+ --ui-field-error-ink: var(--md-sys-color-danger-on-surface-variant); /* danger TEXT ink — AA-gated on-surface (8.48:1 light / 8.91:1 dark). Bare --md-sys-color-danger is 4.05:1 light = an AA text FAIL; only the TEXT ink moves (any danger BORDER channel stays --md-sys-color-danger, the 3:1 UI bar). 2026-07-02 color audit item 1. */
57
+ --ui-field-error-font: var(--md-sys-font-sm);
58
+ }
59
+
60
+ /* disabled → a role-REPOINT of the label/description ink (NOT opacity, tokens.md canon), keyed off the
61
+ SLOTTED control's reflected [disabled] attr (the field carries no disabled prop of its own — LLD-C4).
62
+ :where() holds this at specificity 0 so it wins the cascade by declaration order alone, the same pattern
63
+ as text-field's/checkbox's own [disabled] repoint block. */
64
+ :where(ui-field:has([disabled])) {
65
+ --ui-field-label-ink: var(--md-sys-color-neutral-on-surface-variant);
66
+ --ui-field-description-ink: var(--md-sys-color-neutral-on-surface-variant);
67
+ }
68
+
69
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-field) consumes ONLY --ui-field-* ════════════════ */
70
+
71
+ @scope (ui-field) {
72
+ /* Container/layout geometry class (no control height, no [size] ramp — the radio-group precedent):
73
+ a column stack, spacing off --md-sys-space-* × density. The parts' DOM order (label prepended,
74
+ description/error appended around the slotted control — field.ts) already IS the reading order. */
75
+ :scope {
76
+ display: flex;
77
+ flex-direction: column;
78
+ row-gap: var(--ui-field-gap);
79
+ min-inline-size: 0; /* the slotted control brings its own ADR-0021 width floor; the field adds none */
80
+ }
81
+
82
+ :scope [data-part='label'] {
83
+ color: var(--ui-field-label-ink);
84
+ font-size: var(--ui-field-label-font);
85
+ font-weight: var(--ui-field-label-weight);
86
+ }
87
+
88
+ :scope [data-part='description'] {
89
+ color: var(--ui-field-description-ink);
90
+ font-size: var(--ui-field-description-font);
91
+ }
92
+
93
+ /* required affordance — pure CSS off the slotted control's reflected [required] attr (LLD-C4/C5); no
94
+ field-side state duplication. */
95
+ :scope:has([required]) [data-part='label']::after {
96
+ content: ' *';
97
+ color: var(--ui-field-required-ink); /* label-matched, NOT error ink — a static requiredness mark is not the dynamic error signifier (ADR-0057) */
98
+ }
99
+
100
+ /* Error visibility — default hidden; field.ts toggles the host's :state(user-invalid) custom state and
101
+ the part's [hidden] attribute together (#renderValidity), but the CSS gate is the custom state (the
102
+ part's own text emptiness is the content guard). Base rule at (0,2,0) carries the ink/font always. */
103
+ :scope [data-part='error'] {
104
+ display: none;
105
+ color: var(--ui-field-error-ink);
106
+ font-size: var(--ui-field-error-font);
107
+ transition: color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard); /* unconditional — the INDICATOR pattern: no :state(ready) gate, the error cannot show at first paint (user-invalid is interaction-gated) */
108
+ }
109
+
110
+ /* Shown when the associated control's user-invalid gate is live (LLD-C1's userInvalid() read, mirrored
111
+ onto this host's own :state(user-invalid) by field.ts's #renderValidity). */
112
+ :scope:state(user-invalid) [data-part='error'] {
113
+ display: block;
114
+ }
115
+
116
+ /* The explicit [hidden] guard — written AFTER the state rule above. Both compute specificity (0,3,0),
117
+ so source order decides; this guard MUST win so field.ts's own hidden toggle (e.g. an empty
118
+ validationMessage while user-invalid) can always suppress the box. Frozen m4 repair — do not reorder. */
119
+ :scope [data-part='error'][hidden] {
120
+ display: none;
121
+ }
122
+
123
+ @media (prefers-reduced-motion: reduce) {
124
+ :scope [data-part='error'] {
125
+ transition: none; /* accessibility (non-negotiable) — reduced motion zeroes the error transition */
126
+ }
127
+ }
128
+
129
+ /* Forced colors (WHCM) — label/description/error stay CanvasText (real text content, not decorative
130
+ glyphs — no forced-color-adjust:none). The error remains distinguishable by PRESENCE, not colour
131
+ alone (WCAG 1.4.1 — the text IS the cue; --md-sys-color-danger already degrades to CanvasText's fleet mapping
132
+ via the token layer, this rule documents the intent rather than overriding a colour that survives). */
133
+ @media (forced-colors: active) {
134
+ :scope [data-part='label'],
135
+ :scope [data-part='description'],
136
+ :scope [data-part='error'] {
137
+ color: CanvasText;
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,27 @@
1
+ import { UIElement, type ReactiveProps } from '../../dom/index.js';
2
+ declare const props: {
3
+ label: {
4
+ reflect: true;
5
+ type: import("../../index.js").PropType<string>;
6
+ default: string;
7
+ attribute?: string | false;
8
+ };
9
+ description: import("../../index.js").PropConfig<string>;
10
+ };
11
+ export interface UIFieldElement extends ReactiveProps<typeof props> {
12
+ }
13
+ export declare class UIFieldElement extends UIElement {
14
+ #private;
15
+ static props: {
16
+ label: {
17
+ reflect: true;
18
+ type: import("../../index.js").PropType<string>;
19
+ default: string;
20
+ attribute?: string | false;
21
+ };
22
+ description: import("../../index.js").PropConfig<string>;
23
+ };
24
+ protected connected(): void;
25
+ protected disconnected(): void;
26
+ }
27
+ export {};