@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,87 @@
1
+ /* avatar.css — ui-avatar, the Indicator-class compact identity mark (LLD-C3, feed-family.lld.md §3;
2
+ * SPEC-R4/R5/R6/R7/R19/R20; ADR-0112 cl.3, fork F3). Two sectioned blocks:
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-avatar)` declares `--ui-avatar-*`: the widget box off the widget-box
5
+ * ramp `--md-sys-compact-{sm,md,lg}` (ADR-0041 — fork F3, no new register minted; `[size]` repoints the
6
+ * tier, `[scale]` on an ancestor re-tables the ramp for free, no avatar-local [scale] rule needed)
7
+ * and ONE neutral plane + on-surface ink pair (SPEC-R7 — no per-identity hue).
8
+ * [2] STYLES BLOCK — `@scope (ui-avatar)` consumes ONLY `--ui-avatar-*`. The host is a circle-masked
9
+ * widget box (`inline-grid`, `place-items: center`, `border-radius: 50%`); exactly one child paints
10
+ * at a time (avatar.ts) — an `<img>` (object-fit: cover), the `[data-part=initials]` text, or a
11
+ * `<ui-icon>` glyph, each sized off the box.
12
+ *
13
+ * No hue anywhere (SPEC-R7 AC1): the fallback plane/ink pair is identical for every `name` — a
14
+ * computed-style equality probe, not a visual comparison. Identity is carried by the initials/name text,
15
+ * never by color (ADR-0057).
16
+ *
17
+ * Forced colors (SPEC-R19): a background-drawn circle plane can visually merge with the page under WHCM
18
+ * — an explicit `forced-colors` block adds a system-ink border so the circle boundary stays visible (the
19
+ * badge/checkbox box-boundary precedent); initials are real text and survive untouched.
20
+ *
21
+ * No interaction states (SPEC-R4 — non-interactive, no focus/hover/active, no post-connect motion gate:
22
+ * there is nothing to transition). Standalone stylesheet — linked via the host page / component-styles
23
+ * barrel; NEVER injected from avatar.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
24
+ */
25
+
26
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-avatar) declares --ui-avatar-* ════════════════ */
27
+
28
+ :where(ui-avatar) {
29
+ /* Widget-box size (ADR-0041 fork F3) — defaults to the compact-realm `md` tier; a page override for
30
+ larger chrome (a profile header) is the token's job, not a new fleet register (F3's named trigger). */
31
+ --ui-avatar-size: var(--md-sys-compact-md);
32
+
33
+ /* ONE neutral pair (SPEC-R7) — AA-verifiable once; every `name` computes the identical plane/ink. */
34
+ --ui-avatar-plane: var(--md-sys-color-neutral-surface-high);
35
+ --ui-avatar-ink: var(--md-sys-color-neutral-on-surface);
36
+ }
37
+
38
+ /* [size] → widget-box tier (the checkbox/badge precedent: sm/lg repoint, md is the base default above). */
39
+ :where(ui-avatar[size='sm']) {
40
+ --ui-avatar-size: var(--md-sys-compact-sm);
41
+ }
42
+ :where(ui-avatar[size='lg']) {
43
+ --ui-avatar-size: var(--md-sys-compact-lg);
44
+ }
45
+
46
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-avatar) consumes ONLY --ui-avatar-* ════════════════ */
47
+
48
+ @scope (ui-avatar) {
49
+ :scope {
50
+ display: inline-grid;
51
+ place-items: center;
52
+ overflow: hidden;
53
+ border-radius: 50%; /* the circle mask (SPEC-R7) */
54
+ inline-size: var(--ui-avatar-size);
55
+ block-size: var(--ui-avatar-size);
56
+ background: var(--ui-avatar-plane);
57
+ color: var(--ui-avatar-ink);
58
+ vertical-align: middle; /* an inline-grid box sitting beside text (SPEC-R6's "beside a name" case) */
59
+ }
60
+
61
+ :scope img {
62
+ inline-size: 100%;
63
+ block-size: 100%;
64
+ object-fit: cover;
65
+ display: block;
66
+ }
67
+
68
+ :scope [data-part='initials'] {
69
+ font-size: calc(var(--ui-avatar-size) * 0.42);
70
+ font-weight: var(--md-sys-typescale-label-medium-weight);
71
+ line-height: 1; /* single-line, centers like a glyph (geometry.md's control line-height law) */
72
+ user-select: none;
73
+ text-transform: uppercase;
74
+ letter-spacing: 0.02em;
75
+ }
76
+
77
+ :scope ui-icon {
78
+ /* the content-icon register (geometry.md taxonomy) — the glyph derives from the box, not the font */
79
+ font-size: calc(var(--ui-avatar-size) * 0.6);
80
+ }
81
+
82
+ @media (forced-colors: active) {
83
+ :scope {
84
+ border: 1px solid CanvasText; /* the circle boundary stays visible under WHCM (SPEC-R19) */
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,40 @@
1
+ import { UIElement, type ReactiveProps } from '../../dom/index.js';
2
+ declare const props: {
3
+ src: import("../../index.js").PropConfig<string>;
4
+ identity: import("../../index.js").PropConfig<string>;
5
+ label: {
6
+ reflect: true;
7
+ type: import("../../index.js").PropType<string>;
8
+ default: string;
9
+ attribute?: string | false;
10
+ };
11
+ size: {
12
+ reflect: true;
13
+ type: import("../../index.js").PropType<"sm" | "md" | "lg">;
14
+ default: "sm" | "md" | "lg";
15
+ attribute?: string | false;
16
+ };
17
+ };
18
+ export interface UIAvatarElement extends ReactiveProps<typeof props> {
19
+ }
20
+ export declare class UIAvatarElement extends UIElement {
21
+ #private;
22
+ static props: {
23
+ src: import("../../index.js").PropConfig<string>;
24
+ identity: import("../../index.js").PropConfig<string>;
25
+ label: {
26
+ reflect: true;
27
+ type: import("../../index.js").PropType<string>;
28
+ default: string;
29
+ attribute?: string | false;
30
+ };
31
+ size: {
32
+ reflect: true;
33
+ type: import("../../index.js").PropType<"sm" | "md" | "lg">;
34
+ default: "sm" | "md" | "lg";
35
+ attribute?: string | false;
36
+ };
37
+ };
38
+ protected connected(): void;
39
+ }
40
+ export {};
@@ -0,0 +1,77 @@
1
+ // avatar.ts — UIAvatarElement, the Indicator-class compact identity mark (LLD-C3, feed-family.lld.md §3;
2
+ // SPEC-R4…R7; ADR-0112 cl.3). Non-interactive, non-form-associated leaf: no events, no keyboard contract,
3
+ // no focus. The fallback chain (SPEC-R5) never renders a broken-image box and never renders empty —
4
+ // exactly one of `src` image / initials / person glyph paints at a time.
5
+ //
6
+ // ONE render effect (reads src/identity/#failedSrc) walks the chain in order:
7
+ // 1. `src` non-empty AND not the failed src ⇒ an <img alt=""> (empty-alt — the Option/MenuItem sanction;
8
+ // host ARIA stays on internals) with an `error` listener that records the src into `#failedSrc`. The
9
+ // effect re-runs on that write and falls through to initials/glyph — no broken-image final state. A
10
+ // NEW `src` no longer equals `#failedSrc`, so it re-attempts (SPEC-R5's re-attempt transition falls
11
+ // out of the equality check, no extra state machine); clearing `src` falls back immediately.
12
+ // 2. else `initialsFrom(identity)` non-empty ⇒ a `<span data-part="initials">`.
13
+ // 3. else ⇒ `<ui-icon glyph="user">` — decorative by its own default; the icon control module is
14
+ // statically imported so the tag is defined before use (the sanctioned sibling-control import).
15
+ //
16
+ // A second effect owns ARIA (SPEC-R6) — the `ui-icon` contract shape verbatim: decorative by default
17
+ // (`internals.ariaHidden = 'true'`, no role — a feed avatar beside a visible name would otherwise
18
+ // duplicate it); non-empty `label` opts into `role=img` + `ariaLabel` + clears `ariaHidden`. A label-less
19
+ // avatar beside no name announces nothing — that is a contract-level author error, not a defect here
20
+ // (the descriptor states it explicitly).
21
+ import { signal } from "../../reactive/index.js";
22
+ import { UIElement, prop } from "../../dom/index.js";
23
+ import { initialsFrom } from "./avatar-initials.js";
24
+ import { UIIconElement } from "../icon/icon.js"; // sanctioned sibling-control import — self-defines ui-icon
25
+ const props = {
26
+ src: prop.string(''), // image URL; a load error falls back without ever painting a broken-image box
27
+ identity: prop.string(''), // the identity the initials derive from; NOT announced by default (SPEC-R6). Renamed from `name` (TKT-0069 item 1 ruling: `name` = the FORM name, reserved; the A2UI catalog keeps wire `name`, mapped in its bespoke factory)
28
+ label: { ...prop.string(''), reflect: true }, // the a11y escape hatch — non-empty makes the avatar itself the accessible name
29
+ size: { ...prop.enum(['sm', 'md', 'lg'], 'md'), reflect: true }, // reflected — the CSS [size] hook
30
+ };
31
+ export class UIAvatarElement extends UIElement {
32
+ static props = props;
33
+ // The most recent `src` that failed to load — an equality check, not a boolean flag, so a NEW src
34
+ // (which no longer equals this value) re-attempts for free (SPEC-R5's re-attempt transition).
35
+ #failedSrc = signal('');
36
+ connected() {
37
+ this.effect(() => {
38
+ const src = this.src;
39
+ const name = this.identity;
40
+ const failedSrc = this.#failedSrc.value;
41
+ if (src !== '' && src !== failedSrc) {
42
+ const img = document.createElement('img');
43
+ img.alt = ''; // empty-alt semantics on an interior node — host ARIA stays on internals
44
+ img.addEventListener('error', () => { this.#failedSrc.value = src; }, { once: true });
45
+ img.src = src;
46
+ this.replaceChildren(img);
47
+ return;
48
+ }
49
+ const initials = initialsFrom(name);
50
+ if (initials !== '') {
51
+ const span = document.createElement('span');
52
+ span.dataset.part = 'initials';
53
+ span.textContent = initials;
54
+ this.replaceChildren(span);
55
+ return;
56
+ }
57
+ const icon = document.createElement('ui-icon');
58
+ icon.glyph = 'user';
59
+ this.replaceChildren(icon);
60
+ });
61
+ // ARIA effect (SPEC-R6): decorative by default; `label` opts into role=img + accessible name.
62
+ this.effect(() => {
63
+ if (this.label) {
64
+ this.internals.role = 'img';
65
+ this.internals.ariaLabel = this.label;
66
+ this.internals.ariaHidden = null;
67
+ }
68
+ else {
69
+ this.internals.role = null;
70
+ this.internals.ariaLabel = null;
71
+ this.internals.ariaHidden = 'true';
72
+ }
73
+ });
74
+ }
75
+ }
76
+ if (!customElements.get('ui-avatar'))
77
+ customElements.define('ui-avatar', UIAvatarElement); // idempotent self-define
@@ -0,0 +1,175 @@
1
+ /* badge.css — ui-badge, the compact realm's first shipped non-interactive widget (report-family.lld.md
2
+ * LLD-C8 · SPEC-R12/R13/R14/R15/R16/R17 · ADR-0041 · ADR-0057 · ADR-0111 cl.5).
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-badge)` (specificity 0,0,0) declares the --ui-badge-* chain from the
5
+ * colour roles (--md-sys-color-{family}-{role}) and the compact-realm widget-box ramp
6
+ * (--md-sys-compact-lg, ADR-0041 — box = compact-realm law, NOT h/2; SPEC-R13). `[intent]` repoints the
7
+ * colour channel (fill/ink/outline) here — the SAME technique checkbox.css uses for `[size]`/
8
+ * `[disabled]`: every attribute-conditional token repoint stays inside `:where()`, never inside
9
+ * `@scope` (component-packaging.md/tokens.md — "@scope consumes ONLY --ui-{name}-*"). No `[size]`
10
+ * attribute in v1 (SPEC-R13 AC3) — the box is fixed to `--md-sys-compact-lg` regardless of content.
11
+ * [2] STYLES BLOCK — `@scope (ui-badge)` consumes ONLY --ui-badge-*. The host is an inline-flex pill:
12
+ * fixed square block-size (the compact-realm box), the compact pad law `2px + box·ratio·density`
13
+ * (never `h/2` — geometry.md's compact-realm exception), radius = box/2 (the realm's "count pill"
14
+ * case). The glyph is a real, constant `[data-part=glyph]` node; its SHAPE differs per `[intent]`
15
+ * via `clip-path` (the checkbox tick precedent, ADR-0057's non-color signifier — shape is the
16
+ * meaning channel, colour is the redundant one) — `neutral` shows no glyph at all (absence IS its
17
+ * signifier). Forced-colors maps the host to Canvas/CanvasText and the glyph to CanvasText so both
18
+ * survive WHCM (the bar-chart fill lesson: a background-drawn clip-path glyph is otherwise flattened
19
+ * to `Canvas` and vanishes).
20
+ *
21
+ * No interaction states at all (SPEC-R11 — non-interactive, no focus/hover/active, no post-connect motion
22
+ * gate: there is nothing to transition). Standalone stylesheet — linked via the host page / component-
23
+ * styles barrel; NEVER injected from badge.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
24
+ */
25
+
26
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-badge) declares --ui-badge-* ════════════════ */
27
+
28
+ :where(ui-badge) {
29
+ /* Compact-realm widget-box (ADR-0041; SPEC-R13) — fixed regardless of content/label length; [scale] on
30
+ an ancestor re-tables --md-sys-compact-lg for its subtree for free (no badge-local [scale] rule needed). */
31
+ --ui-badge-box: var(--md-sys-compact-lg); /* 18px @ default ui-md scale */
32
+
33
+ /* The compact pad law (dimensional-standard.md §2.4; geometry.md "the compact realm") — 2px + box ×
34
+ 0.375 × density. Density rides the PAD only; the box above stays density-invariant (geometry.md). */
35
+ --ui-badge-pad: calc(2px + var(--ui-badge-box) * 0.375 * var(--md-sys-density, 1));
36
+
37
+ /* Text rides the fleet font ramp (NOT the compact box) — ADR-0111 cl.5's dual-listing resolution: box =
38
+ compact-realm law, text = --md-sys-font-*. line-height:1 in the styles block centers it like a glyph. */
39
+ --ui-badge-font: var(--md-sys-font-sm);
40
+
41
+ /* Rhythm — the glyph↔label gap (density-responsive for free, geometry.md "rhythm ∝ font"). */
42
+ --ui-badge-gap: var(--md-sys-gap-sm);
43
+
44
+ /* The glyph fills half the box — a content-mark, not an inline affordance (anatomy.md's frame family). */
45
+ --ui-badge-glyph: calc(var(--ui-badge-box) * 0.5);
46
+
47
+ /* Colour channel — neutral default (the "tag" reading of the one component, ADR-0111 fork F3). */
48
+ --ui-badge-fill: var(--md-sys-color-neutral-container);
49
+ --ui-badge-ink: var(--md-sys-color-neutral-on-surface);
50
+ --ui-badge-outline: var(--md-sys-color-neutral-outline-variant);
51
+ }
52
+
53
+ /* [intent] → the colour channel repoint (SPEC-R14 AC3 defaults, build-time AA-probe-gated per intent ×
54
+ light/dark — the -on-surface-variant / -container pairing was flagged as a likely AA casualty at design
55
+ time). MEASURED (badge.browser.test.ts, both engines): 7.10–7.62:1 across all four intents × both
56
+ colour schemes — clears 4.5:1 comfortably; the predicted casualty did not materialize, no repoint owed.
57
+ A failing pairing would be a token-default repoint here, never a mechanism change (SPEC-R14/LLD-C8
58
+ failure-mode #13). */
59
+ :where(ui-badge[intent='info']) {
60
+ --ui-badge-fill: var(--md-sys-color-info-container);
61
+ --ui-badge-ink: var(--md-sys-color-info-on-surface-variant);
62
+ --ui-badge-outline: var(--md-sys-color-info-outline-variant);
63
+ }
64
+ :where(ui-badge[intent='success']) {
65
+ --ui-badge-fill: var(--md-sys-color-success-container);
66
+ --ui-badge-ink: var(--md-sys-color-success-on-surface-variant);
67
+ --ui-badge-outline: var(--md-sys-color-success-outline-variant);
68
+ }
69
+ :where(ui-badge[intent='warning']) {
70
+ --ui-badge-fill: var(--md-sys-color-warning-container);
71
+ --ui-badge-ink: var(--md-sys-color-warning-on-surface-variant);
72
+ --ui-badge-outline: var(--md-sys-color-warning-outline-variant);
73
+ }
74
+ :where(ui-badge[intent='danger']) {
75
+ --ui-badge-fill: var(--md-sys-color-danger-container);
76
+ --ui-badge-ink: var(--md-sys-color-danger-on-surface-variant);
77
+ --ui-badge-outline: var(--md-sys-color-danger-outline-variant);
78
+ }
79
+
80
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-badge) consumes ONLY --ui-badge-* ════════════════ */
81
+
82
+ @scope (ui-badge) {
83
+ :scope {
84
+ display: inline-flex;
85
+ align-items: center;
86
+ gap: var(--ui-badge-gap);
87
+ box-sizing: border-box;
88
+ block-size: var(--ui-badge-box);
89
+ min-inline-size: var(--ui-badge-box); /* empty-label floor → a filled pill/dot (SPEC-R13 AC2) */
90
+ padding-inline: var(--ui-badge-pad);
91
+ padding-block: 0; /* the frame law — block-size is the vertical lever, never block-padding */
92
+ border-radius: calc(var(--ui-badge-box) / 2); /* the pill = box/2, the realm's "count pill" case */
93
+ border: 1px solid var(--ui-badge-outline);
94
+ background: var(--ui-badge-fill);
95
+ color: var(--ui-badge-ink);
96
+ font-size: var(--ui-badge-font);
97
+ line-height: 1; /* single-line token centers like a glyph (geometry.md's control line-height law) */
98
+ white-space: nowrap; /* anonymous host-as-content label text — the SAME no-wrapper gap ui-button had
99
+ (button.css, fixed by ADR-0133) also exists here: no overflow/text-overflow, so
100
+ a label an ancestor constrains would clip/overlap with no ellipsis affordance.
101
+ NAMED DEFERRAL, not fixed here (ADR-0133 §Decision cl.3, TKT-0042): no evidence
102
+ has been submitted that a badge label actually overflows in practice (the
103
+ compact-realm box is meant for short tags/counts), and ADR-0106's Alternatives
104
+ already rejected a generic fleet-wide truncate fix — per-control adoption stays
105
+ per-control evidence. A future report against this control reopens ADR-0133's
106
+ Alternatives with its own evidence, rather than starting cold. */
107
+ }
108
+
109
+ /* Glyph visibility is DEFAULT-OFF, opted in per the four known intents below — never default-on with a
110
+ neutral/unknown opt-out. This is deliberately robust to an out-of-enum [intent] attribute value that
111
+ the property-level hardening effect (badge.ts) cannot reach: the effect snaps the PROPERTY back to
112
+ 'neutral', but a raw/reflected attribute mismatch (e.g. a plain-author typo, or a window where the
113
+ attribute briefly differs from the hardened property) must still degrade to "no glyph," never a
114
+ shapeless solid square (component-review finding — verified reachable on a literal out-of-enum
115
+ attribute in both engines before this fix). */
116
+ :scope [data-part='glyph'] {
117
+ display: none;
118
+ flex: none;
119
+ background-color: currentColor;
120
+ inline-size: var(--ui-badge-glyph);
121
+ block-size: var(--ui-badge-glyph);
122
+ }
123
+
124
+ /* Per-intent glyph SHAPES — pairwise-distinct clip-paths (SPEC-R12 AC1/AC2; the checkbox tick
125
+ precedent). Shape is the meaning channel; the [intent] colour repoints above are the REDUNDANT hue
126
+ channel (ADR-0057) — removing colour (e.g. grayscale) must never collapse two intents together.
127
+ `neutral` (and any unrecognized [intent]) inherits the base rule's `display: none` — absence IS
128
+ neutral's signifier, and it is also the safe fallback for anything not in this closed set. */
129
+ :scope[intent='success'] [data-part='glyph'] {
130
+ /* tick — the checkbox ✓ clip-path polygon, reused verbatim as the shared fleet "affirmative" mark */
131
+ display: inline-block;
132
+ clip-path: polygon(14% 44%, 0% 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
133
+ }
134
+ :scope[intent='danger'] [data-part='glyph'] {
135
+ /* cross — an X polygon, distinct from the tick and the triangle at every corner */
136
+ display: inline-block;
137
+ clip-path: polygon(
138
+ 20% 8%,
139
+ 8% 20%,
140
+ 38% 50%,
141
+ 8% 80%,
142
+ 20% 92%,
143
+ 50% 62%,
144
+ 80% 92%,
145
+ 92% 80%,
146
+ 62% 50%,
147
+ 92% 20%,
148
+ 80% 8%,
149
+ 50% 38%
150
+ );
151
+ }
152
+ :scope[intent='warning'] [data-part='glyph'] {
153
+ display: inline-block;
154
+ clip-path: polygon(50% 0, 100% 100%, 0 100%); /* triangle */
155
+ }
156
+ :scope[intent='info'] [data-part='glyph'] {
157
+ display: inline-block;
158
+ clip-path: circle(50%); /* disc */
159
+ }
160
+
161
+ /* Forced-colors (SPEC-R15) — the badge keeps its boxed identity via a system-ink border, and the glyph
162
+ paints in CanvasText: a background-drawn clip-path glyph is otherwise forced to `Canvas` and vanishes
163
+ (the bar-chart fill lesson). forced-color-adjust:none preserves these explicit system-colour mappings. */
164
+ @media (forced-colors: active) {
165
+ :scope {
166
+ background-color: Canvas;
167
+ color: CanvasText;
168
+ border-color: CanvasText;
169
+ forced-color-adjust: none;
170
+ }
171
+ :scope [data-part='glyph'] {
172
+ background-color: CanvasText;
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,36 @@
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
+ intent: {
10
+ reflect: true;
11
+ type: import("../../index.js").PropType<"neutral" | "info" | "success" | "warning" | "danger">;
12
+ default: "neutral" | "info" | "success" | "warning" | "danger";
13
+ attribute?: string | false;
14
+ };
15
+ };
16
+ export interface UIBadgeElement extends ReactiveProps<typeof props> {
17
+ }
18
+ export declare class UIBadgeElement extends UIElement {
19
+ #private;
20
+ static props: {
21
+ label: {
22
+ reflect: true;
23
+ type: import("../../index.js").PropType<string>;
24
+ default: string;
25
+ attribute?: string | false;
26
+ };
27
+ intent: {
28
+ reflect: true;
29
+ type: import("../../index.js").PropType<"neutral" | "info" | "success" | "warning" | "danger">;
30
+ default: "neutral" | "info" | "success" | "warning" | "danger";
31
+ attribute?: string | false;
32
+ };
33
+ };
34
+ protected connected(): void;
35
+ }
36
+ export {};
@@ -0,0 +1,71 @@
1
+ // badge.ts — UIBadgeElement, the compact-realm's first shipped non-interactive status/label token
2
+ // (report-family.lld.md LLD-C7; SPEC-R11…R13; ADR-0111 cl.1/2/3/4/5, fork F3; ADR-0057). Light-DOM
3
+ // display leaf — extends UIElement directly (no form participation, no focus, no keyboard contract, no
4
+ // events; SPEC-R11): a badge announces via its own real text, nothing else.
5
+ //
6
+ // Two props: `label` (plain string text) and `intent` (bindable STATUS DATA, unlike Sparkline's
7
+ // structural `variant` — ADR-0111 cl.2) — the enum snaps an unknown ATTRIBUTE string back to 'neutral'
8
+ // for free (`prop.enum`'s codec, the attribute↔value crossing in dom/props.ts). That codec guard fires
9
+ // only on the attribute-string path, though: a raw PROPERTY write (`el.intent = 'bogus'`, the shape a
10
+ // `{path}` DATA-MODEL bind actually takes at runtime — ADR-0098's validator sees literals at parse time,
11
+ // never a bound value) goes straight through `finalize()`'s generic accessor with no codec involved at
12
+ // all. SPEC-R11 AC2 requires the STORED value itself to read back 'neutral' after such a write (not just
13
+ // what a render effect happens to clean on the way out) — so the render-boundary hardening idiom
14
+ // (sparkline's `cleanSeries`, table's `cleanColumns`/`cleanRows`) is not enough on its own here; this file
15
+ // adds one self-correcting `effect` (below) that re-validates `intent` on every change and writes the
16
+ // snapped value BACK through the normal reactive setter (which also re-reflects the `[intent]` attribute
17
+ // — AC2's second half). It converges in at most one extra microtask (bad → 'neutral' → no further write)
18
+ // and uses only the public `this.effect`/`this.intent` surface — no reach into the props.ts internals.
19
+ //
20
+ // The glyph node is CONSTANT DOM — built once in connected(), never replaced. Every per-intent visual
21
+ // difference (shape, color, visibility) is CSS keyed off the reflected `[intent]` host attribute, so an
22
+ // intent change (a bound update) is a zero-DOM-churn attribute flip, never a re-render.
23
+ import { UIElement, prop } from "../../dom/index.js";
24
+ /** The closed intent set (SPEC-R11) — shared between the enum prop and the runtime hardening guard below. */
25
+ const INTENTS = ['neutral', 'info', 'success', 'warning', 'danger'];
26
+ const props = {
27
+ label: { ...prop.string(''), reflect: true },
28
+ // Bindable status data (fork F3): `prop.enum`'s codec snaps an unknown ATTRIBUTE string to 'neutral'
29
+ // for free; the property-write path is hardened separately (see the connected() effect). REFLECTS so
30
+ // `[intent]` CSS keys on JS-set/bound values (the ui-text `variant`/`size` precedent).
31
+ intent: { ...prop.enum(INTENTS, 'neutral'), reflect: true },
32
+ };
33
+ export class UIBadgeElement extends UIElement {
34
+ static props = props;
35
+ // The label part — held so the build below is genuinely once-EVER (TKT-0067): light-DOM children
36
+ // persist across a disconnect, so a reconnect reuses the SAME nodes instead of re-minting them.
37
+ #label;
38
+ connected() {
39
+ // Component-side hardening (SPEC-R11 AC2, ADR-0111 cl.2): a bound-garbage `intent` value snaps back
40
+ // to 'neutral' — re-checked on every change, written back through the normal reactive setter so BOTH
41
+ // `el.intent` and the reflected `[intent]` attribute read the sanitized value. Self-converging: the
42
+ // corrective write changes the signal from the bad value to 'neutral', which re-runs this same effect
43
+ // once more, finds 'neutral' valid, and stops (never a tight loop — `intent`'s own change is what
44
+ // re-triggers it, and the second pass is a no-op).
45
+ this.effect(() => {
46
+ if (!INTENTS.includes(this.intent))
47
+ this.intent = 'neutral';
48
+ });
49
+ // One-time DOM build (LLD-C7), now genuinely once-EVER behind an idempotent guard (TKT-0067: the
50
+ // prior code re-minted both nodes on every connect despite this comment's own "neither node is ever
51
+ // replaced" claim — true within a connection, false across reconnect; the parts-once canon): the
52
+ // glyph is decorative-only (aria-hidden; shape/visibility ride CSS off `[intent]`), the label
53
+ // carries the announced text. Only the label's textContent updates; only the host's `[intent]` flips.
54
+ if (this.#label === undefined) {
55
+ const glyph = document.createElement('span');
56
+ glyph.dataset.part = 'glyph';
57
+ glyph.setAttribute('aria-hidden', 'true');
58
+ this.#label = document.createElement('span');
59
+ this.#label.dataset.part = 'label';
60
+ this.replaceChildren(glyph, this.#label);
61
+ }
62
+ const label = this.#label;
63
+ // The one render effect: real text is the whole announcement (SPEC-R12 AC3) — no internals ARIA is
64
+ // minted at all (no role, no aria-label); the host's accessible name is its own text content.
65
+ this.effect(() => {
66
+ label.textContent = this.label;
67
+ });
68
+ }
69
+ }
70
+ if (!customElements.get('ui-badge'))
71
+ customElements.define('ui-badge', UIBadgeElement);
@@ -0,0 +1,110 @@
1
+ /* bar-chart.css — ui-bar-chart, the Display-class magnitude-comparison bar list (LLD-C6,
2
+ * chart-family.lld.md §3; SPEC-R6/R9/R10/R11/R12). Two clearly-SECTIONED blocks:
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-bar-chart)` (specificity 0,0,0) declares the `--ui-bar-chart-*` chain:
5
+ * the whole-shape floor (SPEC-R9 AC1 — a bare, unstyled chart in a flex row must still paint a
6
+ * visible, non-collapsed, proportionally-correct box), density-INVARIANT mark geometry (bar
7
+ * thickness/radius — SPEC-R12: mark geometry never rides `[scale]`), the data-bearing fill ink (a
8
+ * ≥3:1-vs-surface fleet colour role, WCAG 1.4.11 — the fill is the graphic, the track is
9
+ * decorative) and the density-RESPONSIVE row/column rhythm (rides `--md-sys-space` × `[density]` for
10
+ * free — the ADR-0103 radio-group precedent, mirroring `ui-list`'s gap).
11
+ * [2] STYLES BLOCK — `@scope (ui-bar-chart)` consumes ONLY `--ui-bar-chart-*` (∪ the shared
12
+ * `--md-sys-color-*`/`--md-sys-typescale-*` namespaces the token block itself reads to build that
13
+ * chain). `ui-bar-chart` is Display-class, TEXT-BEARING (SPEC-R12): labels/values read the
14
+ * `--md-sys-typescale-body-medium-*` row directly (no `[size]`/`[scale]` lever — a chart takes
15
+ * neither, SPEC-R12 AC2); no control height, no `padding-block` law.
16
+ *
17
+ * Row model: `fit-content(40%) 1fr auto` — labels cap at 40% and WRAP (`overflow-wrap: anywhere`, SPEC-R6
18
+ * AC3 — never starve the track); the track takes the remainder; values right-align with tabular numerals
19
+ * so magnitudes scan vertically. Each `[role='listitem']` row is a REAL `subgrid` child sharing the three
20
+ * columns (not `display: contents`, whose role-semantics engine-bug history the fleet doesn't need — LLD
21
+ * §3 note). The fill's inset/length ride two row-scoped custom properties SET IMPERATIVELY by bar-chart.ts
22
+ * (`--_bar-start`/`--_bar-length` — a non-`--ui-*` namespace, the slider/slider-multi `--value-pct`
23
+ * precedent the family-coherence token gate already exempts) — this sheet owns every paint decision, the
24
+ * behaviour file never writes a `width`.
25
+ *
26
+ * RTL is free (SPEC-R11 AC1): `inset-inline-start` + logical grid flow mirror under `dir="rtl"` — no
27
+ * physical property anywhere in this file. WHCM (SPEC-R10 AC1): the fill MUST render in a system ink (a
28
+ * background-drawn fill is otherwise forced to `Canvas` and vanishes) — an explicit `forced-colors` block
29
+ * paints it `CanvasText`, with the track kept visible + distinguishable via a `Canvas` fill + `CanvasText`
30
+ * border. Non-color signifier (SPEC-R8, ADR-0057): magnitude travels by length + the printed number; no
31
+ * intent-role is consumed anywhere in this sheet — nothing to co-carry.
32
+ *
33
+ * Standalone stylesheet — linked via the component-styles barrel (wave M1-b); NEVER injected from
34
+ * bar-chart.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
35
+ */
36
+
37
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-bar-chart) declares --ui-bar-chart-* ════════════════ */
38
+
39
+ :where(ui-bar-chart) {
40
+ --ui-bar-chart-min-inline-size: 16em; /* the whole-shape floor (SPEC-R9 AC1) — never a collapsed sliver */
41
+ --ui-bar-chart-bar-size: 0.5rem; /* bar/track thickness — density-invariant (SPEC-R12) */
42
+ --ui-bar-chart-bar-radius: 2px;
43
+ --ui-bar-chart-bar-ink: var(--md-sys-color-primary); /* the data-bearing fill — >=3:1 vs surface (WCAG 1.4.11) */
44
+ --ui-bar-chart-track-ink: var(--md-sys-color-neutral-container); /* the decorative rail */
45
+ --ui-bar-chart-text-ink: var(--md-sys-color-neutral-on-surface); /* label/value ink — the ONE role reference (role-pure) */
46
+ --ui-bar-chart-row-gap: var(--md-sys-space-sm); /* row rhythm — rides [density] for free (ADR-0103) */
47
+ --ui-bar-chart-col-gap: var(--md-sys-space-sm);
48
+ }
49
+
50
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-bar-chart) consumes ONLY --ui-bar-chart-* ════════════════ */
51
+
52
+ @scope (ui-bar-chart) {
53
+ :scope {
54
+ display: grid;
55
+ grid-template-columns: fit-content(40%) 1fr auto;
56
+ column-gap: var(--ui-bar-chart-col-gap);
57
+ row-gap: var(--ui-bar-chart-row-gap);
58
+ align-items: center;
59
+ min-inline-size: var(--ui-bar-chart-min-inline-size);
60
+ box-sizing: border-box;
61
+ /* text-bearing Display (SPEC-R12) — the type matrix is the lever, not [size]/[scale]/--md-sys-height-* */
62
+ font-size: var(--md-sys-typescale-body-medium-size);
63
+ line-height: var(--md-sys-typescale-body-medium-line-height);
64
+ color: var(--ui-bar-chart-text-ink);
65
+ }
66
+
67
+ :scope [role='listitem'] {
68
+ display: grid;
69
+ grid-template-columns: subgrid;
70
+ grid-column: 1 / -1;
71
+ align-items: center;
72
+ }
73
+
74
+ :scope [data-part='label'] {
75
+ overflow-wrap: anywhere; /* long labels wrap at the 40% cap (SPEC-R6 AC3) instead of starving the track */
76
+ }
77
+
78
+ :scope [data-part='track'] {
79
+ position: relative;
80
+ display: block;
81
+ block-size: var(--ui-bar-chart-bar-size);
82
+ border-radius: var(--ui-bar-chart-bar-radius);
83
+ background: var(--ui-bar-chart-track-ink);
84
+ }
85
+
86
+ :scope [data-part='fill'] {
87
+ position: absolute;
88
+ inset-block: 0;
89
+ inset-inline-start: calc(var(--_bar-start) * 1%); /* row-scoped, imperatively set (bar-chart.ts) */
90
+ inline-size: calc(var(--_bar-length) * 1%);
91
+ display: block;
92
+ background: var(--ui-bar-chart-bar-ink);
93
+ border-radius: inherit;
94
+ }
95
+
96
+ :scope [data-part='value'] {
97
+ text-align: end;
98
+ font-variant-numeric: tabular-nums;
99
+ }
100
+
101
+ @media (forced-colors: active) {
102
+ :scope [data-part='fill'] {
103
+ background: CanvasText; /* a background-drawn fill is otherwise forced to Canvas and vanishes */
104
+ }
105
+ :scope [data-part='track'] {
106
+ background: Canvas;
107
+ border: 1px solid CanvasText; /* keeps the track distinguishable from the fill under WHCM */
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,26 @@
1
+ import { UIElement, type ReactiveProps } from '../../dom/index.js';
2
+ declare const props: {
3
+ data: import("../../index.js").PropConfig<import("./bar-math.js").BarDatum[]>;
4
+ label: {
5
+ reflect: true;
6
+ type: import("../../index.js").PropType<string>;
7
+ default: string;
8
+ attribute?: string | false;
9
+ };
10
+ };
11
+ export interface UIBarChartElement extends ReactiveProps<typeof props> {
12
+ }
13
+ export declare class UIBarChartElement extends UIElement {
14
+ #private;
15
+ static props: {
16
+ data: import("../../index.js").PropConfig<import("./bar-math.js").BarDatum[]>;
17
+ label: {
18
+ reflect: true;
19
+ type: import("../../index.js").PropType<string>;
20
+ default: string;
21
+ attribute?: string | false;
22
+ };
23
+ };
24
+ protected connected(): void;
25
+ }
26
+ export {};