@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,498 @@
1
+ /* text-field.css — ui-text-field, the single-file component stylesheet (ADR-0003). Two clearly-SECTIONED
2
+ * blocks, mirroring button.css:
3
+ *
4
+ * [1] TOKEN BLOCK — `:where(ui-text-field)` (specificity 0,0,0) DECLARES the `--ui-text-field-*` chain from
5
+ * the colour roles (`--md-sys-color-{family}-{role}`; family = neutral) and the dimensional ramp
6
+ * (`--ui-{height,font,gap}-{size}` from `@agent-ui-kit/shared/dimensions.css`). The state is Kim's
7
+ * filled/container law (TKT-0062, superseding ADR-0014 cl.2c's border-only channel) — background,
8
+ * border, AND text-ink all repoint together across five states (default/filled/hover/focus/disabled;
9
+ * see the TOKEN block's own table comment for the exact role per state). The SOLID role ladder (NOT
10
+ * soft-alpha — alpha outlines resolve sub-3:1 against the field surface, failing WCAG 1.4.11) is
11
+ * declared as per-state `--ui-text-field-{bg,border,ink}[-filled|-hover|-focus]` tokens, each from a
12
+ * ROLE (never a `color-mix`) — border is `transparent` in every state except `:hover` (the shared
13
+ * `outline` ring is the sole focus indicator; a focus border-color step would double with the ring;
14
+ * ADR-0014 dev#1). `user-invalid`'s danger border channel is UNCHANGED/orthogonal, layered on top.
15
+ * `[size]` repoints the geometry ramp + INLINES `--ui-text-field-icon` the way button.css does (no
16
+ * shared `--ui-ind` edit).
17
+ * [2] STYLES BLOCK — `@scope (ui-text-field)` CONSUMES only `--ui-text-field-*` for its OWN tokens (the one
18
+ * fleet exception: the shared focus-ring + motion constants, read DIRECTLY so every control draws the
19
+ * identical ring / uses the identical timing). Geometry per references/geometry.md (the law — Control
20
+ * class): `block-size` off the ramp, `padding-block: 0`, value edge `h/2`, slot edge `½(h − icon)`.
21
+ * Anatomy per references/anatomy.md (ADR-0006/0012, host-as-grid): a presence-driven `:has()` grid with
22
+ * OPTIONAL `[slot=leading]` / `[slot=trailing]` adornments — `order` places `[leading? | editor | trailing?]`
23
+ * because the editor is a CONTROL-INJECTED part (`[data-part=editor]`, the centre value cell), not DOM-ordered.
24
+ * The BORDER-ON-HOST field frame (border + radius on the host) so a slotted adornment sits INSIDE the box.
25
+ * The filled/hover/focus state rules repoint the underlying `--ui-text-field-ink` TOKEN (not a
26
+ * standalone `color:` on `:scope`) so the editor part's own `color: var(--ui-text-field-ink)` and the
27
+ * placeholder's alias both re-resolve for free (a host-only `color:` override never reaches a child
28
+ * with its own explicit declaration — a real bug this exact file hit and fixed). Focus (ADR-0014
29
+ * dev#1) = the shared `outline` ring as the SOLE indicator, with the field border stepped to
30
+ * `transparent` (NOT a `--md-sys-color-focus-ring` border-color step — that doubled with the ring into a visible
31
+ * double border), BOTH on `:focus-within` (ALL focus — native text-input parity), NOT `:focus-visible`
32
+ * (the focusable element is the editor child). The transparent border holds the box geometry (no layout
33
+ * shift); the outline is also the FORCED-COLORS indicator (`border-color` is dropped by `forced-colors`;
34
+ * the outline survives via `--md-sys-color-focus-ring → Highlight`). State PAINT motion gated behind `:state(ready)`, reduced-motion
35
+ * zeroed. A forced-colors block keeps the border + ink + placeholder visible (CanvasText).
36
+ *
37
+ * Standalone stylesheet — linked via the host page / component-styles barrel (s12); NEVER injected from
38
+ * text-field.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
39
+ */
40
+
41
+ /* ════════════════ [1] TOKEN BLOCK — :where(ui-text-field) declares --ui-text-field-* ════════════════ */
42
+ /* Wave 3 (ADR-0044): type-specific styles are in [2] @scope — password masking via -webkit-text-security
43
+ (a de-facto-standard property, universally supported despite the -webkit- prefix), search clear-button
44
+ visibility via :has([data-empty]), and forced-color-adjust:none on decorative adornment glyphs so the
45
+ magnifier/symbol/reveal survive forced-colors (they are aria-hidden decoration, not the text content). */
46
+
47
+ :where(ui-text-field) {
48
+ /* Field-frame colour channel (family = neutral) — Kim's filled/container state law (TKT-0062),
49
+ SUPERSEDING the ADR-0014 cl.2c border-only channel: THREE channels now repoint per state
50
+ (background, border, text-ink), keyed off five states — default (empty, idle) / filled (has a
51
+ value, idle) / hover / focus / disabled. user-invalid is UNCHANGED — its own border-only danger
52
+ channel (below) layers on top of whichever bg/ink this table already picked; it never touches bg.
53
+ state | bg | border | ink
54
+ default | container-low | transparent | neutral
55
+ filled | container | transparent | on-surface-variant
56
+ hover | container | outline-variant | on-surface-variant
57
+ focus | container-low | transparent (ring)| on-surface
58
+ disabled | container-low | transparent | neutral-low
59
+ "filled" reads `[data-part='editor']:not([data-empty])` (the SAME emptiness signal the placeholder
60
+ ::before already keys off) — see @scope's `:has()` selector below. Declared here, consumed at the
61
+ matching pseudo-/state selector in @scope (so @scope reads only --ui-text-field-*, never a role). */
62
+ --ui-text-field-bg: var(--md-sys-color-neutral-container-low); /* default */
63
+ --ui-text-field-bg-filled: var(--md-sys-color-neutral-container);
64
+ --ui-text-field-bg-hover: var(--md-sys-color-neutral-container);
65
+ --ui-text-field-bg-focus: var(--md-sys-color-neutral-container-low);
66
+ --ui-text-field-border: transparent; /* default — the ring is the interactive indicator, not the border */
67
+ --ui-text-field-border-hover: var(--md-sys-color-neutral-outline-variant); /* :hover — the ONE visible-border state */
68
+ --ui-text-field-border-focus: transparent; /* :focus-within — TRANSPARENT: the shared outline ring is the SOLE focus indicator (a --md-sys-color-focus-ring border-color step doubled with the ring into a visible double border — a defect found on the live site; ADR-0014 dev#1). Transparent holds the box geometry (the 1px frame still occupies its track — no layout shift). */
69
+ --ui-text-field-border-invalid: var(--md-sys-color-danger); /* :state(user-invalid) — UNCHANGED, orthogonal to the table above */
70
+ --ui-text-field-border-invalid-hover: var(--md-sys-color-danger-high); /* user-invalid + :hover — UNCHANGED */
71
+ --ui-text-field-ink: var(--md-sys-color-neutral); /* default — empty, idle (== the placeholder ink, below) */
72
+ --ui-text-field-ink-filled: var(--md-sys-color-neutral-on-surface-variant);
73
+ --ui-text-field-ink-hover: var(--md-sys-color-neutral-on-surface-variant);
74
+ --ui-text-field-ink-focus: var(--md-sys-color-neutral-on-surface);
75
+ --ui-text-field-placeholder: var(--ui-text-field-ink); /* tracks the SAME default-state ink role (Kim's table has no separate placeholder row) — re-resolves live off whatever --ui-text-field-ink cascades to for this element, so the disabled repoint below reaches it for free */
76
+
77
+ /* Geometry — off the s6 dimensional ramp (default size = md). [size] repoints below. */
78
+ --ui-text-field-height: var(--md-sys-height-md);
79
+ --ui-text-field-font: var(--md-sys-font-md);
80
+ --ui-text-field-gap: var(--md-sys-gap-md); /* the one density-bearing quantity (slot↔editor column-gap) */
81
+ --ui-text-field-icon: var(--md-sys-icon-md); /* §1-SET icon per [scale]-tier (ADR-0038 / ADR-0035 conformance gap); repoints with [scale] from dimensions.css — was calc(18px * var(--md-sys-scale)) */
82
+
83
+ /* Size-INVARIANT frame quantities — declared ONCE in the base block (NOT in the per-[size] blocks below): */
84
+ --ui-text-field-radius: var(--md-sys-shape-corner-base); /* fixed --md-sys-shape-corner-base rounded-rect — the container-fleet referent an ENTRY control takes (NOT the action family's h/2 pill); overridable per-field; ADR-0015 cl.5 / #71 amendment. */
85
+ --ui-text-field-min-inline-size: 20ch; /* the host typing-width floor (entry-control frame-law leg, ADR-0021) — ch is font-relative so one ~20ch already scales with --ui-text-field-font across [size]; native <input size> parity so a bare field is hittable (width above the floor is the layout's job). */
86
+
87
+ /* Visible inline-validation message tokens (ADR-0029 A1 — extends ADR-0014): the .ui-text-field-message
88
+ node (control-managed, aria-describedby target) becomes VISIBLE when non-empty under :state(user-invalid).
89
+ Declared here (specificity 0, overridable), consumed in the @scope block. */
90
+ --ui-text-field-message-font: var(--md-sys-font-sm); /* small type — subordinate to the field's own label/value */
91
+ --ui-text-field-message-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, so the TEXT ink moves; the danger BORDER channel above (--ui-text-field-border-invalid) stays --md-sys-color-danger (border = 3:1 UI bar). ADR-0014 cl.2c · 2026-07-02 color audit item 1. */
92
+ }
93
+
94
+ /* size → dimensional ramp (size is reflected, so the attribute is present) */
95
+ :where(ui-text-field[size='sm']) {
96
+ --ui-text-field-height: var(--md-sys-height-sm);
97
+ --ui-text-field-font: var(--md-sys-font-sm);
98
+ --ui-text-field-gap: var(--md-sys-gap-sm);
99
+ --ui-text-field-icon: var(--md-sys-icon-sm); /* §1-SET icon per [scale]-tier (ADR-0038) */
100
+ }
101
+ :where(ui-text-field[size='lg']) {
102
+ --ui-text-field-height: var(--md-sys-height-lg);
103
+ --ui-text-field-font: var(--md-sys-font-lg);
104
+ --ui-text-field-gap: var(--md-sys-gap-lg);
105
+ --ui-text-field-icon: var(--md-sys-icon-lg); /* §1-SET icon per [scale]-tier (ADR-0038) */
106
+ }
107
+
108
+ /* disabled → a role-REPOINT (NOT opacity, tokens.md canon; TKT-0062's disabled row). Matches BOTH the
109
+ own/reflected [disabled] AND the effective :state(disabled) (own || form-disabled — ADR-0013/0014
110
+ cl.2b). Declared LAST so it wins the specificity-0 cascade for the BASE `--ui-text-field-{bg,border,
111
+ ink}` tokens the unconditional `:scope` rule reads. Only the base trio needs repointing — @scope's
112
+ `filled`/`hover` selectors are explicitly guarded with `:not(:is([disabled], :state(disabled)))` (a
113
+ genuinely new hazard the old border-only law never had: "filled" is a content-based `:has()` selector,
114
+ not an interaction pseudo-class, so it WOULD still match a disabled-but-non-empty field without that
115
+ guard — unlike `:hover`/`:focus-within`, which a pointer-inert, non-focusable disabled host can never
116
+ really trigger). user-invalid cannot co-occur (a disabled field does not willValidate — text-field.ts
117
+ gates it), so the danger border is left untouched. */
118
+ :where(ui-text-field:is([disabled], :state(disabled))) {
119
+ --ui-text-field-bg: var(--md-sys-color-neutral-container-low);
120
+ --ui-text-field-border: transparent;
121
+ --ui-text-field-ink: var(--md-sys-color-neutral-low);
122
+ }
123
+
124
+ /* ════════════════ [2] STYLES BLOCK — @scope (ui-text-field) consumes ONLY --ui-text-field-* ════════════════ */
125
+
126
+ @scope (ui-text-field) {
127
+ :scope {
128
+ display: inline-grid;
129
+ grid-template-columns: 1fr; /* bare editor — slotless */
130
+ align-items: center;
131
+ box-sizing: border-box;
132
+
133
+ block-size: var(--ui-text-field-height); /* the vertical lever — geometry.md (Control class) */
134
+ /* the host typing-width floor (the entry-control leg of the frame-law min-inline-size, ADR-0021 — the sibling
135
+ of button's action-control square floor) so a BARE, unsized field is hittable instead of collapsing its 1fr
136
+ editor cell to a ~0-width typing target (the box keeps only its ~30px padding+border chrome). Width ABOVE the floor stays the layout's / author's job (a flex/grid
137
+ track or an explicit inline-size grows it; --ui-text-field-min-inline-size is the per-field / theme override).
138
+ COMPLEMENTARY to the editor cell's `min-inline-size: 0` below: that lets long text scroll WITHIN the field,
139
+ this holds the box open. */
140
+ min-inline-size: var(--ui-text-field-min-inline-size);
141
+ padding-block: 0; /* NEVER block-padding */
142
+ padding-inline: calc(var(--ui-text-field-height) / 2); /* slotless value edge = h/2 */
143
+
144
+ font-size: var(--ui-text-field-font);
145
+ background: var(--ui-text-field-bg);
146
+ color: var(--ui-text-field-ink);
147
+
148
+ /* the BORDER-ON-HOST field frame (§5): border + radius on the host, so a slotted adornment sits INSIDE the
149
+ box. The radius is the fixed --md-sys-shape-corner-base rounded-rect — the SAME referent the container fleet (card /
150
+ modal) uses, because a field is an entry SURFACE kin to a container, NOT the action/keyboard family's
151
+ press-affordance h/2 pill (ADR-0015 cl.5 / the #71 amendment; geometry.md "Corner radius"). It reads the
152
+ component --ui-text-field-radius token (role-purity + per-field override). The browser clamps border-radius
153
+ to ≤ h/2, so a very dense/short field degrades gracefully to the pill — never overflows. */
154
+ border: 1px solid var(--ui-text-field-border);
155
+ border-radius: var(--ui-text-field-radius);
156
+ cursor: text;
157
+ }
158
+
159
+ /* Anatomy (references/anatomy.md — ADR-0006 extended by ADR-0012): host-as-grid with three POSITION regions —
160
+ an OPTIONAL leading [slot=leading] adornment, the EDITOR (the control-injected centre value cell, the
161
+ accessible textbox), and an OPTIONAL trailing [slot=trailing] adornment — each presence-driven via :has().
162
+ Because the editor is control-INJECTED (appended in connected(), so NOT reliably between the user-slotted
163
+ adornments in DOM order), placement is by `order`, not source order:
164
+ [leading?] order 0 · [editor] order 1 · [trailing?] order 2. */
165
+ :scope > [slot='leading'] {
166
+ order: 0;
167
+ }
168
+ :scope > [data-part='editor'] {
169
+ order: 1;
170
+ font-size: var(--ui-text-field-font);
171
+ line-height: var(--md-sys-control-line-height); /* single-line control standard — ADR-0036; explicit here because the editor re-declares font-size (the host `:scope` inherits, but the explict editor decl makes the contract visible at both sites) */
172
+ color: var(--ui-text-field-ink);
173
+ min-inline-size: 0; /* let the 1fr cell shrink so the editor scrolls (overflow) instead of widening the grid */
174
+ white-space: nowrap; /* single-line */
175
+ overflow: hidden; /* clip the overflow; the editor self-scrolls to keep the caret in view */
176
+ outline: none; /* the focus ring is on the HOST frame, never the editor child */
177
+ }
178
+ :scope > [slot='trailing'] {
179
+ order: 2;
180
+ }
181
+
182
+ /* the placeholder — a control-toggled `data-empty` attr keys generated content carrying the `data-placeholder`
183
+ prop (NOT `:empty` — a cleared contenteditable can carry a bogus trailing <br>; ADR-0014 consequences).
184
+ `pointer-events: none` + generated content ⇒ it never participates in the editable text or the caret. */
185
+ :scope > [data-part='editor'][data-empty]::before {
186
+ content: attr(data-placeholder);
187
+ color: var(--ui-text-field-placeholder);
188
+ pointer-events: none;
189
+ }
190
+
191
+ /* The adornment CELL — both slots get the SAME square, icon-sized cell (border-box), so the grid column is
192
+ icon-wide and the per-edge centering pad (½(h − icon)) holds whichever adornment sits in it. Glyph SIZING
193
+ (icon fills / caret = font) is the role axis — out of G6 scope here (the minimal field is editor + value +
194
+ validity); a leading content-icon (the active role) fills its icon-sized cell. */
195
+ :scope > [slot='leading'],
196
+ :scope > [slot='trailing'] {
197
+ inline-size: var(--ui-text-field-icon);
198
+ block-size: var(--ui-text-field-icon); /* the square slot, icon-sized — the CELL */
199
+ box-sizing: border-box;
200
+ }
201
+
202
+ /* [leading | editor] (no trailing): auto 1fr — leading slot edge ½(h−icon), trailing value edge h/2 */
203
+ :scope:has(> [slot='leading']):not(:has(> [slot='trailing'])) {
204
+ grid-template-columns: auto 1fr;
205
+ column-gap: var(--ui-text-field-gap);
206
+ padding-inline-start: calc((var(--ui-text-field-height) - var(--ui-text-field-icon)) / 2);
207
+ padding-inline-end: calc(var(--ui-text-field-height) / 2);
208
+ }
209
+
210
+ /* [editor | trailing] (no leading): 1fr auto — leading value edge h/2, trailing slot edge ½(h−icon) */
211
+ :scope:has(> [slot='trailing']):not(:has(> [slot='leading'])) {
212
+ grid-template-columns: 1fr auto;
213
+ column-gap: var(--ui-text-field-gap);
214
+ padding-inline-start: calc(var(--ui-text-field-height) / 2);
215
+ padding-inline-end: calc((var(--ui-text-field-height) - var(--ui-text-field-icon)) / 2);
216
+ }
217
+
218
+ /* [leading | editor | trailing]: auto 1fr auto — both adornment edges ½(h−icon), one gap rhythm on each side */
219
+ :scope:has(> [slot='leading']):has(> [slot='trailing']) {
220
+ grid-template-columns: auto 1fr auto;
221
+ column-gap: var(--ui-text-field-gap);
222
+ padding-inline-start: calc((var(--ui-text-field-height) - var(--ui-text-field-icon)) / 2);
223
+ padding-inline-end: calc((var(--ui-text-field-height) - var(--ui-text-field-icon)) / 2);
224
+ }
225
+
226
+ /* The filled/container state ladder (TKT-0062, Kim's ruling — supersedes the ADR-0014 cl.2c
227
+ border-only channel) — repoints `background`/`border-color`/`color` together from the per-state
228
+ tokens declared in the :where() block (the geometry law is untouched). "filled" = the editor
229
+ carries a value (`:not([data-empty])`, the SAME emptiness signal the placeholder ::before already
230
+ keys off) — a CONTENT-based selector, not an interaction pseudo-class, so unlike :hover/
231
+ :focus-within it WOULD still match a disabled-but-non-empty field.
232
+
233
+ Precedence is enforced by MUTUAL EXCLUSION (`:not()`), not source-order/specificity: `:has()`/
234
+ `:not()` combinators contribute REAL specificity in a plain (non-:where()) selector — unlike the
235
+ old border-only law's bare single-pseudo-class rules, source order alone can no longer be trusted
236
+ to pick a winner (a `:not(:is(disabled))`-guarded `:hover` was measured to OUTRANK an unguarded
237
+ `:focus-within` by specificity, so clicking to focus — which also leaves the pointer hovering —
238
+ kept the hover border instead of stepping transparent; a live regression this exact history was
239
+ caught fixing). Making each rule EXPLICITLY exclude every state that should outrank it removes the
240
+ ambiguity regardless of computed specificity: filled excludes hover/focus/disabled; hover excludes
241
+ focus/disabled; focus is checked last and excludes nothing (a disabled field is never focusable, so
242
+ no guard is needed there, matching the old law). user-invalid's border-only override still layers
243
+ on top of whichever bg/ink row above resolved, untouched. */
244
+ /* NOTE (code-reviewer HIGH finding, fixed): repointing the --ui-text-field-ink TOKEN itself here,
245
+ NOT a direct `color:` declaration on `:scope` — the editor part carries its OWN color declaration
246
+ reading that same token (text-field.css's anatomy block), which reads the token directly, not the
247
+ host's computed `color` (an element's own declared `color` always wins over inherited value, so a
248
+ host-only `color:` override never reached the visible typed text or the placeholder — proven red
249
+ in a real browser gate, both engines, before this fix). Repointing the TOKEN cascades to every
250
+ consumer (the base :scope rule's own color read, the editor's own, AND the placeholder token's
251
+ alias onto it) for free — the same mechanism the disabled `:where()` block below already used
252
+ correctly. */
253
+ :scope:not(:hover):not(:focus-within):not(:is([disabled], :state(disabled))):has(> [data-part='editor']:not([data-empty])) {
254
+ background: var(--ui-text-field-bg-filled);
255
+ --ui-text-field-ink: var(--ui-text-field-ink-filled);
256
+ }
257
+ :scope:not(:focus-within):not(:is([disabled], :state(disabled))):hover {
258
+ background: var(--ui-text-field-bg-hover);
259
+ border-color: var(--ui-text-field-border-hover);
260
+ --ui-text-field-ink: var(--ui-text-field-ink-hover);
261
+ }
262
+
263
+ /* Focus (ADR-0014 dev#1) = the shared `outline` ring as the SOLE interactive-frame indicator, with the
264
+ field border stepped to TRANSPARENT (the --ui-text-field-border-focus token), BOTH on :focus-within
265
+ (ALL focus — a text field must visibly signal where typing lands, including on a mouse click), NOT
266
+ :focus-visible. A `--md-sys-color-focus-ring` border-color step here doubled with the ring into a
267
+ visible DOUBLE border (a defect found on the live site); transparent makes the ring the lone focus
268
+ indicator AND preserves the box geometry (no layout shift — the 1px border still occupies its
269
+ track). :focus-within (not :focus-visible on the editor) is used because the focusable element is
270
+ the editor child; the ring is drawn on the host frame off the focused editor. The OUTLINE is the
271
+ load-bearing FORCED-COLORS indicator — `border-color` does NOT survive `forced-colors: active`, the
272
+ `outline` does (via --md-sys-color-focus-ring → Highlight) — so the ring is the focus indicator
273
+ there too. The outline reads the SHARED fleet tokens DIRECTLY (the one --ui-text-field-* exception,
274
+ same as button) so every control draws the identical ring; outline/outline-offset do not
275
+ participate in box geometry. Focus's bg/ink (TKT-0062) override filled/hover unconditionally — a
276
+ focused-and-filled field still shows the FOCUS row (container-low bg, on-surface ink), not filled's. */
277
+ :scope:focus-within {
278
+ background: var(--ui-text-field-bg-focus);
279
+ border-color: var(--ui-text-field-border-focus);
280
+ --ui-text-field-ink: var(--ui-text-field-ink-focus);
281
+ outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
282
+ outline-offset: var(--md-sys-state-focus-ring-offset);
283
+ }
284
+
285
+ /* user-invalid (the danger border) — appears only AFTER the first interaction; the trackUserInvalid
286
+ controller (G4) owns that timing and text-field.ts toggles the :state(user-invalid) custom state. */
287
+ :scope:state(user-invalid) {
288
+ border-color: var(--ui-text-field-border-invalid);
289
+ }
290
+ :scope:state(user-invalid):hover {
291
+ border-color: var(--ui-text-field-border-invalid-hover);
292
+ }
293
+
294
+ /* Visible inline-validation message (ADR-0029 A1 — extends ADR-0014). The control-managed
295
+ `.ui-text-field-message` node (the aria-describedby target, ADR-0014 cl.4) is `hidden` by default
296
+ (out of flow) and has no base display rule. When :state(user-invalid) is active AND the node carries
297
+ a message text (text-field.ts toggles `hidden` false only when non-empty), it becomes visible:
298
+ danger ink, small type — subordinate to the field, consistent with the danger border. Gated by
299
+ :state(user-invalid) to keep the ADR-0014 interaction-state timing (only after first blur/change).
300
+ `ui-field` (G7), when it lands, will relayout/host this node rather than re-introduce it. */
301
+ :scope:state(user-invalid) > .ui-text-field-message {
302
+ display: block;
303
+ font-size: var(--ui-text-field-message-font);
304
+ color: var(--ui-text-field-message-ink);
305
+ }
306
+
307
+ /* disabled host — pointer-inert (matches BOTH own [disabled] and the effective :state(disabled), so a
308
+ fieldset-disabled field is inert too); makes :hover / :focus-within never match, holding the muted frame
309
+ repointed in the :where() block. The editor's non-focusability + the role repoint do the rest (text-field.ts). */
310
+ :scope:is([disabled], :state(disabled)) {
311
+ pointer-events: none;
312
+ cursor: default;
313
+ }
314
+
315
+ /* Motion (ADR-0008 / interaction-states standard) — transition the state-PAINT properties ONLY
316
+ (border/background/colour), enumerated (never `transition: all`). Geometry is NEVER transitioned (a
317
+ [scale]/[size]/[density] change must SNAP — animating the ramp fights the sizing law), and the focus ring
318
+ stays instant (outline omitted). Gated behind the :state(ready) custom state, set in text-field.ts one frame
319
+ past first paint, so the upgrade/first paint snaps and only SUBSEQUENT state changes animate. */
320
+ :scope:state(ready) {
321
+ transition:
322
+ border-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
323
+ background-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
324
+ color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
325
+ }
326
+ @media (prefers-reduced-motion: reduce) {
327
+ :scope:state(ready) {
328
+ transition: none; /* accessibility (non-negotiable) — reduced motion zeroes the state transitions */
329
+ }
330
+ }
331
+
332
+ /* ── Wave 3 type-specific styles (ADR-0044) ─────────────────────────────────────────────────────── */
333
+
334
+ /* password masking (ADR-0044): -webkit-text-security is a de-facto-standard property (Chrome/Safari/Firefox)
335
+ that renders the editable text as bullet discs while the real value stays in textContent. The :state(revealed)
336
+ custom state (toggled by the reveal button in text-field.ts) flips it to none so the user can see their
337
+ password. The contenteditable editor carries the masking — the host NEVER carries a type attribute (it is the
338
+ class host, not a native input), so we scope on :scope[type='password'] (the reflected type prop attribute). */
339
+ :scope[type='password'] > [data-part='editor'] {
340
+ -webkit-text-security: disc; /* the password mask — discs hide the typed characters visually */
341
+ }
342
+ :scope[type='password']:state(revealed) > [data-part='editor'] {
343
+ -webkit-text-security: none; /* reveal toggle: show raw text */
344
+ }
345
+
346
+ /* search: the clear button (data-role='clear') is hidden when the field is empty — keyed off the
347
+ [data-empty] attribute on the editor PART (toggled by the model→surface effect in text-field.ts).
348
+ :has() reaches the editor sibling to check emptiness. When non-empty, the button is visible so the
349
+ user can clear the search field with one click. */
350
+ :scope:has(> [data-part='editor'][data-empty]) [data-role='clear'] {
351
+ display: none; /* empty field: hide the clear affordance */
352
+ }
353
+
354
+ /* adornment button baseline: no default browser button chrome, inherit the field's font/color, sized to
355
+ the slot cell (the [slot='leading']/[slot='trailing'] CSS above sizes the cell to --ui-text-field-icon;
356
+ the button fills it). Interactive affordances (clear / reveal / steppers / calendar) use data-part names;
357
+ they share the same reset so they render chrome-free and glyph-sized out of the box. */
358
+ :scope [data-part='clear-button'],
359
+ :scope [data-part='reveal-button'],
360
+ :scope [data-part='step-up'],
361
+ :scope [data-part='step-down'],
362
+ :scope [data-part='calendar-button'] {
363
+ display: inline-flex;
364
+ align-items: center;
365
+ justify-content: center;
366
+ border: none;
367
+ background: none;
368
+ padding: 0;
369
+ cursor: pointer;
370
+ font: inherit; /* inherit the field font — the glyph is font-sized */
371
+ color: inherit;
372
+ line-height: 1;
373
+ }
374
+
375
+ /* stepper container: stacks the two step buttons vertically in the trailing icon-sized cell.
376
+ Used for type=number and type=currency (no suffix — just the caret-up/caret-down steppers
377
+ stacked in the icon square). */
378
+ :scope [data-role='stepper'] {
379
+ display: inline-flex;
380
+ flex-direction: column;
381
+ align-items: center;
382
+ justify-content: center;
383
+ inline-size: var(--ui-text-field-icon);
384
+ block-size: var(--ui-text-field-icon);
385
+ }
386
+ :scope [data-part='step-up'],
387
+ :scope [data-part='step-down'] {
388
+ block-size: 50%; /* each stepper takes half the icon cell height */
389
+ inline-size: 100%;
390
+ }
391
+
392
+ /* Wave 5A (ADR-0047): numeric trailing cell — suffix span + steppers in a flex ROW.
393
+ Used for type=unit (localized label) and type=percent (%).
394
+ inline-size is auto (wider than the icon square); block-size stays icon-sized for vertical centering. */
395
+ :scope [data-role='numeric'] {
396
+ display: inline-flex;
397
+ flex-direction: row;
398
+ align-items: center;
399
+ justify-content: flex-end;
400
+ inline-size: auto;
401
+ block-size: var(--ui-text-field-icon);
402
+ gap: 0;
403
+ }
404
+ /* Override the slot's fixed icon inline-size for numeric cells (they carry suffix + steppers). */
405
+ :scope > [slot='trailing'][data-role='numeric'] {
406
+ inline-size: auto;
407
+ }
408
+ /* Numeric steppers inside a numeric cell: each is font-sized and shares the row (no 50%-block override). */
409
+ :scope [data-role='numeric'] [data-part='step-up'],
410
+ :scope [data-role='numeric'] [data-part='step-down'] {
411
+ block-size: auto;
412
+ inline-size: auto;
413
+ line-height: 1;
414
+ }
415
+
416
+ /* Wave 5B (ADR-0048): calendar popup wrapper (type=date). The `[data-part='calendar-popup']` div is
417
+ the `[popover]` element that hosts the `<ui-calendar>`. Strip UA popover defaults (padding / border /
418
+ background) so the calendar's own `[data-box]` panel owns all spacing and appearance — the wrapper
419
+ is a transparent host for the top-layer popup; it contributes no visual chrome of its own. */
420
+ :scope > [popover][data-part='calendar-popup'] {
421
+ padding: 0;
422
+ border: 0;
423
+ background: transparent;
424
+ margin: 0;
425
+ }
426
+
427
+ /* suffix span: the trailing unit label ('kg') or percent sign ('%) — sized = font (§4.6 inline
428
+ affordance law), decorative (aria-hidden). Subdued ink (the placeholder role) so it reads as
429
+ a unit annotation, not competing with the editor value. */
430
+ :scope [data-part='suffix'] {
431
+ font-size: var(--ui-text-field-font);
432
+ line-height: 1;
433
+ user-select: none;
434
+ color: var(--ui-text-field-placeholder);
435
+ }
436
+
437
+ /* leading/trailing adornment content (magnifier icon, currency symbol text): font-sized, centered.
438
+ The cell is icon-sized (--ui-text-field-icon from the slot rule above); the glyph fits within it. */
439
+ :scope [data-part='leading-adornment'],
440
+ :scope [data-part='trailing-adornment'] {
441
+ display: inline-flex;
442
+ align-items: center;
443
+ justify-content: center;
444
+ font-size: var(--ui-text-field-font); /* the glyph is sized = font (§4.6 law) */
445
+ line-height: 1;
446
+ user-select: none;
447
+ }
448
+
449
+ /* Injected Phosphor glyphs (setIcon, @agent-ui-kit/icons): magnifier · x (clear) · eye/eye-slash (reveal) ·
450
+ calendar-blank · caret-up/caret-down (steppers). 1em tracks each part's own font-size — inherited
451
+ (--ui-text-field-font) on the buttons, explicit on [data-part='leading-adornment'] above — at every
452
+ [size] tier, matching the previous inline-text glyphs' rendered size exactly. Overrides the svg's own
453
+ width/height="100%" presentational attributes (resolve.ts); [data-part='suffix'] carries NO icon (a
454
+ text label — kg / %), so it is deliberately absent from this selector list. */
455
+ :scope [data-part='clear-button'] svg,
456
+ :scope [data-part='reveal-button'] svg,
457
+ :scope [data-part='step-up'] svg,
458
+ :scope [data-part='step-down'] svg,
459
+ :scope [data-part='calendar-button'] svg,
460
+ :scope [data-part='leading-adornment'] svg {
461
+ display: block;
462
+ inline-size: 1em;
463
+ block-size: 1em;
464
+ }
465
+
466
+ /* forced-colors — keep the field border + ink + placeholder VISIBLE (they must not vanish). The focus ring
467
+ survives for free: `--md-sys-color-focus-ring` carries the token layer's `→ Highlight` WHCM mapping, so the
468
+ :focus-within outline above resolves to the system focus colour with no per-control rule (border-color is
469
+ dropped by forced-colors — which is WHY deviation #1 makes the OUTLINE the sole focus indicator: the
470
+ transparent focus border carries nothing, the ring carries it). A disabled field reads GrayText (the system
471
+ disabled colour). The decorative adornment glyphs (magnifier / currency symbol / reveal eye / steppers) are
472
+ aria-hidden and use forced-color-adjust:none so they keep their inherited ink in forced-colors mode — they
473
+ are VISUAL cues, not the accessible text content (which lives in the editor). */
474
+ @media (forced-colors: active) {
475
+ :scope {
476
+ border-color: CanvasText;
477
+ color: CanvasText;
478
+ }
479
+ :scope > [data-part='editor'][data-empty]::before {
480
+ color: CanvasText; /* the placeholder stays visible */
481
+ }
482
+ :scope:is([disabled], :state(disabled)) {
483
+ border-color: GrayText;
484
+ color: GrayText;
485
+ }
486
+ /* adornment glyphs: keep their ink (they are decorative, aria-hidden — not a text content channel) */
487
+ :scope [data-part='leading-adornment'],
488
+ :scope [data-part='trailing-adornment'],
489
+ :scope [data-part='suffix'],
490
+ :scope [data-part='clear-button'],
491
+ :scope [data-part='reveal-button'],
492
+ :scope [data-part='step-up'],
493
+ :scope [data-part='step-down'],
494
+ :scope [data-part='calendar-button'] {
495
+ forced-color-adjust: none; /* the magnifier / symbol / reveal / suffix / steppers / calendar glyph survive */
496
+ }
497
+ }
498
+ }