@kerfjs/ui 5.0.0-beta.98 → 5.0.0-beta.99

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 (407) hide show
  1. package/README.md +42 -1
  2. package/ai/application-ui-diagnostic-ids-v1.json +1 -0
  3. package/ai/component-catalog.json +487 -66
  4. package/ai/component-catalog.schema.json +1 -1
  5. package/ai/component-composition-rules.json +22 -2
  6. package/ai/component-composition.json +360 -33
  7. package/ai/component-composition.schema.json +1 -1
  8. package/ai/components/README.md +18 -17
  9. package/ai/components/anchored-surface.md +13 -0
  10. package/ai/components/application-tabs.md +1 -1
  11. package/ai/components/chip.md +9 -5
  12. package/ai/components/choice-chips.md +15 -8
  13. package/ai/components/collapsible-panel.md +3 -2
  14. package/ai/components/composer.md +83 -0
  15. package/ai/components/content-item.md +2 -2
  16. package/ai/components/corner-status.md +1 -1
  17. package/ai/components/document-baseline.md +3 -3
  18. package/ai/components/foundation.md +2 -2
  19. package/ai/components/layout.md +1 -1
  20. package/ai/components/list-action-row.md +5 -1
  21. package/ai/components/list-item.md +5 -1
  22. package/ai/components/list.md +19 -3
  23. package/ai/components/nav-stack.md +11 -2
  24. package/ai/components/pane.md +13 -4
  25. package/ai/components/popup-menu.md +4 -4
  26. package/ai/components/recipe-app-shell.md +3 -3
  27. package/ai/components/recipe-collapsible-sidebar.md +3 -3
  28. package/ai/components/recipe-compact-toolbar.md +1 -1
  29. package/ai/components/recipe-composer-form.md +3 -3
  30. package/ai/components/recipe-list-detail-dialog.md +2 -2
  31. package/ai/components/recipe-list-workspace-states.md +3 -3
  32. package/ai/components/recipe-loading-inspector.md +1 -1
  33. package/ai/components/recipe-navigation-sidebar.md +3 -3
  34. package/ai/components/recipe-navigation-stack.md +3 -3
  35. package/ai/components/recipe-workspace-header.md +3 -3
  36. package/ai/components/resize.md +2 -2
  37. package/ai/components/split-view.md +4 -2
  38. package/ai/components/sunken-panel.md +2 -2
  39. package/ai/components/surface-scaffold.md +15 -3
  40. package/ai/components/tab-bar.md +1 -0
  41. package/ai/components/tab-navigator.md +4 -0
  42. package/ai/components/toolbar-control-group.md +2 -0
  43. package/ai/components/toolbar-text.md +1 -0
  44. package/ai/components/toolbar.md +1 -0
  45. package/ai/components/workbench.md +1 -0
  46. package/ai/public-api-signatures-v1.md +487 -77
  47. package/ai/skill.md +40 -13
  48. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  49. package/analyzer/component-ownership.mjs +1 -1
  50. package/analyzer/index.mjs +41 -14
  51. package/dist/anchored-surface.d.ts +30 -2
  52. package/dist/anchored-surface.js +1 -1
  53. package/dist/app-tab.js +1 -1
  54. package/dist/browser/composer.js +4 -0
  55. package/dist/catalog-resources.d.ts +3 -2
  56. package/dist/catalog-resources.js +4 -4
  57. package/dist/catalog-resources.js.map +1 -1
  58. package/dist/catalog.d.ts +22 -5
  59. package/dist/catalog.js +50 -31
  60. package/dist/catalog.js.map +1 -1
  61. package/dist/chip.d.ts +41 -9
  62. package/dist/chip.js +1 -1
  63. package/dist/choice-chips.d.ts +40 -16
  64. package/dist/choice-chips.js +1 -1
  65. package/dist/chunk-34U4ANDJ.js +119 -0
  66. package/dist/chunk-34U4ANDJ.js.map +1 -0
  67. package/dist/{chunk-36TL6BCR.js → chunk-3GLYIIFR.js} +34 -6
  68. package/dist/chunk-3GLYIIFR.js.map +1 -0
  69. package/dist/{chunk-HWY4BSLS.js → chunk-3VXDSIBJ.js} +2 -2
  70. package/dist/{chunk-HWY4BSLS.js.map → chunk-3VXDSIBJ.js.map} +1 -1
  71. package/dist/chunk-3WYREXW4.js +120 -0
  72. package/dist/chunk-3WYREXW4.js.map +1 -0
  73. package/dist/chunk-AV7CPAPT.js +82 -0
  74. package/dist/chunk-AV7CPAPT.js.map +1 -0
  75. package/dist/{chunk-XK4E225T.js → chunk-B53PS2LE.js} +2 -2
  76. package/dist/{chunk-XK4E225T.js.map → chunk-B53PS2LE.js.map} +1 -1
  77. package/dist/{chunk-R42D2BAO.js → chunk-CA7LNX3J.js} +2 -2
  78. package/dist/{chunk-R42D2BAO.js.map → chunk-CA7LNX3J.js.map} +1 -1
  79. package/dist/{chunk-AZWE2FB5.js → chunk-CMETHPKR.js} +12 -15
  80. package/dist/chunk-CMETHPKR.js.map +1 -0
  81. package/dist/{chunk-Y43B6DS3.js → chunk-CVD5IZGB.js} +2 -2
  82. package/dist/{chunk-Y43B6DS3.js.map → chunk-CVD5IZGB.js.map} +1 -1
  83. package/dist/{chunk-6STQXRT4.js → chunk-CXZXHZTR.js} +121 -12
  84. package/dist/chunk-CXZXHZTR.js.map +1 -0
  85. package/dist/{chunk-GZE2XVKC.js → chunk-DZJ4GBSP.js} +3 -3
  86. package/dist/{chunk-GZE2XVKC.js.map → chunk-DZJ4GBSP.js.map} +1 -1
  87. package/dist/chunk-ELCMSQNJ.js +49 -0
  88. package/dist/chunk-ELCMSQNJ.js.map +1 -0
  89. package/dist/{chunk-P74VXMB6.js → chunk-EOO6PYGF.js} +7 -14
  90. package/dist/chunk-EOO6PYGF.js.map +1 -0
  91. package/dist/{chunk-PRG4CL3Q.js → chunk-FR76ZJQC.js} +21 -19
  92. package/dist/chunk-FR76ZJQC.js.map +1 -0
  93. package/dist/{chunk-QQ3TTWFH.js → chunk-G3J7JDP6.js} +1 -1
  94. package/dist/{chunk-QQ3TTWFH.js.map → chunk-G3J7JDP6.js.map} +1 -1
  95. package/dist/{chunk-IGTWXPTN.js → chunk-HGV7J4T6.js} +3 -3
  96. package/dist/{chunk-IGTWXPTN.js.map → chunk-HGV7J4T6.js.map} +1 -1
  97. package/dist/{chunk-YWQGSUF2.js → chunk-HLDRH23D.js} +3 -3
  98. package/dist/{chunk-YWQGSUF2.js.map → chunk-HLDRH23D.js.map} +1 -1
  99. package/dist/{chunk-PBT3CPPY.js → chunk-HQZYKJ56.js} +24 -4
  100. package/dist/chunk-HQZYKJ56.js.map +1 -0
  101. package/dist/{chunk-V6W56V3N.js → chunk-HZPHCSCS.js} +16 -3
  102. package/dist/chunk-HZPHCSCS.js.map +1 -0
  103. package/dist/{chunk-GJYQ27C2.js → chunk-INI4WDWC.js} +8 -1
  104. package/dist/chunk-INI4WDWC.js.map +1 -0
  105. package/dist/{chunk-U657IHFB.js → chunk-IQSRHXZR.js} +10 -4
  106. package/dist/chunk-IQSRHXZR.js.map +1 -0
  107. package/dist/{chunk-KQDU7Z7P.js → chunk-K4AT3V3J.js} +2 -1
  108. package/dist/chunk-K4AT3V3J.js.map +1 -0
  109. package/dist/{chunk-QIAPOWHH.js → chunk-KCS6NJ3O.js} +2 -2
  110. package/dist/{chunk-QIAPOWHH.js.map → chunk-KCS6NJ3O.js.map} +1 -1
  111. package/dist/{chunk-UKKRPP56.js → chunk-LOSRXZAH.js} +2 -2
  112. package/dist/{chunk-UKKRPP56.js.map → chunk-LOSRXZAH.js.map} +1 -1
  113. package/dist/{chunk-ROZLJULF.js → chunk-MF6ZUUJZ.js} +8 -3
  114. package/dist/chunk-MF6ZUUJZ.js.map +1 -0
  115. package/dist/{chunk-6IVNMC6R.js → chunk-MHMWQ7RB.js} +2 -2
  116. package/dist/{chunk-6IVNMC6R.js.map → chunk-MHMWQ7RB.js.map} +1 -1
  117. package/dist/{chunk-JYD7G4RG.js → chunk-PIVY32AE.js} +15 -5
  118. package/dist/chunk-PIVY32AE.js.map +1 -0
  119. package/dist/{chunk-527IAEWG.js → chunk-PYBBYSZ3.js} +10 -17
  120. package/dist/chunk-PYBBYSZ3.js.map +1 -0
  121. package/dist/chunk-QDSZTQXL.js +28 -0
  122. package/dist/chunk-QDSZTQXL.js.map +1 -0
  123. package/dist/{chunk-YXOMI7ES.js → chunk-QIG4IGUP.js} +2 -2
  124. package/dist/{chunk-YXOMI7ES.js.map → chunk-QIG4IGUP.js.map} +1 -1
  125. package/dist/{chunk-6BRRUPJY.js → chunk-QROYYGWU.js} +3 -3
  126. package/dist/chunk-QROYYGWU.js.map +1 -0
  127. package/dist/{chunk-FOQK4F3L.js → chunk-RHD7RKGI.js} +28 -7
  128. package/dist/chunk-RHD7RKGI.js.map +1 -0
  129. package/dist/{chunk-BBU2SPRE.js → chunk-T4WJ5VGP.js} +4 -4
  130. package/dist/{chunk-BBU2SPRE.js.map → chunk-T4WJ5VGP.js.map} +1 -1
  131. package/dist/{chunk-6NOLL5KG.js → chunk-TGLUDQP2.js} +3 -3
  132. package/dist/{chunk-6NOLL5KG.js.map → chunk-TGLUDQP2.js.map} +1 -1
  133. package/dist/{chunk-HO6FU3G7.js → chunk-TLMMFQWM.js} +4 -4
  134. package/dist/{chunk-HO6FU3G7.js.map → chunk-TLMMFQWM.js.map} +1 -1
  135. package/dist/chunk-UMKV7YVQ.js +20 -0
  136. package/dist/chunk-UMKV7YVQ.js.map +1 -0
  137. package/dist/{chunk-M2NRCFRO.js → chunk-VFYO3UGZ.js} +2 -2
  138. package/dist/{chunk-M2NRCFRO.js.map → chunk-VFYO3UGZ.js.map} +1 -1
  139. package/dist/{chunk-YOKEL75M.js → chunk-WVS6CUCT.js} +2 -2
  140. package/dist/{chunk-YOKEL75M.js.map → chunk-WVS6CUCT.js.map} +1 -1
  141. package/dist/{chunk-MN3POJFN.js → chunk-YMUIQRWK.js} +2 -2
  142. package/dist/{chunk-MN3POJFN.js.map → chunk-YMUIQRWK.js.map} +1 -1
  143. package/dist/{chunk-Y3LC7JQO.js → chunk-YOA5W5ZH.js} +2 -2
  144. package/dist/{chunk-Y3LC7JQO.js.map → chunk-YOA5W5ZH.js.map} +1 -1
  145. package/dist/{chunk-44A6LP54.js → chunk-YQ2AC4E2.js} +2 -2
  146. package/dist/{chunk-44A6LP54.js.map → chunk-YQ2AC4E2.js.map} +1 -1
  147. package/dist/chunk-ZNROKDSZ.js +54 -0
  148. package/dist/chunk-ZNROKDSZ.js.map +1 -0
  149. package/dist/collapsible-panel.d.ts +3 -2
  150. package/dist/collapsible-panel.js +4 -3
  151. package/dist/collapsible-panel.js.map +1 -1
  152. package/dist/composer.d.ts +62 -0
  153. package/dist/composer.js +5 -0
  154. package/dist/composer.js.map +1 -0
  155. package/dist/corner-status.d.ts +1 -1
  156. package/dist/corner-status.js +1 -1
  157. package/dist/css-values.d.ts +9 -1
  158. package/dist/css-values.js +1 -1
  159. package/dist/disclosure-arrow.js +1 -1
  160. package/dist/empty-state.js +1 -1
  161. package/dist/grid.js +1 -1
  162. package/dist/icon-button.js +1 -1
  163. package/dist/icon-toggle-button.js +1 -1
  164. package/dist/index.d.ts +10 -5
  165. package/dist/index.js +40 -37
  166. package/dist/list-action-row.d.ts +10 -2
  167. package/dist/list-action-row.js +1 -1
  168. package/dist/list-header.js +1 -1
  169. package/dist/list-icon-tile-B8dr97OW.d.ts +18 -0
  170. package/dist/list-item.d.ts +10 -3
  171. package/dist/list-item.js +1 -1
  172. package/dist/list.d.ts +36 -4
  173. package/dist/list.js +1 -1
  174. package/dist/loading-spinner.js +1 -1
  175. package/dist/lucide-icon.js +1 -1
  176. package/dist/{nav-stack-Cp3Y8kJ9.d.ts → nav-stack-VXTumzKa.d.ts} +48 -3
  177. package/dist/nav-stack.d.ts +3 -2
  178. package/dist/nav-stack.js +2 -1
  179. package/dist/pane.d.ts +40 -3
  180. package/dist/pane.js +1 -1
  181. package/dist/{panel-toolbar-CL9tYOkh.d.ts → panel-toolbar-vg6Ir5Dy.d.ts} +6 -1
  182. package/dist/popup-menu-register.d.ts +2 -1
  183. package/dist/popup-menu-register.js +97 -22
  184. package/dist/popup-menu-register.js.map +1 -1
  185. package/dist/popup-menu.js +1 -1
  186. package/dist/row.js +1 -1
  187. package/dist/scroll-clearance-BikHljTc.d.ts +14 -0
  188. package/dist/select.js +1 -1
  189. package/dist/skeleton.js +1 -1
  190. package/dist/spacer.js +1 -1
  191. package/dist/split-button.js +1 -1
  192. package/dist/split-view.d.ts +7 -5
  193. package/dist/split-view.js +6 -3
  194. package/dist/split-view.js.map +1 -1
  195. package/dist/state-banner.d.ts +6 -1
  196. package/dist/state-banner.js +1 -1
  197. package/dist/styles/anchored-surface.css +27 -1
  198. package/dist/styles/catalog/components/catalog-example.css +33 -0
  199. package/dist/styles/catalog/components/catalog-stage.css +7 -0
  200. package/dist/styles/chip.css +70 -0
  201. package/dist/styles/components/actions/popup-menu/popup-menu.css +14 -4
  202. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +75 -1
  203. package/dist/styles/components/actions/toolbar-text/toolbar-text.css +66 -0
  204. package/dist/styles/components/collections/list/list.css +77 -7
  205. package/dist/styles/components/collections/list-action-row/list-action-row.css +177 -22
  206. package/dist/styles/components/collections/list-item/list-item.css +158 -11
  207. package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +27 -1
  208. package/dist/styles/components/feedback/chip/chip.css +70 -0
  209. package/dist/styles/components/feedback/state-banner/state-banner.css +24 -13
  210. package/dist/styles/components/forms/composer/composer.css +100 -0
  211. package/dist/styles/components/forms/token-search-field/token-search-field.css +13 -2
  212. package/dist/styles/components/layout/pane/pane.css +81 -2
  213. package/dist/styles/components/navigation/nav-stack/nav-stack.css +84 -0
  214. package/dist/styles/components/navigation/tab-bar/tab-bar.css +15 -4
  215. package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +79 -4
  216. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +195 -0
  217. package/dist/styles/composer.css +100 -0
  218. package/dist/styles/foundation.css +40 -0
  219. package/dist/styles/list-action-row.css +177 -22
  220. package/dist/styles/list-item.css +158 -11
  221. package/dist/styles/list.css +77 -7
  222. package/dist/styles/nav-stack.css +84 -0
  223. package/dist/styles/pane.css +81 -2
  224. package/dist/styles/popup-menu.css +14 -4
  225. package/dist/styles/state-banner.css +24 -13
  226. package/dist/styles/styles.css +1 -0
  227. package/dist/styles/surface-scaffold.css +195 -0
  228. package/dist/styles/tab-bar.css +15 -4
  229. package/dist/styles/tab-navigator.css +79 -4
  230. package/dist/styles/token-search-field.css +13 -2
  231. package/dist/styles/toolbar-control-group.css +75 -1
  232. package/dist/styles/toolbar-text.css +66 -0
  233. package/dist/surface-scaffold-register.d.ts +6 -1
  234. package/dist/surface-scaffold-register.js +89 -3
  235. package/dist/surface-scaffold-register.js.map +1 -1
  236. package/dist/surface-scaffold.d.ts +66 -7
  237. package/dist/surface-scaffold.js +1 -1
  238. package/dist/tab-navigator.d.ts +23 -2
  239. package/dist/tab-navigator.js +2 -0
  240. package/dist/tab-navigator.js.map +1 -1
  241. package/dist/token-search-field.d.ts +1 -1
  242. package/dist/token-search-field.js +1 -1
  243. package/dist/{token-search-model-DTcTXWMS.d.ts → token-search-model-aYR0YAei.d.ts} +32 -2
  244. package/dist/token-search-model.d.ts +1 -1
  245. package/dist/toolbar-control-group.js +1 -1
  246. package/dist/toolbar-text.js +1 -1
  247. package/dist/toolbar.d.ts +1 -0
  248. package/dist/value-table.js +1 -1
  249. package/dist/wire-catalog.d.ts +3 -2
  250. package/dist/wire-catalog.js +2 -2
  251. package/dist/wire-composers.d.ts +16 -0
  252. package/dist/wire-composers.js +3 -0
  253. package/dist/wire-composers.js.map +1 -0
  254. package/dist/wire-list-focus-scroll.d.ts +16 -0
  255. package/dist/wire-list-focus-scroll.js +3 -0
  256. package/dist/wire-list-focus-scroll.js.map +1 -0
  257. package/dist/wire-nav-stack.js +135 -0
  258. package/dist/wire-nav-stack.js.map +1 -1
  259. package/dist/wire-scroll-dividers.d.ts +9 -0
  260. package/dist/wire-scroll-dividers.js +1 -1
  261. package/dist/wire-tab-bars.js +1 -1
  262. package/dist/wire-tab-navigator.d.ts +3 -1
  263. package/dist/wire-tab-navigator.js +133 -1
  264. package/dist/wire-tab-navigator.js.map +1 -1
  265. package/dist/wire-token-search-fields.d.ts +1 -1
  266. package/dist/wire-token-search-fields.js +1 -1
  267. package/dist/workbench.d.ts +8 -3
  268. package/dist/workbench.js +2 -1
  269. package/docs/accessibility.md +132 -3
  270. package/docs/anchored-surface.md +46 -4
  271. package/docs/app-layouts.md +23 -2
  272. package/docs/apple-hig.md +1 -0
  273. package/docs/catalog.md +54 -8
  274. package/docs/collapsible-panel.md +9 -6
  275. package/docs/component-contract.md +41 -12
  276. package/docs/component-selection.md +143 -19
  277. package/docs/composer.md +143 -0
  278. package/docs/design/templates/choice-chips/multiple-dark.svg +1 -0
  279. package/docs/design/templates/choice-chips/multiple.svg +1 -0
  280. package/docs/design/templates/choice-chips-dark.svg +1 -1
  281. package/docs/design/templates/choice-chips.svg +1 -1
  282. package/docs/design/templates/composer/chat-dark.svg +1 -0
  283. package/docs/design/templates/composer/chat.svg +1 -0
  284. package/docs/design/templates/composer/prompt-dark.svg +1 -0
  285. package/docs/design/templates/composer/prompt.svg +1 -0
  286. package/docs/design/templates/composer-dark.svg +1 -0
  287. package/docs/design/templates/composer.svg +1 -0
  288. package/docs/design/templates/empty-state/filtered-dark.svg +1 -1
  289. package/docs/design/templates/empty-state/filtered.svg +1 -1
  290. package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
  291. package/docs/design/templates/empty-state/with-action.svg +1 -1
  292. package/docs/design/templates/empty-state-dark.svg +1 -1
  293. package/docs/design/templates/empty-state.svg +1 -1
  294. package/docs/design/templates/list/compact-dark.svg +1 -1
  295. package/docs/design/templates/list/compact.svg +1 -1
  296. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  297. package/docs/design/templates/list-action-row/default.svg +1 -1
  298. package/docs/design/templates/list-action-row/icon-tile-dark.svg +1 -0
  299. package/docs/design/templates/list-action-row/icon-tile.svg +1 -0
  300. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  301. package/docs/design/templates/list-action-row/selected.svg +1 -1
  302. package/docs/design/templates/list-action-row/trailing-menu-dark.svg +1 -1
  303. package/docs/design/templates/list-action-row/trailing-menu.svg +1 -1
  304. package/docs/design/templates/list-action-row-dark.svg +1 -1
  305. package/docs/design/templates/list-action-row.svg +1 -1
  306. package/docs/design/templates/list-dark.svg +1 -1
  307. package/docs/design/templates/list-header/size-l-dark.svg +1 -1
  308. package/docs/design/templates/list-header/size-l.svg +1 -1
  309. package/docs/design/templates/list-header/size-m-dark.svg +1 -1
  310. package/docs/design/templates/list-header/size-m.svg +1 -1
  311. package/docs/design/templates/list-header/size-xl-dark.svg +1 -1
  312. package/docs/design/templates/list-header/size-xl.svg +1 -1
  313. package/docs/design/templates/list-header-dark.svg +1 -1
  314. package/docs/design/templates/list-header.svg +1 -1
  315. package/docs/design/templates/list-item/icon-tile-dark.svg +1 -0
  316. package/docs/design/templates/list-item/icon-tile.svg +1 -0
  317. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  318. package/docs/design/templates/list-item/selected.svg +1 -1
  319. package/docs/design/templates/list-item-dark.svg +1 -1
  320. package/docs/design/templates/list-item.svg +1 -1
  321. package/docs/design/templates/list.svg +1 -1
  322. package/docs/design/templates/segmented-control-dark.svg +1 -1
  323. package/docs/design/templates/segmented-control.svg +1 -1
  324. package/docs/design/templates/skeleton-dark.svg +1 -1
  325. package/docs/design/templates/skeleton.svg +1 -1
  326. package/docs/design/templates/state-banner/danger-dark.svg +1 -1
  327. package/docs/design/templates/state-banner/danger.svg +1 -1
  328. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
  329. package/docs/design/templates/state-banner/info-badge.svg +1 -1
  330. package/docs/design/templates/state-banner/info-dark.svg +1 -1
  331. package/docs/design/templates/state-banner/info.svg +1 -1
  332. package/docs/design/templates/state-banner/strip-dark.svg +1 -1
  333. package/docs/design/templates/state-banner/strip.svg +1 -1
  334. package/docs/design/templates/state-banner/success-dark.svg +1 -1
  335. package/docs/design/templates/state-banner/success.svg +1 -1
  336. package/docs/design/templates/state-banner/warning-dark.svg +1 -1
  337. package/docs/design/templates/state-banner/warning.svg +1 -1
  338. package/docs/design/templates/state-banner-dark.svg +1 -1
  339. package/docs/design/templates/state-banner.svg +1 -1
  340. package/docs/design/templates/tab-bar-dark.svg +1 -1
  341. package/docs/design/templates/tab-bar.svg +1 -1
  342. package/docs/design/templates/token-search-field-dark.svg +1 -1
  343. package/docs/design/templates/token-search-field.svg +1 -1
  344. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  345. package/docs/design/templates/toolbar-control-group.svg +1 -1
  346. package/docs/design/templates/toolbar-dark.svg +1 -1
  347. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  348. package/docs/design/templates/toolbar-text/default.svg +1 -1
  349. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  350. package/docs/design/templates/toolbar-text/large.svg +1 -1
  351. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  352. package/docs/design/templates/toolbar-text.svg +1 -1
  353. package/docs/design/templates/toolbar.svg +1 -1
  354. package/docs/design/templates/value-table/details-dark.svg +1 -1
  355. package/docs/design/templates/value-table/details.svg +1 -1
  356. package/docs/design/templates/value-table-dark.svg +1 -1
  357. package/docs/design/templates/value-table.svg +1 -1
  358. package/docs/design-philosophy.md +9 -1
  359. package/docs/examples/command-palette-adapter.tsx +3 -3
  360. package/docs/layout.md +147 -14
  361. package/docs/nav-stack.md +112 -2
  362. package/docs/recipes.md +9 -9
  363. package/docs/split-view.md +3 -3
  364. package/docs/sunken-panel.md +2 -2
  365. package/docs/surface-scaffold.md +138 -7
  366. package/docs/tab-navigator.md +38 -1
  367. package/docs/type-contracts.md +16 -16
  368. package/docs/ui-analyzer.md +3 -1
  369. package/docs/ui-doctor.md +71 -0
  370. package/docs/ux-demo.md +6 -6
  371. package/docs/workbench.md +10 -6
  372. package/docs/writing.md +152 -0
  373. package/doctor/copy-capitalization-checks.mjs +591 -0
  374. package/doctor/index.mjs +17 -8
  375. package/llms.txt +105 -11
  376. package/package.json +15 -1
  377. package/ux-demo/recipes/app-shell.tsx +11 -11
  378. package/ux-demo/recipes/collapsible-sidebar.tsx +12 -11
  379. package/ux-demo/recipes/compact-toolbar.tsx +10 -10
  380. package/ux-demo/recipes/composer-form.tsx +8 -8
  381. package/ux-demo/recipes/list-detail-dialog.tsx +13 -13
  382. package/ux-demo/recipes/list-workspace-states.tsx +13 -13
  383. package/ux-demo/recipes/loading-inspector.tsx +10 -10
  384. package/ux-demo/recipes/navigation-sidebar.tsx +7 -7
  385. package/ux-demo/recipes/navigation-stack.tsx +5 -5
  386. package/ux-demo/recipes/workspace-header.tsx +5 -5
  387. package/dist/chunk-36TL6BCR.js.map +0 -1
  388. package/dist/chunk-4LOK4634.js +0 -21
  389. package/dist/chunk-4LOK4634.js.map +0 -1
  390. package/dist/chunk-527IAEWG.js.map +0 -1
  391. package/dist/chunk-6BRRUPJY.js.map +0 -1
  392. package/dist/chunk-6STQXRT4.js.map +0 -1
  393. package/dist/chunk-76PWEN5Y.js +0 -64
  394. package/dist/chunk-76PWEN5Y.js.map +0 -1
  395. package/dist/chunk-AZWE2FB5.js.map +0 -1
  396. package/dist/chunk-DQDTZE4W.js +0 -70
  397. package/dist/chunk-DQDTZE4W.js.map +0 -1
  398. package/dist/chunk-FOQK4F3L.js.map +0 -1
  399. package/dist/chunk-GJYQ27C2.js.map +0 -1
  400. package/dist/chunk-JYD7G4RG.js.map +0 -1
  401. package/dist/chunk-KQDU7Z7P.js.map +0 -1
  402. package/dist/chunk-P74VXMB6.js.map +0 -1
  403. package/dist/chunk-PBT3CPPY.js.map +0 -1
  404. package/dist/chunk-PRG4CL3Q.js.map +0 -1
  405. package/dist/chunk-ROZLJULF.js.map +0 -1
  406. package/dist/chunk-U657IHFB.js.map +0 -1
  407. package/dist/chunk-V6W56V3N.js.map +0 -1
