@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,194 @@
1
+ /* switch.css — ui-switch, the single-file component stylesheet (indicator-element.lld.md LLD-C4 / ADR-0041).
2
+ * Two clearly-SECTIONED blocks:
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-switch)` (specificity 0,0,0) DECLARES the `--ui-switch-*` chain from the
5
+ * colour roles (`--md-sys-color-{family}-{role}`) and the compact widget ramp (`--md-sys-compact-{size}` / ADR-0041).
6
+ * `[size]` repoints the box token (the widget ramp step); `:state(checked)` repoints the track + thumb
7
+ * colour channels to the primary family (ON = filled primary, OFF = muted neutral); `[disabled]` mutes
8
+ * everything. No `color-mix` — colour opinions live in the token layer (ADR-0008).
9
+ * [2] STYLES BLOCK — `@scope (ui-switch)` CONSUMES only `--ui-switch-*` for its OWN tokens. The host is
10
+ * an `inline-flex` row: the TRACK (`::before`) is the pill (block-size = box, inline-size ≈ 1.8× box,
11
+ * border-radius = box/2) and sits as the first flex item; the label slot follows it. The THUMB (`::after`)
12
+ * is a circle (`position: absolute`) inset by `--md-sys-widget-inset` (2px) → thumb = box − 4px (ADR-0041
13
+ * cl.3: the 2px inset law). The thumb slides from `inset-inline-start: inset` (unchecked) to
14
+ * `translate: calc(box * 0.8)` on `:state(checked)` — a net shift of 0.8× box, landing the thumb
15
+ * with a symmetric 2px inset from the track end (the ADR-0041 rendered-px proof).
16
+ * `:focus-visible` ring (keyboard-only, ADR-0009). Forced-colors safe. Density-invariant.
17
+ *
18
+ * Standalone stylesheet — linked via the host page / component-styles barrel (Phase 2); NEVER injected from
19
+ * switch.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
20
+ */
21
+
22
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-switch) declares --ui-switch-* ════════════════ */
23
+
24
+ :where(ui-switch) {
25
+ /* Track colour (unchecked default = solid neutral). A per-state ROLE-LADDER step, never a color-mix
26
+ (ADR-0008). The OFF track is a state-bearing control part → the SOLID --md-sys-color-neutral-track (SC 1.4.11
27
+ 3:1 vs every surface plane), NOT the old translucent --md-sys-color-neutral-outline-variant which composited to
28
+ 1.51:1 light / 1.73:1 (an SC 1.4.11 fail — the off switch went near-invisible; the white thumb did
29
+ not carry it either, 1.22–1.84:1). Hover steps one role more prominent (2026-07-02 color audit item 4
30
+ · ADR-0059). The CHECKED track (primary, below) already clears 3.94:1 light / 4.68:1 dark — untouched. */
31
+ --ui-switch-track: var(--md-sys-color-neutral-track);
32
+ --ui-switch-track-hover: var(--md-sys-color-neutral-track-hover);
33
+ /* Thumb colour (unchecked default = brightest neutral surface ≈ white). Constant across schemes. */
34
+ --ui-switch-thumb: var(--md-sys-color-neutral-surface-brightest);
35
+
36
+ /* Box geometry — the compact widget-box ramp step (ADR-0041; default size = md). `[size]` repoints. */
37
+ --ui-switch-box: var(--md-sys-compact-md);
38
+
39
+ /* Label gap — the rhythm between the pill and the optional label text (density-bearing, via --md-sys-gap-md). */
40
+ --ui-switch-gap: var(--md-sys-gap-md);
41
+ }
42
+
43
+ /* [size] → compact widget ramp repoint (ADR-0041: the LITERAL per-[scale] table in dimensions.css
44
+ re-tables --md-sys-compact-{sm,md,lg}; `[size]` on the host picks which step the switch box targets). */
45
+ :where(ui-switch[size='sm']) {
46
+ --ui-switch-box: var(--md-sys-compact-sm);
47
+ --ui-switch-gap: var(--md-sys-gap-sm);
48
+ }
49
+ :where(ui-switch[size='lg']) {
50
+ --ui-switch-box: var(--md-sys-compact-lg);
51
+ --ui-switch-gap: var(--md-sys-gap-lg);
52
+ }
53
+
54
+ /* :state(checked) — primary-family track + on-primary thumb: the ON state, solid primary fill.
55
+ Hover steps to a darker primary (same ADR-0008 role-ladder pattern as the button). */
56
+ :where(ui-switch):state(checked) {
57
+ --ui-switch-track: var(--md-sys-color-primary);
58
+ --ui-switch-track-hover: var(--md-sys-color-primary-hover);
59
+ --ui-switch-thumb: var(--md-sys-color-primary-on-primary);
60
+ }
61
+
62
+ /* [disabled] → muted neutral roles (overrides all states). HOLDS at idle: the disabled host is
63
+ pointer-inert (styles block), so `:hover` never matches it (ADR-0008 disabled pattern). */
64
+ :where(ui-switch[disabled]) {
65
+ --ui-switch-track: var(--md-sys-color-neutral-surface-high);
66
+ --ui-switch-track-hover: var(--md-sys-color-neutral-surface-high);
67
+ --ui-switch-thumb: var(--md-sys-color-neutral-on-surface-variant);
68
+ }
69
+
70
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-switch) consumes ONLY --ui-switch-* ════════════════ */
71
+
72
+ @scope (ui-switch) {
73
+ /* Host: inline-flex row — the pill track (::before) + optional label slot side by side. */
74
+ :scope {
75
+ display: inline-flex;
76
+ align-items: center;
77
+ gap: var(--ui-switch-gap);
78
+ position: relative; /* containing block for the absolutely-positioned ::after thumb */
79
+ cursor: pointer;
80
+ -webkit-user-select: none; /* WebKit/Safari still requires the prefix */
81
+ user-select: none;
82
+ }
83
+
84
+ /* TRACK (::before) — the pill; a sized flex item.
85
+ block-size = --ui-switch-box; inline-size ≈ 1.8× box; pill radius = box/2.
86
+ State-PAINT transition only — geometry never animates (geometry.md). */
87
+ :scope::before {
88
+ content: '';
89
+ display: block;
90
+ flex-shrink: 0; /* never crushed by a long label */
91
+ box-sizing: border-box;
92
+
93
+ block-size: var(--ui-switch-box);
94
+ inline-size: calc(var(--ui-switch-box) * 1.8);
95
+ border-radius: calc(var(--ui-switch-box) / 2); /* pill radius = box/2 */
96
+
97
+ background: var(--ui-switch-track);
98
+ transition: background-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
99
+ }
100
+
101
+ /* THUMB (::after) — a circle inset by --md-sys-widget-inset (2px) on all sides (ADR-0041 cl.3).
102
+ *
103
+ * Size: thumb = box − 2×inset = box − 4px.
104
+ * Block: inset-block-start: 50% + transform: translateY(-50%) centres the thumb in the host height.
105
+ * With align-items: center on the host, the track (::before) is also vertically centred, so
106
+ * the thumb centre coincides with the track centre in both the bare-pill and with-label cases.
107
+ * Inline: unchecked → inset-inline-start = inset (2px from inline-start of the host = of the track).
108
+ * checked → translate = 0.8×box (derived: track_width − thumb_size − inset − inset_start
109
+ * = 1.8×box − (box−4) − 2 − 2 = 0.8×box — start and end insets are symmetric).
110
+ *
111
+ * Motion: `translate` transitions the thumb slide — a paint-only transform (no layout shift). The
112
+ * transition is suppressed under `prefers-reduced-motion` (accessibility non-negotiable). */
113
+ :scope::after {
114
+ content: '';
115
+ position: absolute;
116
+ box-sizing: border-box;
117
+
118
+ /* ADR-0041 cl.3: thumb = box − 2×inset = box − 4px */
119
+ --ui-switch-thumb-size: calc(var(--ui-switch-box) - 2 * var(--md-sys-widget-inset));
120
+ inline-size: var(--ui-switch-thumb-size);
121
+ block-size: var(--ui-switch-thumb-size);
122
+ border-radius: 50%; /* circle */
123
+
124
+ /* Unchecked starting position: 2px from inline-start + vertically centred in the host */
125
+ inset-block-start: 50%;
126
+ transform: translateY(-50%);
127
+ inset-inline-start: var(--md-sys-widget-inset);
128
+
129
+ background: var(--ui-switch-thumb);
130
+ transition: translate var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
131
+ }
132
+
133
+ /* :state(checked) — slide the thumb to the END by 0.8×box (the net shift from the start position).
134
+ * The end-side inset = track_width − (inset_start + 0.8×box + thumb_size) = 2px = --md-sys-widget-inset ✓
135
+ * (the 2px inset law holds on BOTH edges and in BOTH states — the ADR-0041 rendered-px proof). */
136
+ :scope:state(checked)::after {
137
+ translate: calc(var(--ui-switch-box) * 0.8) 0;
138
+ }
139
+
140
+ /* Hover — repoints `background` of the track from the per-state token (ADR-0008). Disabled is
141
+ pointer-inert so :hover never matches it (the disabled token holds at the idle value, no lift possible). */
142
+ :scope:hover::before {
143
+ background: var(--ui-switch-track-hover);
144
+ }
145
+
146
+ /* Focus ring (ADR-0009) — the SHARED fleet ring: `:focus-visible` keyboard-only. These are the ONE
147
+ deliberate exception to "consume only --ui-switch-*": a focus ring is a fleet constant, read directly
148
+ so every control draws the identical ring. `outline` is layout-neutral (no box geometry shift). */
149
+ :scope:focus-visible {
150
+ outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
151
+ outline-offset: var(--md-sys-state-focus-ring-offset);
152
+ }
153
+
154
+ /* Reduced motion — zero all transitions on this control (geometry.md: NEVER animate the ramp;
155
+ accessibility: non-negotiable for users who need it). */
156
+ @media (prefers-reduced-motion: reduce) {
157
+ :scope::before {
158
+ transition: none;
159
+ }
160
+ :scope::after {
161
+ transition: none; /* the thumb slide must also be suppressed */
162
+ }
163
+ }
164
+
165
+ /* disabled host — pointer-inert so :hover/:active never lift the muted neutral tokens */
166
+ :scope:is([disabled]) {
167
+ pointer-events: none;
168
+ cursor: default;
169
+ }
170
+
171
+ /* forced-colors — keep the track visible as a border + Canvas fill (unchecked) or Highlight (checked);
172
+ the thumb paints as ButtonText (off) / HighlightText (on). forced-color-adjust:none preserves our
173
+ explicit system-colour mappings so the browser does not re-override them (matches checkbox.css pattern).
174
+ The `:focus-visible` ring survives for free: `--md-sys-color-focus-ring → Highlight` from the token layer. */
175
+ @media (forced-colors: active) {
176
+ :scope::before {
177
+ border: 2px solid ButtonText;
178
+ background: Canvas;
179
+ }
180
+ :scope:state(checked)::before {
181
+ border-color: Highlight;
182
+ background: Highlight;
183
+ forced-color-adjust: none;
184
+ }
185
+ :scope::after {
186
+ background: ButtonText;
187
+ forced-color-adjust: none;
188
+ }
189
+ :scope:state(checked)::after {
190
+ background: HighlightText;
191
+ forced-color-adjust: none;
192
+ }
193
+ }
194
+ }
@@ -0,0 +1,5 @@
1
+ import { UIIndicatorElement } from '../_base/index.js';
2
+ export declare class UISwitchElement extends UIIndicatorElement {
3
+ /** LLD-C2: ARIA role for the switch widget — UIIndicatorElement.connected() sets internals.role from this. */
4
+ static role: string;
5
+ }
@@ -0,0 +1,18 @@
1
+ // switch.ts — UISwitchElement, the FACE switch control (goals.md §G6 / indicator-element.lld.md LLD-C4).
2
+ // BEHAVIOUR + self-define ONLY — the base UIIndicatorElement owns everything: the boolean form value
3
+ // (checked/value props + formValue()), the state machine + ARIA (ariaChecked, :state(checked),
4
+ // tabbable/disabled), and the pressActivation toggle (click/Space toggles; Enter does NOT — platform parity).
5
+ //
6
+ // This leaf adds ONLY: `static role = 'switch'` (LLD-C2 — internals.role set from it in connected()), the
7
+ // pill-track + 2px-inset thumb geometry in switch.css (LLD-C4), and the contract in switch.md.
8
+ // Boolean only (no indeterminate — a switch is ON/OFF; the base's `indeterminate` property is not used
9
+ // here and no CSS paints an indeterminate state). Optional label slot (light-DOM, anatomy ADR-0006).
10
+ //
11
+ // Layer: controls/ — imports only controls/_base/ (inward-only ✓: controls ← traits ← dom ← reactive).
12
+ import { UIIndicatorElement } from "../_base/index.js";
13
+ export class UISwitchElement extends UIIndicatorElement {
14
+ /** LLD-C2: ARIA role for the switch widget — UIIndicatorElement.connected() sets internals.role from this. */
15
+ static role = 'switch';
16
+ }
17
+ if (!customElements.get('ui-switch'))
18
+ customElements.define('ui-switch', UISwitchElement);
@@ -0,0 +1,43 @@
1
+ import type { PropConfig } from '../../dom/props.js';
2
+ /** One table column — the SAME shape serves both the public input contract (SPEC-R1: `type` is optional,
3
+ * defaulting to `'string'`) and the rendered-set (post-`cleanColumns`, `type` is always POPULATED — a
4
+ * runtime invariant `cleanColumns` guarantees, not one TS enforces, so a caller may omit `type` and still
5
+ * satisfy this interface). A valid `key`/`label` string pair; `type` NORMALIZED to a member of the closed
6
+ * set when present. */
7
+ export interface TableColumn {
8
+ key: string;
9
+ label: string;
10
+ type?: 'string' | 'number';
11
+ }
12
+ /** One table row — a structurally valid record (SPEC §2): a plain, non-null, non-array object. Cell
13
+ * VALUES are unconstrained here (`string | number | null | undefined | unknown`) — `resolveCell` is the
14
+ * only place a value is judged. Never positional (fork F1) — cells resolve by `column.key` lookup. */
15
+ export type TableRow = Record<string, unknown>;
16
+ /**
17
+ * Harden an arbitrary input into the rendered column set (SPEC-R3 rows 1/3/4): a non-array input yields
18
+ * `[]`; each entry survives only as a plain object with a `string` `key` AND a `string` `label` — dropped,
19
+ * never coerced (SPEC-R3 row 3). `type` is NORMALIZED, never a drop reason: `'number'` is kept as-is,
20
+ * anything else (absent, or an unknown string — SPEC-R3 row 4) becomes `'string'`. Order preserved
21
+ * (positional — duplicate keys both survive, SPEC-R3 row 12).
22
+ */
23
+ export declare function cleanColumns(input: unknown): TableColumn[];
24
+ /**
25
+ * Harden an arbitrary input into the rendered row set (SPEC-R3 row 5): a non-array input yields `[]`; each
26
+ * entry survives only as a plain object (non-null, non-array) — dropped, never coerced. Cell values are
27
+ * NOT inspected here (structural validity only, SPEC §2) — `resolveCell` judges each cell at render time.
28
+ * Order preserved (positional).
29
+ */
30
+ export declare function cleanRows(input: unknown): TableRow[];
31
+ /** Format a finite number via the shared, memoized `Intl.NumberFormat` (default locale). */
32
+ export declare function formatNumber(v: number): string;
33
+ /**
34
+ * The SPEC-R3 cell-resolution table (rows 6-11), as one pure, NEVER-throwing function: absent/missing key/
35
+ * `undefined`/`null` → `''` (the empty cell, rows 6/7) · a finite `number` (ANY column type — row 10, value-
36
+ * driven, never column-gated) → `formatNumber` · a non-finite `number` (`NaN`/`±Infinity`, row 8) → the `—`
37
+ * placeholder · a `string` (row 9, incl. a type-mismatched string in a `type:'number'` column) → verbatim,
38
+ * uncoerced · anything else — `boolean`/`object`/`array` (row 11, a foreign-typed cell) → `''`, the value
39
+ * dropped, the row survives. Alignment is NEVER decided here — it is column-driven (LLD-C2/C3).
40
+ */
41
+ export declare function resolveCell(column: TableColumn, row: TableRow): string;
42
+ export declare const tableColumnsProp: PropConfig<TableColumn[]>;
43
+ export declare const tableRowsProp: PropConfig<TableRow[]>;
@@ -0,0 +1,124 @@
1
+ // table-model.ts — the pure, DOM-free table math (LLD-C1, report-family.lld.md §2). No DOM, no host,
2
+ // unit-testable in plain Node/Vitest. Owns four things (the bar-math/sparkline-math precedent — the
3
+ // in-folder pure-core split, ADR-0111 cl.8):
4
+ //
5
+ // 1. Input hardening (`cleanColumns`/`cleanRows`, SPEC-R3 rows 1/3/4/5): a non-array input is `[]`; a
6
+ // column entry survives only as a plain object with a `string` `key` AND a `string` `label` — `type`
7
+ // is NORMALIZED ('number' kept, anything else incl. an unknown string → 'string'), never dropping the
8
+ // column over a bad `type`. A row entry survives only as a plain object (non-null, non-array) — cell
9
+ // VALUES are not judged here, that is `resolveCell`'s per-cell job (structural validity only, SPEC §2).
10
+ // Both drop, never coerce; order is preserved (positional — SPEC-R3 row 12's duplicate-key case).
11
+ // 2. Cell resolution (`resolveCell`, SPEC-R3 rows 6-11): the pure mapping `(column, row) → rendered cell
12
+ // text`, covering every value-degenerate case. NEVER throws. Alignment is NOT decided here — it is
13
+ // COLUMN-driven (the `data-type='number'` attribute the element sets from `column.type`, LLD-C3) —
14
+ // this function only decides the printed TEXT, which is VALUE-driven.
15
+ // 3. `formatNumber` — a module-memoized default-locale `Intl.NumberFormat` (the chart-family printed-value
16
+ // precedent, bar-math.ts/sparkline-math.ts).
17
+ // 4. `tableColumnsProp`/`tableRowsProp`, the safe JSON codecs (SPEC-R1 AC3): `from(null)` (attribute
18
+ // absent/removed) is `[]`, NEVER `null`; malformed attribute JSON is caught and also falls back to
19
+ // `[]` — no throw ever reaches `attributeChangedCallback`. `dom/props.ts`'s generic `jsonType<T>()` is
20
+ // deliberately NOT used — its bare `JSON.parse` throws on malformed attributes and maps a removed
21
+ // attribute to `null`, both of which SPEC-R1 forbids reaching the render path (the exact reasoning
22
+ // bar-math.ts's `barDataProp` states for its own codec — the chart-family construction, reused here).
23
+ /** A plain, non-null, non-array object — the structural test both `cleanColumns` and `cleanRows` share. */
24
+ function isPlainObject(v) {
25
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
26
+ }
27
+ /**
28
+ * Harden an arbitrary input into the rendered column set (SPEC-R3 rows 1/3/4): a non-array input yields
29
+ * `[]`; each entry survives only as a plain object with a `string` `key` AND a `string` `label` — dropped,
30
+ * never coerced (SPEC-R3 row 3). `type` is NORMALIZED, never a drop reason: `'number'` is kept as-is,
31
+ * anything else (absent, or an unknown string — SPEC-R3 row 4) becomes `'string'`. Order preserved
32
+ * (positional — duplicate keys both survive, SPEC-R3 row 12).
33
+ */
34
+ export function cleanColumns(input) {
35
+ if (!Array.isArray(input))
36
+ return [];
37
+ const out = [];
38
+ for (const entry of input) {
39
+ if (!isPlainObject(entry))
40
+ continue;
41
+ const key = entry.key;
42
+ const label = entry.label;
43
+ if (typeof key !== 'string' || typeof label !== 'string')
44
+ continue;
45
+ out.push({ key, label, type: entry.type === 'number' ? 'number' : 'string' });
46
+ }
47
+ return out;
48
+ }
49
+ /**
50
+ * Harden an arbitrary input into the rendered row set (SPEC-R3 row 5): a non-array input yields `[]`; each
51
+ * entry survives only as a plain object (non-null, non-array) — dropped, never coerced. Cell values are
52
+ * NOT inspected here (structural validity only, SPEC §2) — `resolveCell` judges each cell at render time.
53
+ * Order preserved (positional).
54
+ */
55
+ export function cleanRows(input) {
56
+ if (!Array.isArray(input))
57
+ return [];
58
+ const out = [];
59
+ for (const entry of input) {
60
+ if (isPlainObject(entry))
61
+ out.push(entry);
62
+ }
63
+ return out;
64
+ }
65
+ /** Module-memoized default-locale formatter — the exact text every finite-number cell prints (SPEC-R3 row 10). */
66
+ const numberFormat = new Intl.NumberFormat();
67
+ /** Format a finite number via the shared, memoized `Intl.NumberFormat` (default locale). */
68
+ export function formatNumber(v) {
69
+ return numberFormat.format(v);
70
+ }
71
+ /** The defined "present but unrepresentable" placeholder (SPEC §2) — U+2014 EM DASH, never the strings
72
+ * `NaN`/`Infinity`/`-Infinity`. */
73
+ const PLACEHOLDER = '—';
74
+ /**
75
+ * The SPEC-R3 cell-resolution table (rows 6-11), as one pure, NEVER-throwing function: absent/missing key/
76
+ * `undefined`/`null` → `''` (the empty cell, rows 6/7) · a finite `number` (ANY column type — row 10, value-
77
+ * driven, never column-gated) → `formatNumber` · a non-finite `number` (`NaN`/`±Infinity`, row 8) → the `—`
78
+ * placeholder · a `string` (row 9, incl. a type-mismatched string in a `type:'number'` column) → verbatim,
79
+ * uncoerced · anything else — `boolean`/`object`/`array` (row 11, a foreign-typed cell) → `''`, the value
80
+ * dropped, the row survives. Alignment is NEVER decided here — it is column-driven (LLD-C2/C3).
81
+ */
82
+ export function resolveCell(column, row) {
83
+ const value = row[column.key];
84
+ if (value === undefined || value === null)
85
+ return '';
86
+ if (typeof value === 'number')
87
+ return Number.isFinite(value) ? formatNumber(value) : PLACEHOLDER;
88
+ if (typeof value === 'string')
89
+ return value;
90
+ return ''; // boolean / object / array — a foreign-typed cell value, dropped (the row survives)
91
+ }
92
+ /**
93
+ * The safe `columns`/`rows` codec (SPEC-R1 AC3): `from(null)` (the attribute absent/removed) → `[]`, never
94
+ * `null`; malformed attribute JSON is caught and also falls back to `[]` — no throw ever reaches
95
+ * `attributeChangedCallback`. Every parsed value (well-formed or not) is run through the matching hardening
96
+ * function, so the property never carries a raw, un-hardened array (the codec and the SPEC-R3 case-3
97
+ * property-write guard — table.ts's effects call `cleanColumns`/`cleanRows` again — share the one hardening
98
+ * function each).
99
+ */
100
+ function safeJsonCodec(clean) {
101
+ return {
102
+ from(attr) {
103
+ if (attr === null)
104
+ return [];
105
+ try {
106
+ return clean(JSON.parse(attr));
107
+ }
108
+ catch {
109
+ return [];
110
+ }
111
+ },
112
+ to(value) {
113
+ return JSON.stringify(value);
114
+ },
115
+ };
116
+ }
117
+ export const tableColumnsProp = {
118
+ type: safeJsonCodec(cleanColumns),
119
+ default: [],
120
+ };
121
+ export const tableRowsProp = {
122
+ type: safeJsonCodec(cleanRows),
123
+ default: [],
124
+ };
@@ -0,0 +1,90 @@
1
+ /* table.css — ui-table, the Display-class static data table (LLD-C3, report-family.lld.md §2;
2
+ * report-family.spec.md SPEC-R2/R3/R5/R14/R15/R16/R17). Two clearly-SECTIONED blocks (ADR-0003):
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-table)` (specificity 0,0,0) DECLARES every `--ui-table-*` component
5
+ * token from the fleet `--md-sys-color-*` roles + the `--md-sys-space-*` rhythm ladder. No `[size]`/
6
+ * `[scale]` repoint matrix — `ui-table` is Display-class (SPEC-R17): no control height, no
7
+ * `--md-sys-height-*` lever, the type matrix + the space ladder are the whole lever.
8
+ * [2] STYLES BLOCK — `@scope (ui-table)` CONSUMES only `--ui-table-*` (∪ the shared `--md-sys-*`/
9
+ * `--md-sys-space-*` namespaces). Owns the UA-default + page-cascade reset a stamped native `<table>`
10
+ * needs (border-collapse, cell padding, caption alignment — SPEC-R2 AC2, the ADR-0078 cl.4
11
+ * `font: inherit`-reset precedent scaled up) and the overflow-in-own-container law (SPEC-R5): the
12
+ * component's OWN `[data-part='scroll']` container scrolls, never the page.
13
+ *
14
+ * RTL (SPEC-R16): logical properties throughout (`text-align: start/end`, `border-block-end`, block/inline
15
+ * padding pairs); the native `<table>` handles column order under `dir="rtl"` for free; `text-align: end`
16
+ * flips number-column alignment — the browser leg (table.browser.test.ts) measures it.
17
+ *
18
+ * WHCM (SPEC-R15): NO dedicated `forced-colors` block — every row/header separator is a REAL `border`
19
+ * (system-ink-repainted, never removed under `forced-colors: active`, unlike a background); all content is
20
+ * real text. The bar-chart/badge fill lesson (a background-drawn mark vanishing under WHCM) does not apply
21
+ * here — nothing in this file draws a background-only mark.
22
+ *
23
+ * Standalone stylesheet — linked via the host page / component-styles barrel (Phase 2); NEVER injected
24
+ * from table.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
25
+ */
26
+
27
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-table) declares --ui-table-* ════════════════ */
28
+
29
+ :where(ui-table) {
30
+ --ui-table-min-inline-size: 16em; /* the whole-shape floor (SPEC-R14/R17 AC1) */
31
+ --ui-table-cell-pad-inline: var(--md-sys-space-sm); /* rhythm — rides [density] free (ADR-0103) */
32
+ --ui-table-cell-pad-block: var(--md-sys-space-xs);
33
+ --ui-table-rule-ink: var(--md-sys-color-neutral-outline-variant); /* row-separator hairline */
34
+ --ui-table-header-rule-ink: var(--md-sys-color-neutral-outline); /* thead underline, stronger */
35
+ --ui-table-caption-ink: var(--md-sys-color-neutral-on-surface-variant);
36
+ }
37
+
38
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-table) consumes ONLY --ui-table-* ════════════════ */
39
+
40
+ @scope (ui-table) {
41
+ :scope {
42
+ display: block;
43
+ min-inline-size: var(--ui-table-min-inline-size); /* the whole-shape floor — a bare instance never collapses */
44
+ }
45
+
46
+ /* the component's OWN overflow container (SPEC-R5) — a wide table scrolls HERE, never the page */
47
+ :scope [data-part='scroll'] {
48
+ overflow-x: auto;
49
+ }
50
+
51
+ /* the UA-default + page-cascade reset (SPEC-R2 AC2) — a stamped native <table> owns its OWN geometry */
52
+ :scope table {
53
+ border-collapse: collapse;
54
+ inline-size: 100%; /* a narrow table fills the host — no orphaned gutter (SPEC-R5) */
55
+ font: inherit;
56
+ text-align: start;
57
+ font-size: var(--md-sys-typescale-body-medium-size);
58
+ line-height: var(--md-sys-typescale-body-medium-line-height);
59
+ }
60
+
61
+ :scope caption {
62
+ text-align: start;
63
+ color: var(--ui-table-caption-ink);
64
+ padding-block-end: var(--ui-table-cell-pad-block);
65
+ }
66
+
67
+ :scope th,
68
+ :scope td {
69
+ padding: var(--ui-table-cell-pad-block) var(--ui-table-cell-pad-inline);
70
+ text-align: start;
71
+ overflow-wrap: anywhere; /* a huge unbroken string wraps within its cell (SPEC-R3 row 14) */
72
+ }
73
+
74
+ :scope th {
75
+ font-weight: var(--md-sys-typescale-label-large-weight);
76
+ border-block-end: 1px solid var(--ui-table-header-rule-ink);
77
+ }
78
+
79
+ :scope tbody tr {
80
+ border-block-end: 1px solid var(--ui-table-rule-ink);
81
+ }
82
+
83
+ /* alignment is COLUMN-driven (SPEC-R3 row 9): every cell in a type:"number" column end-aligns and never
84
+ wraps — a huge mismatched string here forces the [data-part='scroll'] container instead (SPEC-R5). */
85
+ :scope [data-type='number'] {
86
+ text-align: end;
87
+ font-variant-numeric: tabular-nums;
88
+ white-space: nowrap;
89
+ }
90
+ }
@@ -0,0 +1,29 @@
1
+ import { UIElement, type ReactiveProps } from '../../dom/index.js';
2
+ import { type TableColumn, type TableRow } from './table-model.js';
3
+ declare const props: {
4
+ columns: import("../../index.js").PropConfig<TableColumn[]>;
5
+ rows: import("../../index.js").PropConfig<TableRow[]>;
6
+ label: {
7
+ reflect: true;
8
+ type: import("../../index.js").PropType<string>;
9
+ default: string;
10
+ attribute?: string | false;
11
+ };
12
+ };
13
+ export interface UITableElement extends ReactiveProps<typeof props> {
14
+ }
15
+ export declare class UITableElement extends UIElement {
16
+ #private;
17
+ static props: {
18
+ columns: import("../../index.js").PropConfig<TableColumn[]>;
19
+ rows: import("../../index.js").PropConfig<TableRow[]>;
20
+ label: {
21
+ reflect: true;
22
+ type: import("../../index.js").PropType<string>;
23
+ default: string;
24
+ attribute?: string | false;
25
+ };
26
+ };
27
+ protected connected(): void;
28
+ }
29
+ export {};