@@ -96,6 +96,185 @@
96
96
  .kui-dialog-surface[data-footer-inset="comfortable"] > wa-dialog::part(footer) {
97
97
  padding: var(--kui-wa-container-inset, 1rem);
98
98
  }
99
+ /* Fixed rows above one filling row: the leading children keep their natural
100
+ height and the last unslotted child (the light-DOM children are the body
101
+ slot's flex items) takes the rest and owns its scrolling. */
102
+ .kui-dialog-surface[data-body-layout="fill-last"] > wa-dialog::part(body) {
103
+ display: flex;
104
+ flex-direction: column;
105
+ overflow: hidden;
106
+ }
107
+ .kui-dialog-surface[data-body-layout="fill-last"] > wa-dialog > :not([slot]) {
108
+ flex: none;
109
+ }
110
+ .kui-dialog-surface[data-body-layout="fill-last"]
111
+ > wa-dialog
112
+ > :nth-last-child(1 of :not([slot])) {
113
+ flex: 1 1 0;
114
+ min-block-size: 0;
115
+ }
116
+ /* A floating footer lies over the body's block end, inset from the panel's
117
+ inline and block-end edges; the top-layer panel is its positioning
118
+ context. The register publishes the footer's measured block size on the
119
+ panel, and the body reserves it (plus the offset) after its own inset, so
120
+ flowing content scrolls beneath the footer and a filling child's scrollport
121
+ ends above it: the last content is never hidden. */
122
+ .kui-dialog-surface[data-footer-presentation="floating"] > wa-dialog {
123
+ --_kui-dialog-surface-footer-resolved-offset: max(
124
+ 0px,
125
+ var(
126
+ --_kui-dialog-surface-footer-offset,
127
+ var(--kui-wa-container-inset, 1rem)
128
+ )
129
+ );
130
+ }
131
+ .kui-dialog-surface[data-footer-presentation="floating"]
132
+ > wa-dialog::part(footer) {
133
+ position: absolute;
134
+ inset-inline: var(--_kui-dialog-surface-footer-resolved-offset);
135
+ inset-block-end: var(--_kui-dialog-surface-footer-resolved-offset);
136
+ z-index: 1;
137
+ background: transparent;
138
+ }
139
+ .kui-dialog-surface[data-footer-presentation="floating"]
140
+ > wa-dialog::part(body) {
141
+ --_kui-dialog-surface-footer-clearance: calc(
142
+ var(--_kui-dialog-surface-footer-block-size, 0px) +
143
+ var(--_kui-dialog-surface-footer-resolved-offset)
144
+ );
145
+ scroll-padding-block-end: var(--_kui-dialog-surface-footer-clearance);
146
+ }
147
+ .kui-dialog-surface[data-footer-presentation="floating"][data-body-inset="none"]
148
+ > wa-dialog::part(body) {
149
+ padding-block-end: var(--_kui-dialog-surface-footer-clearance);
150
+ }
151
+ .kui-dialog-surface[data-footer-presentation="floating"][data-body-inset="compact"]
152
+ > wa-dialog::part(body) {
153
+ padding-block-end: calc(
154
+ var(--kui-wa-surface-inset, 0.5rem) +
155
+ var(--_kui-dialog-surface-footer-clearance)
156
+ );
157
+ }
158
+ .kui-dialog-surface[data-footer-presentation="floating"][data-body-inset="comfortable"]
159
+ > wa-dialog::part(body) {
160
+ padding-block-end: calc(
161
+ var(--kui-wa-container-inset, 1rem) +
162
+ var(--_kui-dialog-surface-footer-clearance)
163
+ );
164
+ }
165
+ /* Scroll clearance. A dialog body starts a new scroll context, so it clears
166
+ any `--kui-scroll-clearance-*` a page region published around the dialog.
167
+ With a floating footer over a filling child that opts into block-end
168
+ `scrollClearance` (a transcript List or Pane), the body stops reserving the
169
+ footer below that child: the child reaches the panel's block end, the body
170
+ publishes the footer's clearance for it, and the child adds it inside its
171
+ own scroll area, so content scrolls visibly beneath the transparent footer
172
+ while its last item still comes to rest above it. */
173
+ .kui-dialog-surface > wa-dialog::part(body) {
174
+ --kui-scroll-clearance-block-start: 0px;
175
+ --kui-scroll-clearance-block-end: 0px;
176
+ }
177
+ .kui-dialog-surface[data-footer-presentation="floating"]:is(
178
+ [data-body-layout="fill"],
179
+ [data-body-layout="fill-last"]
180
+ )
181
+ > wa-dialog:has(
182
+ > :nth-last-child(1 of :not([slot])):is(
183
+ [data-scroll-clearance="block-end"],
184
+ [data-scroll-clearance="both"]
185
+ )
186
+ )::part(body) {
187
+ --kui-scroll-clearance-block-end: var(--_kui-dialog-surface-footer-clearance);
188
+ padding-block-end: 0;
189
+ }
190
+ /* A floating header lies over the body's block-start edge on the
191
+ translucent chrome material (opaque under reduced transparency, increased
192
+ contrast, or without backdrop-filter; a system surface in forced colors).
193
+ The register publishes the header's measured block size on the panel; the
194
+ header gives that height back as a negative block-end margin, so the body
195
+ starts at the panel's block-start edge beneath it, and the body reserves it
196
+ again before its own inset (with a matching scroll-padding): flowing
197
+ content rests below the title bar and scrolls beneath it. Unmeasured, the
198
+ size resolves to 0px and the header stays docked. */
199
+ .kui-dialog-surface[data-header-presentation="floating"]
200
+ > wa-dialog::part(header) {
201
+ position: relative;
202
+ z-index: 1;
203
+ margin-block-end: calc(
204
+ -1 * var(--_kui-dialog-surface-header-block-size, 0px)
205
+ );
206
+ -webkit-backdrop-filter: var(--kui-chrome-material-filter);
207
+ backdrop-filter: var(--kui-chrome-material-filter);
208
+ background: var(--kui-chrome-material-raised);
209
+ box-shadow: inset 0 -1px
210
+ var(--_kui-dialog-surface-header-divider, transparent);
211
+ }
212
+ /* The floating header's divider is scroll state, as on a Pane's overlay
213
+ header: the register publishes `--_kui-dialog-surface-header-divider` on
214
+ the panel (as this color) only while the body, or a filling child that
215
+ reaches beneath the header, is scrolled away from its top. The line is an
216
+ inset shadow, so showing it never moves the header or the content. */
217
+ .kui-dialog-surface[data-header-presentation="floating"] > wa-dialog {
218
+ --_kui-dialog-surface-header-divider-color: var(
219
+ --kui-color-neutral-border-normal
220
+ );
221
+ }
222
+ .kui-dialog-surface[data-header-presentation="floating"]
223
+ > wa-dialog::part(body) {
224
+ --_kui-dialog-surface-header-clearance: var(
225
+ --_kui-dialog-surface-header-block-size,
226
+ 0px
227
+ );
228
+ scroll-padding-block-start: var(--_kui-dialog-surface-header-clearance);
229
+ }
230
+ .kui-dialog-surface[data-header-presentation="floating"][data-body-inset="none"]
231
+ > wa-dialog::part(body) {
232
+ padding-block-start: var(--_kui-dialog-surface-header-clearance);
233
+ }
234
+ .kui-dialog-surface[data-header-presentation="floating"][data-body-inset="compact"]
235
+ > wa-dialog::part(body) {
236
+ padding-block-start: calc(
237
+ var(--kui-wa-surface-inset, 0.5rem) +
238
+ var(--_kui-dialog-surface-header-clearance)
239
+ );
240
+ }
241
+ .kui-dialog-surface[data-header-presentation="floating"][data-body-inset="comfortable"]
242
+ > wa-dialog::part(body) {
243
+ padding-block-start: calc(
244
+ var(--kui-wa-container-inset, 1rem) +
245
+ var(--_kui-dialog-surface-header-clearance)
246
+ );
247
+ }
248
+ /* A filling child that is the body's only child and opts into block-start
249
+ `scrollClearance` reaches the panel's block-start edge beneath the floating
250
+ header: the body publishes the header's clearance for it, and the child
251
+ adds it inside its own scroll area, so content scrolls visibly beneath the
252
+ title bar while its first item still rests below it. Leading children of
253
+ `fill-last` keep the body's reservation instead. */
254
+ .kui-dialog-surface[data-header-presentation="floating"]:is(
255
+ [data-body-layout="fill"],
256
+ [data-body-layout="fill-last"]
257
+ )
258
+ > wa-dialog:has(
259
+ > :nth-child(1 of :not([slot])):nth-last-child(1 of :not([slot])):is(
260
+ [data-scroll-clearance="block-start"],
261
+ [data-scroll-clearance="both"]
262
+ )
263
+ )::part(body) {
264
+ --kui-scroll-clearance-block-start: var(
265
+ --_kui-dialog-surface-header-clearance
266
+ );
267
+ padding-block-start: 0;
268
+ }
269
+ @media (forced-colors: active) {
270
+ .kui-dialog-surface[data-header-presentation="floating"]
271
+ > wa-dialog::part(header) {
272
+ -webkit-backdrop-filter: none;
273
+ backdrop-filter: none;
274
+ background: Canvas;
275
+ border-block-end: 1px solid CanvasText;
276
+ }
277
+ }
99
278
  .kui-dialog-surface[data-presentation="side-sheet"] > wa-dialog::part(dialog) {
100
279
  width: min(30rem, 100vw);
101
280
  max-width: 100vw;
@@ -199,6 +378,22 @@
199
378
  overflow: hidden;
200
379
  grid-template-rows: minmax(0, 1fr);
201
380
  }
381
+ /* Fixed rows above one filling last child, which owns its scrolling. */
382
+ .kui-popover-dialog-surface[data-body-layout="fill-last"]:is(
383
+ :popover-open,
384
+ [data-presentation="embedded"]
385
+ ) {
386
+ display: flex;
387
+ flex-direction: column;
388
+ overflow: hidden;
389
+ }
390
+ .kui-popover-dialog-surface[data-body-layout="fill-last"] > * {
391
+ flex: none;
392
+ }
393
+ .kui-popover-dialog-surface[data-body-layout="fill-last"] > :last-child {
394
+ flex: 1 1 0;
395
+ min-block-size: 0;
396
+ }
202
397
  /* In flow for a catalog or inline use: the same surface without the top
203
398
  layer, never wider than its container. */
204
399
  .kui-popover-dialog-surface[data-presentation="embedded"] {
@@ -48,10 +48,21 @@
48
48
  .kui-tab-bar__tabs[data-snap-tabs="true"] {
49
49
  scroll-snap-type: x mandatory;
50
50
  scroll-padding-inline-start: var(--kui-tab-bar-snap-inset, 0px);
51
- padding-inline-end: calc(
52
- var(--kui-tab-bar-strip-padding, 1px) +
53
- var(--kui-tab-bar-snap-end-extra, 0px)
54
- );
51
+ }
52
+ /* The snap end room is scrollable content after the last tab, not padding on
53
+ the strip: padding is part of the strip's box, which a flex item cannot
54
+ shrink below, so a room measured at one width would hold the strip open
55
+ past its allocation when it narrows and push the trailing and end zones out
56
+ of the bar. As content it lengthens only the scroll range, and the strip
57
+ still shrinks to its allocation. The start margin gives back the strip gap,
58
+ so an empty room adds no width. */
59
+ .kui-tab-bar__tabs[data-snap-tabs="true"]::after {
60
+ content: "";
61
+ inline-size: var(--kui-tab-bar-snap-end-extra, 0px);
62
+ margin-inline-start: calc(-1 * var(--kui-tab-bar-strip-gap, 0px));
63
+ pointer-events: none;
64
+ align-self: stretch;
65
+ flex: none;
55
66
  }
56
67
  .kui-tab-bar[data-icon-only-at] .kui-tab-bar__tabs {
57
68
  container: kui-tab-bar-tabs / inline-size;
@@ -69,10 +69,12 @@
69
69
  ):only-child
70
70
  )
71
71
  ) {
72
- padding-block-start: var(--kui-edge-inset-block-start);
72
+ padding-block: var(--kui-edge-inset-block-start)
73
+ var(--kui-edge-inset-block-end);
73
74
  padding-inline: var(--kui-edge-inset-inline-start)
74
75
  var(--kui-edge-inset-inline-end);
75
- scroll-padding-block-start: var(--kui-edge-inset-block-start);
76
+ scroll-padding-block: var(--kui-edge-inset-block-start)
77
+ var(--kui-edge-inset-block-end);
76
78
  }
77
79
 
78
80
  /* A plain scene adds one outer gutter; a sole Pane or layout keeps ownership
@@ -91,13 +93,15 @@
91
93
  padding-block-start: calc(
92
94
  var(--kui-edge-inset-block-start) + var(--kui-space-xs)
93
95
  );
94
- padding-block-end: var(--kui-space-xs);
96
+ padding-block-end: calc(
97
+ var(--kui-edge-inset-block-end) + var(--kui-space-xs)
98
+ );
95
99
  padding-inline: calc(var(--kui-edge-inset-inline-start) + var(--kui-space-xs))
96
100
  calc(var(--kui-edge-inset-inline-end) + var(--kui-space-xs));
97
101
  scroll-padding-block: calc(
98
102
  var(--kui-edge-inset-block-start) + var(--kui-space-xs)
99
103
  )
100
- var(--kui-space-xs);
104
+ calc(var(--kui-edge-inset-block-end) + var(--kui-space-xs));
101
105
  }
102
106
 
103
107
  .kui-tab-scaffold__scene
@@ -164,6 +168,51 @@
164
168
  --_kui-tab-scaffold-bar-divider: var(--kui-color-neutral-border-normal);
165
169
  }
166
170
 
171
+ /* `barPresentation="overlay"`: the bar paints the translucent chrome
172
+ material. Once `wireTabNavigator` has measured the live bar
173
+ (`data-bar-overlay="measured"`, with its border-box block size, bottom safe
174
+ area and 1px top edge included, published on this root), the bar lies over
175
+ the scenes' bottom edge and the scenes hand that size to their content as
176
+ the block-end edge context (outranking the docked 0px above): a plain scene
177
+ pads and scroll-pads by it, and a sole Pane or layout (a NavStack, whose own
178
+ bottom toolbar then sits just above the bar) clears it itself. Content
179
+ scrolls visibly beneath the bar while its last item rests clear of it.
180
+ Unwired, the bar stays docked. */
181
+ .kui-tab-scaffold[data-bar-presentation="overlay"] > .kui-tab-scaffold__bar {
182
+ -webkit-backdrop-filter: var(--kui-chrome-material-filter);
183
+ backdrop-filter: var(--kui-chrome-material-filter);
184
+ background: var(--kui-chrome-material);
185
+ }
186
+
187
+ .kui-tab-scaffold[data-bar-presentation="overlay"][data-bar-overlay="measured"] {
188
+ position: relative;
189
+ }
190
+
191
+ .kui-tab-scaffold[data-bar-presentation="overlay"][data-bar-overlay="measured"]
192
+ > .kui-tab-scaffold__bar {
193
+ position: absolute;
194
+ z-index: 2;
195
+ inset-block-end: 0;
196
+ /* Like the NavStack overlay bars, it ends where a classic scrollbar's
197
+ track begins, so the thumb and buttons stay visible and usable. */
198
+ inset-inline: 0 var(--_kui-tab-scaffold-bar-scrollbar-inline-size, 0px);
199
+ }
200
+
201
+ .kui-tab-scaffold[data-bar-presentation="overlay"][data-bar-overlay="measured"]
202
+ > .kui-tab-scaffold__scenes {
203
+ --kui-edge-inset-block-end: var(--_kui-tab-scaffold-bar-block-size, 0px);
204
+ }
205
+
206
+ @media (forced-colors: active) {
207
+ /* Its always-present 1px top border becomes the visible edge. */
208
+ .kui-tab-scaffold[data-bar-presentation="overlay"] > .kui-tab-scaffold__bar {
209
+ -webkit-backdrop-filter: none;
210
+ backdrop-filter: none;
211
+ background: Canvas;
212
+ border-block-start-color: CanvasText;
213
+ }
214
+ }
215
+
167
216
  .kui-tab-scaffold__tab {
168
217
  display: flex;
169
218
  flex: 1 1 0;
@@ -185,6 +234,32 @@
185
234
  color: var(--kui-color-brand-on-quiet);
186
235
  }
187
236
 
237
+ /* Over the material, content of any color passes beneath the bar. Both label
238
+ colors fall below AA over the worst case (the material over black in light,
239
+ white in dark): the quiet unselected color, and in light the brand selected
240
+ color (about 3.2:1). An unselected tab lifts halfway toward the normal text
241
+ color, as an overlaid ToolbarText description does, and the selected tab
242
+ mixes 40% of it into the brand color, staying plainly the brand hue. */
243
+ .kui-tab-scaffold[data-bar-presentation="overlay"]
244
+ > .kui-tab-scaffold__bar
245
+ > .kui-tab-scaffold__tab:not([aria-selected="true"]) {
246
+ color: color-mix(
247
+ in srgb,
248
+ var(--kui-color-text-quiet),
249
+ var(--kui-color-text) 50%
250
+ );
251
+ }
252
+
253
+ .kui-tab-scaffold[data-bar-presentation="overlay"]
254
+ > .kui-tab-scaffold__bar
255
+ > .kui-tab-scaffold__tab[aria-selected="true"] {
256
+ color: color-mix(
257
+ in srgb,
258
+ var(--kui-color-brand-on-quiet),
259
+ var(--kui-color-text) 40%
260
+ );
261
+ }
262
+
188
263
  .kui-tab-scaffold__tab:focus-visible {
189
264
  outline: var(--kui-focus-ring);
190
265
  outline-offset: -2px;
@@ -172,10 +172,19 @@
172
172
  .kui-token-search__clear:hover,
173
173
  .kui-token-search__clear:focus-visible,
174
174
  .kui-token-search__trailing-action:hover,
175
- .kui-token-search__trailing-action:focus-visible {
175
+ .kui-token-search__trailing-action:focus-visible,
176
+ .kui-token-search__trailing-action[aria-expanded="true"],
177
+ .kui-token-search__trailing-action[aria-pressed="true"] {
176
178
  background: color-mix(in srgb, currentColor 12%, transparent);
177
179
  }
178
180
 
181
+ /* An open disclosure or an on toggle keeps the hover fill and reads at full
182
+ text strength, so its state stays visible after the pointer leaves. */
183
+ .kui-token-search__trailing-action[aria-expanded="true"],
184
+ .kui-token-search__trailing-action[aria-pressed="true"] {
185
+ color: var(--kui-color-text);
186
+ }
187
+
179
188
  .kui-token-search__token button:focus-visible,
180
189
  .kui-token-search__clear:focus-visible,
181
190
  .kui-token-search__trailing-action:focus-visible {
@@ -386,7 +395,9 @@
386
395
  box-shadow: none;
387
396
  }
388
397
 
389
- .kui-token-search[data-presentation="toolbar-group"] .kui-token-search__clear {
398
+ .kui-token-search[data-presentation="toolbar-group"] .kui-token-search__clear,
399
+ .kui-token-search[data-presentation="toolbar-group"]
400
+ .kui-token-search__trailing-action {
390
401
  border-radius: var(--kui-control-highlight-radius);
391
402
  }
392
403
 
@@ -139,9 +139,12 @@
139
139
  );
140
140
  --kui-toolbar-control-border: var(--kui-toolbar-control-dark-border, #353536);
141
141
  --kui-toolbar-control-color: var(--kui-color-neutral-on-loud);
142
+ /* A 14% surface wash over the loud fill: visible hover feedback that keeps
143
+ the on-loud icon above 3:1 over a light page (about 3.4:1; a 28% wash
144
+ measured 2.7:1) and well above it in dark (about 5.8:1). */
142
145
  --kui-toolbar-control-hover-background: color-mix(
143
146
  in srgb,
144
- var(--kui-color-surface) 28%,
147
+ var(--kui-color-surface) 14%,
145
148
  transparent
146
149
  );
147
150
  }
@@ -384,6 +387,39 @@
384
387
  background: transparent;
385
388
  }
386
389
 
390
+ /* In overlay NavStack chrome (`chromePresentation="overlay"`), an overlay
391
+ NavStack bottom toolbar (`bottomToolbarPresentation="overlay"`), cross-fading
392
+ copies included, an overlay Pane header (`headerPresentation="overlay"`), or
393
+ a DialogSurface floating header's actions (`headerPresentation="floating"`),
394
+ the group sits on the translucent chrome material with arbitrary content
395
+ scrolling beneath it. Its quiet control color over its own translucent fill
396
+ falls below AA there over the worst-case backdrop (about 3.0:1 in light
397
+ over black, 3.5:1 in dark over white), so an untoned group's controls take
398
+ the normal text color (well above 4.5:1 in both), as an untoned
399
+ ToolbarText's title does. A dark-toned group (or an avatar scrim) keeps its
400
+ own loud colors. */
401
+ .kui-pane[data-header-presentation="overlay"]
402
+ > .kui-pane__header
403
+ .kui-toolbar-control-group:not([data-tone="dark"], [data-scrim="true"]),
404
+ .kui-dialog-surface[data-header-presentation="floating"]
405
+ > wa-dialog
406
+ > .kui-toolbar-control-group[slot="header-actions"]:not(
407
+ [data-tone="dark"],
408
+ [data-scrim="true"]
409
+ ),
410
+ .kui-dialog-surface[data-header-presentation="floating"]
411
+ > wa-dialog
412
+ > [slot="header-actions"]
413
+ .kui-toolbar-control-group:not([data-tone="dark"], [data-scrim="true"]),
414
+ .kui-nav-stack[data-chrome-presentation="overlay"]
415
+ > .kui-nav-stack__chrome
416
+ .kui-toolbar-control-group:not([data-tone="dark"], [data-scrim="true"]),
417
+ .kui-nav-stack[data-bottom-toolbar-presentation="overlay"]
418
+ > .kui-nav-stack__bottom
419
+ .kui-toolbar-control-group:not([data-tone="dark"], [data-scrim="true"]) {
420
+ --kui-toolbar-control-color: var(--kui-color-text);
421
+ }
422
+
387
423
  /* `selectedChrome="none"`: a pressed control paints like its unpressed
388
424
  neighbors, for an icon-state toggle whose glyph already shows the state.
389
425
  One button opts out alone with `data-selected-chrome="none"`. After the
@@ -732,6 +768,44 @@
732
768
  background: transparent;
733
769
  }
734
770
 
771
+ /* A lone control's hover replaces the whole group's fill (above). A dark
772
+ group's hover tint is a translucent surface wash meant to lie OVER its loud
773
+ fill, as it does on one control of a dark group; replacing the fill with it
774
+ left a near-white circle behind the dark tone's white icon on a light page
775
+ (about 1.3:1). So a dark lone group keeps its fill and paints the tint over
776
+ it as an image layer. Avatar groups paint their image as the background and
777
+ keep the plain rule. */
778
+ .kui-toolbar-control-group[data-tone="dark"][data-single="true"]:not(
779
+ [data-content="avatar"]
780
+ ):has(
781
+ > :is(
782
+ button:not(:disabled, [aria-disabled="true"]),
783
+ wa-button:not([disabled]),
784
+ .kui-popup-menu,
785
+ wa-dropdown:not(.kui-popup-menu),
786
+ .kui-select,
787
+ .kui-toolbar-action-link
788
+ )
789
+ ):hover,
790
+ .kui-toolbar-control-group[data-tone="dark"]:not(
791
+ [data-single="true"],
792
+ [data-content="avatar"]
793
+ ):has(
794
+ > :is(
795
+ button:not(:disabled, [aria-disabled="true"]),
796
+ wa-button:not([disabled]),
797
+ .kui-select,
798
+ .kui-toolbar-action-link
799
+ ):nth-child(1 of :where(:not([data-toolbar-visibility-state]))):where(
800
+ :nth-last-child(1 of :not([data-toolbar-visibility-state]))
801
+ )
802
+ ):hover {
803
+ background-color: var(--kui-toolbar-control-background);
804
+ background-image: linear-gradient(
805
+ var(--kui-toolbar-control-hover-background) 0 0
806
+ );
807
+ }
808
+
735
809
  /* A toolbar Select beside other controls highlights its own pill on hover
736
810
  (select.css); a lone Select instead lifts the whole group (above). */
737
811
 
@@ -138,6 +138,72 @@ button.kui-toolbar-text:focus-visible {
138
138
  color: var(--kui-color-text);
139
139
  }
140
140
 
141
+ /* In an overlay Pane header (`headerPresentation="overlay"`), overlay
142
+ NavStack chrome (`chromePresentation="overlay"`), or an overlay NavStack
143
+ bottom toolbar (`bottomToolbarPresentation="overlay"`), cross-fading
144
+ copies included, the title sits on the translucent chrome material with
145
+ arbitrary content scrolling beneath it. Quiet colors fall below AA there
146
+ over the worst-case backdrop (the material over black in light, white in
147
+ dark): the default title color
148
+ measures about 3.8:1 (light) and the description about 3.2:1 (light) and
149
+ 4.1:1 (dark). The title takes the normal text color (about 10.5:1 and
150
+ 8.3:1), and the description lifts halfway toward it (about 5.9:1 in both
151
+ themes), so it still reads as secondary but keeps AA. A ToolbarText in a
152
+ DialogSurface floating header (`headerPresentation="floating"`), slotted
153
+ as the dialog's label or into its header actions, sits on the raised chrome
154
+ material and takes the same lift. See docs/accessibility.md "Chrome
155
+ material". */
156
+ .kui-pane[data-header-presentation="overlay"]
157
+ > .kui-pane__header
158
+ .kui-toolbar-text:not([data-tone]),
159
+ .kui-dialog-surface[data-header-presentation="floating"]
160
+ > wa-dialog
161
+ > .kui-toolbar-text:is([slot="label"], [slot="header-actions"]):not(
162
+ [data-tone]
163
+ ),
164
+ .kui-dialog-surface[data-header-presentation="floating"]
165
+ > wa-dialog
166
+ > :is([slot="label"], [slot="header-actions"])
167
+ .kui-toolbar-text:not([data-tone]),
168
+ .kui-nav-stack[data-chrome-presentation="overlay"]
169
+ > .kui-nav-stack__chrome
170
+ .kui-toolbar-text:not([data-tone]),
171
+ .kui-nav-stack[data-bottom-toolbar-presentation="overlay"]
172
+ > .kui-nav-stack__bottom
173
+ .kui-toolbar-text:not([data-tone]) {
174
+ color: var(--kui-color-text);
175
+ }
176
+
177
+ .kui-pane[data-header-presentation="overlay"]
178
+ > .kui-pane__header
179
+ .kui-toolbar-text:not([data-tone])
180
+ .kui-toolbar-text__description,
181
+ .kui-dialog-surface[data-header-presentation="floating"]
182
+ > wa-dialog
183
+ > .kui-toolbar-text:is([slot="label"], [slot="header-actions"]):not(
184
+ [data-tone]
185
+ )
186
+ .kui-toolbar-text__description,
187
+ .kui-dialog-surface[data-header-presentation="floating"]
188
+ > wa-dialog
189
+ > :is([slot="label"], [slot="header-actions"])
190
+ .kui-toolbar-text:not([data-tone])
191
+ .kui-toolbar-text__description,
192
+ .kui-nav-stack[data-chrome-presentation="overlay"]
193
+ > .kui-nav-stack__chrome
194
+ .kui-toolbar-text:not([data-tone])
195
+ .kui-toolbar-text__description,
196
+ .kui-nav-stack[data-bottom-toolbar-presentation="overlay"]
197
+ > .kui-nav-stack__bottom
198
+ .kui-toolbar-text:not([data-tone])
199
+ .kui-toolbar-text__description {
200
+ color: color-mix(
201
+ in srgb,
202
+ var(--kui-color-text-quiet),
203
+ var(--kui-color-text) 50%
204
+ );
205
+ }
206
+
141
207
  .kui-toolbar-text[data-tone="dark"] {
142
208
  color: var(--kui-color-neutral-on-loud);
143
209
  }
@@ -1,4 +1,9 @@
1
- /** Importing this module registers wa-dialog and repairs wrapped native names. */
1
+ /**
2
+ * Importing this module registers wa-dialog, repairs wrapped native names, and
3
+ * measures each DialogSurface header and footer for a floating header's or
4
+ * footer's body clearance, and shows a floating header's divider while
5
+ * content is scrolled beneath it.
6
+ */
2
7
  declare const surfaceScaffoldElementsRegistered = true;
3
8
 
4
9
  export { surfaceScaffoldElementsRegistered };
@@ -2,13 +2,56 @@ import WaDialog from '@awesome.me/webawesome/dist/components/dialog/dialog.js';
2
2
 
3
3
  // src/components/surfaces/surface-scaffold/register/surface-scaffold-register.ts
4
4
 
5
- // src/components/surfaces/surface-scaffold/internal/install-dialog-label.ts
5
+ // src/components/surfaces/surface-scaffold/internal/install-chrome-block-size.ts
6
+ var HEADER_BLOCK_SIZE_PROPERTY = "--_kui-dialog-surface-header-block-size";
7
+ var FOOTER_BLOCK_SIZE_PROPERTY = "--_kui-dialog-surface-footer-block-size";
8
+ var CHROME_PARTS = [
9
+ ["header", HEADER_BLOCK_SIZE_PROPERTY],
10
+ ["footer", FOOTER_BLOCK_SIZE_PROPERTY]
11
+ ];
6
12
  var installed = /* @__PURE__ */ new WeakSet();
13
+ var observedParts = /* @__PURE__ */ new WeakSet();
14
+ function installChromeBlockSize(prototype) {
15
+ if (installed.has(prototype)) return;
16
+ installed.add(prototype);
17
+ const target = prototype;
18
+ const update = target.update;
19
+ target.update = function(changed) {
20
+ update.call(this, changed);
21
+ if (!this.parentElement?.classList.contains("kui-dialog-surface")) return;
22
+ const panel = this.dialog;
23
+ if (!panel) return;
24
+ for (const [part, property] of CHROME_PARTS) {
25
+ const element = this.shadowRoot?.querySelector(`[part~="${part}"]`);
26
+ if (element) observePart(panel, element, property);
27
+ else panel.style.removeProperty(property);
28
+ }
29
+ };
30
+ }
31
+ function observePart(panel, element, property) {
32
+ if (observedParts.has(element)) return;
33
+ observedParts.add(element);
34
+ const publish = (blockSize) => {
35
+ const value = `${blockSize}px`;
36
+ if (panel.style.getPropertyValue(property) !== value)
37
+ panel.style.setProperty(property, value);
38
+ };
39
+ publish(element.getBoundingClientRect().height);
40
+ if (typeof ResizeObserver === "undefined") return;
41
+ new ResizeObserver((entries) => {
42
+ if (!element.isConnected) return;
43
+ const box = entries[entries.length - 1]?.borderBoxSize?.[0];
44
+ publish(box ? box.blockSize : element.getBoundingClientRect().height);
45
+ }).observe(element);
46
+ }
47
+
48
+ // src/components/surfaces/surface-scaffold/internal/install-dialog-label.ts
49
+ var installed2 = /* @__PURE__ */ new WeakSet();
7
50
  var observed = /* @__PURE__ */ new WeakSet();
8
51
  var ownedNames = /* @__PURE__ */ new WeakMap();
9
52
  function installDialogLabel(prototype) {
10
- if (installed.has(prototype)) return;
11
- installed.add(prototype);
53
+ if (installed2.has(prototype)) return;
54
+ installed2.add(prototype);
12
55
  const target = prototype;
13
56
  const update = target.update;
14
57
  target.update = function(changed) {
@@ -56,8 +99,51 @@ function syncDialogLabel(host) {
56
99
  ownedNames.set(panel, { attribute, value });
57
100
  }
58
101
 
102
+ // src/components/surfaces/surface-scaffold/internal/install-header-scroll-divider.ts
103
+ var HEADER_DIVIDER_PROPERTY = "--_kui-dialog-surface-header-divider";
104
+ var SHOWN = "var(--_kui-dialog-surface-header-divider-color)";
105
+ var THRESHOLD = 1;
106
+ var installed3 = /* @__PURE__ */ new WeakSet();
107
+ var wiredPanels = /* @__PURE__ */ new WeakSet();
108
+ function installHeaderScrollDivider(prototype) {
109
+ if (installed3.has(prototype)) return;
110
+ installed3.add(prototype);
111
+ const target = prototype;
112
+ const update = target.update;
113
+ target.update = function(changed) {
114
+ update.call(this, changed);
115
+ if (!this.parentElement?.classList.contains("kui-dialog-surface")) return;
116
+ const panel = this.dialog;
117
+ if (panel && !wiredPanels.has(panel)) wire(this, panel);
118
+ };
119
+ }
120
+ function wire(host, panel) {
121
+ wiredPanels.add(panel);
122
+ const scrolled = /* @__PURE__ */ new Set();
123
+ const onScroll = (event) => {
124
+ const scroller = event.target;
125
+ if (!(scroller instanceof Element)) return;
126
+ const header = host.shadowRoot?.querySelector('[part~="header"]');
127
+ const floating = host.parentElement?.getAttribute("data-header-presentation") === "floating";
128
+ if (floating && header && scroller.scrollTop > THRESHOLD && // Only a scroller that reaches beneath the header.
129
+ scroller.getBoundingClientRect().top <= header.getBoundingClientRect().bottom)
130
+ scrolled.add(scroller);
131
+ else scrolled.delete(scroller);
132
+ for (const element of scrolled)
133
+ if (!element.isConnected) scrolled.delete(element);
134
+ const value = scrolled.size > 0 ? SHOWN : "";
135
+ if (panel.style.getPropertyValue(HEADER_DIVIDER_PROPERTY) === value) return;
136
+ if (value) panel.style.setProperty(HEADER_DIVIDER_PROPERTY, value);
137
+ else panel.style.removeProperty(HEADER_DIVIDER_PROPERTY);
138
+ };
139
+ for (const root of [panel, host])
140
+ root.addEventListener("scroll", onScroll, { capture: true, passive: true });
141
+ }
142
+
59
143
  // src/components/surfaces/surface-scaffold/register/surface-scaffold-register.ts
60
144
  installDialogLabel(WaDialog.prototype);
145
+ installChromeBlockSize(WaDialog.prototype);
146
+ installHeaderScrollDivider(WaDialog.prototype);
61
147
  var surfaceScaffoldElementsRegistered = true;
62
148
 
63
149
  export { surfaceScaffoldElementsRegistered };