@kerfjs/ui 5.0.0-beta.106 → 5.0.0-beta.108

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 (283) hide show
  1. package/README.md +2 -1
  2. package/ai/application-ui-diagnostic-ids-v1.json +1 -0
  3. package/ai/application-ui-profile.defaults.json +6 -1
  4. package/ai/component-catalog.json +184 -39
  5. package/ai/component-composition-rules.json +20 -2
  6. package/ai/component-composition.d.ts +11 -0
  7. package/ai/component-composition.json +126 -21
  8. package/ai/component-composition.schema.json +32 -0
  9. package/ai/components/README.md +3 -2
  10. package/ai/components/button.md +9 -6
  11. package/ai/components/checkbox-group.md +89 -0
  12. package/ai/components/checkbox.md +3 -2
  13. package/ai/components/chip.md +1 -0
  14. package/ai/components/color-picker.md +10 -7
  15. package/ai/components/disclosure.md +6 -1
  16. package/ai/components/foundation.md +1 -1
  17. package/ai/components/list-detail-dialog.md +1 -1
  18. package/ai/components/list.md +1 -1
  19. package/ai/components/message-composer.md +2 -1
  20. package/ai/components/radio-group.md +4 -3
  21. package/ai/components/segmented-control.md +1 -0
  22. package/ai/components/split-button.md +5 -2
  23. package/ai/components/surface-scaffold.md +3 -1
  24. package/ai/components/switch.md +1 -1
  25. package/ai/components/text-field.md +3 -1
  26. package/ai/components/text.md +2 -0
  27. package/ai/components/toolbar-control-group.md +1 -0
  28. package/ai/components/toolbar.md +1 -1
  29. package/ai/components/wa-checkbox-group.md +5 -5
  30. package/ai/components/workbench-dialog.md +1 -1
  31. package/ai/public-api-signatures-v1.md +258 -35
  32. package/ai/skill.md +82 -65
  33. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  34. package/dist/browser/checkbox-group.js +5 -0
  35. package/dist/button.d.ts +18 -5
  36. package/dist/button.js +1 -1
  37. package/dist/catalog.js +11 -10
  38. package/dist/catalog.js.map +1 -1
  39. package/dist/checkbox-group.d.ts +70 -0
  40. package/dist/checkbox-group.js +7 -0
  41. package/dist/checkbox-group.js.map +1 -0
  42. package/dist/chip.d.ts +20 -2
  43. package/dist/chip.js +2 -1
  44. package/dist/{chunk-SQ2Q3OQI.js → chunk-2LM3PYHA.js} +2 -2
  45. package/dist/{chunk-SQ2Q3OQI.js.map → chunk-2LM3PYHA.js.map} +1 -1
  46. package/dist/{chunk-HYORKKYP.js → chunk-3VZ6SFD4.js} +1 -1
  47. package/dist/chunk-3VZ6SFD4.js.map +1 -0
  48. package/dist/{chunk-EKBMUXFV.js → chunk-3Z7N2NAZ.js} +141 -54
  49. package/dist/chunk-3Z7N2NAZ.js.map +1 -0
  50. package/dist/{chunk-23PPPNH6.js → chunk-4OV272LF.js} +2 -2
  51. package/dist/{chunk-23PPPNH6.js.map → chunk-4OV272LF.js.map} +1 -1
  52. package/dist/{chunk-2PXZBFEC.js → chunk-5J6Z4BWY.js} +1 -1
  53. package/dist/{chunk-2PXZBFEC.js.map → chunk-5J6Z4BWY.js.map} +1 -1
  54. package/dist/{chunk-TAHR457X.js → chunk-6CDILXF4.js} +5 -1
  55. package/dist/chunk-6CDILXF4.js.map +1 -0
  56. package/dist/{chunk-DDSCVEO5.js → chunk-CPSGUGAY.js} +6 -6
  57. package/dist/{chunk-DDSCVEO5.js.map → chunk-CPSGUGAY.js.map} +1 -1
  58. package/dist/{chunk-C3RLKKFQ.js → chunk-EYDXFOSH.js} +13 -8
  59. package/dist/chunk-EYDXFOSH.js.map +1 -0
  60. package/dist/{chunk-PE2KPZDW.js → chunk-HDXUHLTQ.js} +7 -2
  61. package/dist/chunk-HDXUHLTQ.js.map +1 -0
  62. package/dist/chunk-HU5IHXCI.js +73 -0
  63. package/dist/chunk-HU5IHXCI.js.map +1 -0
  64. package/dist/{chunk-DF2YKGIX.js → chunk-JCCN3U6X.js} +2 -2
  65. package/dist/{chunk-DF2YKGIX.js.map → chunk-JCCN3U6X.js.map} +1 -1
  66. package/dist/{chunk-UTCAOOXU.js → chunk-KPINJHIC.js} +8 -2
  67. package/dist/chunk-KPINJHIC.js.map +1 -0
  68. package/dist/{chunk-N7MBGONH.js → chunk-LMHN2TJL.js} +22 -2
  69. package/dist/chunk-LMHN2TJL.js.map +1 -0
  70. package/dist/{chunk-NS2TBYM4.js → chunk-LZUUUHNH.js} +7 -3
  71. package/dist/chunk-LZUUUHNH.js.map +1 -0
  72. package/dist/{chunk-GCAK23PU.js → chunk-MVKHSAKE.js} +2 -2
  73. package/dist/{chunk-GCAK23PU.js.map → chunk-MVKHSAKE.js.map} +1 -1
  74. package/dist/{chunk-OY2MXIBB.js → chunk-NB6POCFH.js} +3 -1
  75. package/dist/chunk-NB6POCFH.js.map +1 -0
  76. package/dist/{chunk-ECFCAKWE.js → chunk-NR6O74ST.js} +5 -1
  77. package/dist/chunk-NR6O74ST.js.map +1 -0
  78. package/dist/{chunk-7W3GKMN4.js → chunk-OQP3PFKQ.js} +7 -4
  79. package/dist/chunk-OQP3PFKQ.js.map +1 -0
  80. package/dist/{chunk-FKKJHGN5.js → chunk-P7EJ4CAD.js} +2 -2
  81. package/dist/{chunk-FKKJHGN5.js.map → chunk-P7EJ4CAD.js.map} +1 -1
  82. package/dist/{chunk-ZXZAECHZ.js → chunk-SOFPUW3O.js} +4 -4
  83. package/dist/{chunk-ZXZAECHZ.js.map → chunk-SOFPUW3O.js.map} +1 -1
  84. package/dist/{chunk-WJKHLOP5.js → chunk-TI2GV36S.js} +8 -2
  85. package/dist/chunk-TI2GV36S.js.map +1 -0
  86. package/dist/{chunk-QLA25V2K.js → chunk-WPNIKZMJ.js} +15 -1
  87. package/dist/chunk-WPNIKZMJ.js.map +1 -0
  88. package/dist/{chunk-FG6U7KQQ.js → chunk-WSEJ2FDE.js} +4 -4
  89. package/dist/{chunk-FG6U7KQQ.js.map → chunk-WSEJ2FDE.js.map} +1 -1
  90. package/dist/{chunk-ACUKTVS3.js → chunk-XWNXGFX3.js} +52 -42
  91. package/dist/chunk-XWNXGFX3.js.map +1 -0
  92. package/dist/{chunk-WU57BYYM.js → chunk-YVZCKXR3.js} +8 -2
  93. package/dist/chunk-YVZCKXR3.js.map +1 -0
  94. package/dist/{chunk-CMLPFIC5.js → chunk-ZN7KUMPY.js} +3 -1
  95. package/dist/chunk-ZN7KUMPY.js.map +1 -0
  96. package/dist/collapsible-panel.js +2 -2
  97. package/dist/color-picker.d.ts +30 -5
  98. package/dist/color-picker.js +1 -1
  99. package/dist/disclosure.d.ts +13 -4
  100. package/dist/disclosure.js.map +1 -1
  101. package/dist/help-tags-register.js +1 -1
  102. package/dist/index.d.ts +2 -1
  103. package/dist/index.js +24 -23
  104. package/dist/list-action-row.js +1 -1
  105. package/dist/list-detail-dialog.d.ts +7 -1
  106. package/dist/list-detail-dialog.js +7 -5
  107. package/dist/list-detail-dialog.js.map +1 -1
  108. package/dist/list-header.d.ts +8 -1
  109. package/dist/list-header.js +1 -1
  110. package/dist/list.d.ts +7 -1
  111. package/dist/list.js +1 -1
  112. package/dist/message-composer.js +1 -1
  113. package/dist/nav-stack.js +1 -1
  114. package/dist/popup-menu-register.js +2 -2
  115. package/dist/popup-menu.d.ts +15 -5
  116. package/dist/popup-menu.js +1 -1
  117. package/dist/segmented-control.d.ts +3 -1
  118. package/dist/segmented-control.js +1 -1
  119. package/dist/select-register.js +3 -3
  120. package/dist/split-button.d.ts +14 -2
  121. package/dist/split-button.js +1 -1
  122. package/dist/split-view.js +1 -1
  123. package/dist/styles/anchored-surface.css +3 -0
  124. package/dist/styles/button.css +57 -2
  125. package/dist/styles/catalog/components/catalog-example.css +55 -18
  126. package/dist/styles/checkbox-group.css +63 -0
  127. package/dist/styles/checkbox.css +7 -6
  128. package/dist/styles/color-picker.css +69 -34
  129. package/dist/styles/components/actions/button/button.css +57 -2
  130. package/dist/styles/components/actions/split-button/split-button.css +51 -5
  131. package/dist/styles/components/actions/toolbar/toolbar.css +9 -0
  132. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +29 -1
  133. package/dist/styles/components/collections/framed-list/framed-list.css +6 -0
  134. package/dist/styles/components/collections/list/list.css +6 -0
  135. package/dist/styles/components/collections/list-header/list-header.css +7 -0
  136. package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +3 -0
  137. package/dist/styles/components/forms/checkbox/checkbox.css +7 -6
  138. package/dist/styles/components/forms/checkbox-group/checkbox-group.css +63 -0
  139. package/dist/styles/components/forms/color-picker/color-picker.css +69 -34
  140. package/dist/styles/components/forms/radio-group/radio-group.css +7 -5
  141. package/dist/styles/components/forms/select/select.css +6 -2
  142. package/dist/styles/components/forms/switch/switch.css +3 -3
  143. package/dist/styles/components/layout/pane/pane.css +24 -0
  144. package/dist/styles/components/layout/workbench/workbench.css +4 -0
  145. package/dist/styles/components/navigation/nav-stack/nav-stack.css +77 -1
  146. package/dist/styles/components/navigation/tab-bar/tab-bar.css +6 -0
  147. package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +17 -0
  148. package/dist/styles/components/surfaces/content-item/content-item.css +30 -0
  149. package/dist/styles/components/surfaces/disclosure/disclosure.css +38 -0
  150. package/dist/styles/components/surfaces/sunken-panel/sunken-panel.css +3 -0
  151. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +8 -0
  152. package/dist/styles/components/typography/text/text.css +10 -0
  153. package/dist/styles/content-item.css +30 -0
  154. package/dist/styles/disclosure.css +38 -0
  155. package/dist/styles/foundation.css +78 -13
  156. package/dist/styles/framed-list.css +6 -0
  157. package/dist/styles/list-header.css +7 -0
  158. package/dist/styles/list.css +6 -0
  159. package/dist/styles/nav-stack.css +77 -1
  160. package/dist/styles/pane.css +24 -0
  161. package/dist/styles/radio-group.css +7 -5
  162. package/dist/styles/select.css +6 -2
  163. package/dist/styles/split-button.css +51 -5
  164. package/dist/styles/styles.css +1 -0
  165. package/dist/styles/sunken-panel.css +3 -0
  166. package/dist/styles/surface-scaffold.css +8 -0
  167. package/dist/styles/switch.css +3 -3
  168. package/dist/styles/tab-bar.css +6 -0
  169. package/dist/styles/tab-navigator.css +17 -0
  170. package/dist/styles/text.css +10 -0
  171. package/dist/styles/toolbar-control-group.css +29 -1
  172. package/dist/styles/toolbar.css +9 -0
  173. package/dist/styles/webawesome.css +56 -12
  174. package/dist/styles/workbench.css +4 -0
  175. package/dist/surface-scaffold.d.ts +15 -1
  176. package/dist/surface-scaffold.js +1 -1
  177. package/dist/text-field.d.ts +7 -1
  178. package/dist/text-field.js +1 -1
  179. package/dist/toolbar-control-group.d.ts +8 -2
  180. package/dist/toolbar-control-group.js +1 -1
  181. package/dist/toolbar.d.ts +6 -1
  182. package/dist/toolbar.js +1 -1
  183. package/dist/wire-catalog.js +1 -1
  184. package/dist/wire-color-fields.js +2 -2
  185. package/dist/wire-color-pickers.d.ts +4 -3
  186. package/dist/wire-color-pickers.js +1 -1
  187. package/dist/wire-comboboxes.js +1 -1
  188. package/dist/wire-workbench.js +1 -1
  189. package/dist/workbench-dialog.d.ts +11 -2
  190. package/dist/workbench-dialog.js +6 -3
  191. package/dist/workbench-dialog.js.map +1 -1
  192. package/dist/workbench.js +1 -1
  193. package/docs/accessibility.md +58 -3
  194. package/docs/anchored-surface.md +6 -1
  195. package/docs/catalog.md +26 -1
  196. package/docs/color-picker.md +39 -8
  197. package/docs/component-contract.md +47 -3
  198. package/docs/component-selection.md +58 -21
  199. package/docs/design/templates/button/full-width-dark.svg +1 -0
  200. package/docs/design/templates/button/full-width.svg +1 -0
  201. package/docs/design/templates/button/tones-dark.svg +1 -1
  202. package/docs/design/templates/button/tones.svg +1 -1
  203. package/docs/design/templates/button-dark.svg +1 -1
  204. package/docs/design/templates/button.svg +1 -1
  205. package/docs/design/templates/checkbox/error-dark.svg +1 -1
  206. package/docs/design/templates/checkbox/error.svg +1 -1
  207. package/docs/design/templates/checkbox/states-dark.svg +1 -1
  208. package/docs/design/templates/checkbox/states.svg +1 -1
  209. package/docs/design/templates/checkbox-dark.svg +1 -1
  210. package/docs/design/templates/checkbox-group/horizontal-dark.svg +1 -0
  211. package/docs/design/templates/checkbox-group/horizontal.svg +1 -0
  212. package/docs/design/templates/checkbox-group/vertical-dark.svg +1 -0
  213. package/docs/design/templates/checkbox-group/vertical.svg +1 -0
  214. package/docs/design/templates/checkbox-group-dark.svg +1 -0
  215. package/docs/design/templates/checkbox-group.svg +1 -0
  216. package/docs/design/templates/checkbox.svg +1 -1
  217. package/docs/design/templates/color-picker/gray-dark.svg +1 -1
  218. package/docs/design/templates/color-picker/gray.svg +1 -1
  219. package/docs/design/templates/color-picker/hsb-dark.svg +1 -0
  220. package/docs/design/templates/color-picker/hsb.svg +1 -0
  221. package/docs/design/templates/color-picker/rgb-dark.svg +1 -1
  222. package/docs/design/templates/color-picker/rgb.svg +1 -1
  223. package/docs/design/templates/color-picker-dark.svg +1 -1
  224. package/docs/design/templates/color-picker.svg +1 -1
  225. package/docs/design/templates/disclosure/embedded-dark.svg +1 -0
  226. package/docs/design/templates/disclosure/embedded.svg +1 -0
  227. package/docs/design/templates/disclosure-dark.svg +1 -1
  228. package/docs/design/templates/disclosure.svg +1 -1
  229. package/docs/design/templates/radio-group/horizontal-dark.svg +1 -1
  230. package/docs/design/templates/radio-group/horizontal.svg +1 -1
  231. package/docs/design/templates/radio-group/vertical-dark.svg +1 -1
  232. package/docs/design/templates/radio-group/vertical.svg +1 -1
  233. package/docs/design/templates/radio-group-dark.svg +1 -1
  234. package/docs/design/templates/radio-group.svg +1 -1
  235. package/docs/design/templates/split-button/full-width-dark.svg +1 -0
  236. package/docs/design/templates/split-button/full-width.svg +1 -0
  237. package/docs/design/templates/split-button/pill-dark.svg +1 -0
  238. package/docs/design/templates/split-button/pill.svg +1 -0
  239. package/docs/design/templates/split-button/states-dark.svg +1 -0
  240. package/docs/design/templates/split-button/states.svg +1 -0
  241. package/docs/design/templates/split-button/tones-dark.svg +1 -0
  242. package/docs/design/templates/split-button/tones.svg +1 -0
  243. package/docs/design/templates/split-button-dark.svg +1 -0
  244. package/docs/design/templates/split-button.svg +1 -0
  245. package/docs/design/templates/switch/states-dark.svg +1 -1
  246. package/docs/design/templates/switch/states.svg +1 -1
  247. package/docs/design/templates/switch-dark.svg +1 -1
  248. package/docs/design/templates/switch.svg +1 -1
  249. package/docs/design/templates/text-field/number-dark.svg +1 -0
  250. package/docs/design/templates/text-field/number.svg +1 -0
  251. package/docs/design/templates/text-field-dark.svg +1 -1
  252. package/docs/design/templates/text-field.svg +1 -1
  253. package/docs/design/templates.md +10 -3
  254. package/docs/disclosure.md +34 -0
  255. package/docs/layout.md +16 -5
  256. package/docs/list-detail-dialog.md +4 -0
  257. package/docs/nav-stack.md +6 -2
  258. package/docs/surface-scaffold.md +6 -2
  259. package/docs/ui-doctor.md +16 -4
  260. package/docs/webawesome-migration.md +3 -1
  261. package/docs/webawesome-theme.md +56 -18
  262. package/docs/workbench-dialog.md +4 -1
  263. package/docs/workbench.md +7 -1
  264. package/docs/writing.md +3 -3
  265. package/doctor/copy-capitalization-checks.mjs +10 -7
  266. package/llms.txt +65 -23
  267. package/package.json +7 -1
  268. package/dist/chunk-7W3GKMN4.js.map +0 -1
  269. package/dist/chunk-ACUKTVS3.js.map +0 -1
  270. package/dist/chunk-C3RLKKFQ.js.map +0 -1
  271. package/dist/chunk-CMLPFIC5.js.map +0 -1
  272. package/dist/chunk-ECFCAKWE.js.map +0 -1
  273. package/dist/chunk-EKBMUXFV.js.map +0 -1
  274. package/dist/chunk-HYORKKYP.js.map +0 -1
  275. package/dist/chunk-N7MBGONH.js.map +0 -1
  276. package/dist/chunk-NS2TBYM4.js.map +0 -1
  277. package/dist/chunk-OY2MXIBB.js.map +0 -1
  278. package/dist/chunk-PE2KPZDW.js.map +0 -1
  279. package/dist/chunk-QLA25V2K.js.map +0 -1
  280. package/dist/chunk-TAHR457X.js.map +0 -1
  281. package/dist/chunk-UTCAOOXU.js.map +0 -1
  282. package/dist/chunk-WJKHLOP5.js.map +0 -1
  283. package/dist/chunk-WU57BYYM.js.map +0 -1
@@ -82,6 +82,20 @@ type DialogSurfaceProps = DialogSurfaceName & DialogSurfaceOptions;
82
82
  interface DialogSurfaceOptions {
83
83
  /** The dialog's id, for `openDialog(document.getElementById(id))`. */
84
84
  id?: string;
85
+ /**
86
+ * Id of the element whose text is the dialog's accessible description,
87
+ * such as the `heading` ToolbarText's `descriptionId` or app-owned summary
88
+ * copy in the body; rendered as `aria-describedby` on the `<dialog>`.
89
+ */
90
+ describedBy?: string;
91
+ /**
92
+ * An alert dialog: a confirmation that interrupts the person to report
93
+ * something urgent or ask before a destructive action ("Delete this
94
+ * topic?"). Renders `role="alertdialog"` on the `<dialog>`; set
95
+ * `describedBy` to the element holding the message, and leave
96
+ * `lightDismiss` off so the person answers it.
97
+ */
98
+ alert?: boolean;
85
99
  /** The body content. */
86
100
  children: KerfUiContent;
87
101
  /**
@@ -196,7 +210,7 @@ interface DialogSurfaceOptions {
196
210
  * floating header and footer measurement, the bottom sheet's keyboard
197
211
  * clearance and grabber, and re-render resilience while open.
198
212
  */
199
- declare function DialogSurface({ id, label, labelledBy, heading, headerActions, footer, withoutHeader, lightDismiss, closeLabel, children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, height, minHeight, resizable, enterMotion, exitMotion, bodyLayout, headerPresentation, footerPresentation, footerOffset, rootAttributes, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
213
+ declare function DialogSurface({ id, label, labelledBy, describedBy, alert, heading, headerActions, footer, withoutHeader, lightDismiss, closeLabel, children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, height, minHeight, resizable, enterMotion, exitMotion, bodyLayout, headerPresentation, footerPresentation, footerOffset, rootAttributes, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
200
214
  /** Where a PopoverDialogSurface appears: the top layer, or in flow. */
201
215
  type PopoverDialogSurfacePresentation = 'overlay' | 'embedded';
202
216
  /** Application `data-*` metadata on the surface root. */
@@ -1,4 +1,4 @@
1
- export { DialogSurface, PopoverDialogSurface, PopupSurface } from './chunk-TAHR457X.js';
1
+ export { DialogSurface, PopoverDialogSurface, PopupSurface } from './chunk-6CDILXF4.js';
2
2
  export { closeDialog, openDialog } from './chunk-ATGAJVZ6.js';
3
3
 
4
4
 
@@ -50,6 +50,12 @@ interface TextFieldOptions {
50
50
  showCount?: boolean;
51
51
  /** Single-line value kind (default `text`). */
52
52
  type?: TextFieldType;
53
+ /** Native lower bound for `type="number"` (number or numeric string). */
54
+ min?: number | string;
55
+ /** Native upper bound for `type="number"` (number or numeric string). */
56
+ max?: number | string;
57
+ /** Native number increment (number, numeric string, or `"any"`). */
58
+ step?: number | string;
53
59
  /** A multi-line field: a `<textarea>`. */
54
60
  multiline?: boolean;
55
61
  /** A multi-line field's height in lines (default 3). */
@@ -78,6 +84,6 @@ interface TextFieldOptions {
78
84
  * The whole field is one `<label>`, so clicking anywhere on it focuses the
79
85
  * input; the input is named by `label` (or `ariaLabel`) directly.
80
86
  */
81
- declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, multiline, rows, maxRows, disabled, readOnly, required, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
87
+ declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, min, max, step, multiline, rows, maxRows, disabled, readOnly, required, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
82
88
 
83
89
  export { TextField, type TextFieldName, type TextFieldOptions, type TextFieldProps, type TextFieldRootAttributes, type TextFieldSize, type TextFieldType };
@@ -1,4 +1,4 @@
1
- export { TextField } from './chunk-WU57BYYM.js';
1
+ export { TextField } from './chunk-YVZCKXR3.js';
2
2
 
3
3
 
4
4
 
@@ -19,7 +19,7 @@ type ToolbarControlGroupSelectedTone = 'brand' | 'neutral' | 'pop';
19
19
  type ToolbarControlGroupOverflow = 'visible' | 'scroll' | 'wrap';
20
20
  type ToolbarControlGroupExpandedOverflow = 'clip' | 'visible';
21
21
  type ToolbarControlGroupMenuInset = 'standard' | 'compact' | 'list-zero';
22
- type ToolbarControlGroupVisibility = 'always' | 'compact-only' | 'hide-collapsed-tiny' | 'yield-to-expanded-sibling';
22
+ type ToolbarControlGroupVisibility = 'always' | 'compact-only' | 'hide-collapsed-tiny' | 'yield-to-expanded-sibling' | 'yield-to-priority-row';
23
23
  type ToolbarControlGroupSizing = 'content' | 'grow' | 'fill';
24
24
  type ToolbarControlGroupPlacement = 'start' | 'end';
25
25
  interface ToolbarActionLinkProps {
@@ -82,7 +82,13 @@ interface ToolbarControlGroupProps {
82
82
  overflow?: ToolbarControlGroupOverflow;
83
83
  /** Let app-owned anchored surfaces extend outside an expanded search group. Collapsed searches retain their clipping. */
84
84
  expandedOverflow?: ToolbarControlGroupExpandedOverflow;
85
- /** Responsive visibility owned by the enclosing Toolbar container. */
85
+ /**
86
+ * Responsive visibility owned by the enclosing Toolbar container.
87
+ * `yield-to-priority-row` hides this trailing group exactly while its
88
+ * Toolbar's `responsive="trailing-priority"` row is active (an expanded
89
+ * trailing sibling at the Toolbar's `responsiveAt` width), so the app never
90
+ * restates that breakpoint with `hideBelow`.
91
+ */
86
92
  visibility?: ToolbarControlGroupVisibility;
87
93
  /** Hide below this Toolbar content width; requires wireToolbarVisibility. */
88
94
  hideBelow?: CssLength;
@@ -1,4 +1,4 @@
1
- export { ToolbarActionLink, ToolbarControlGroup } from './chunk-2PXZBFEC.js';
1
+ export { ToolbarActionLink, ToolbarControlGroup } from './chunk-5J6Z4BWY.js';
2
2
 
3
3
 
4
4
 
package/dist/toolbar.d.ts CHANGED
@@ -9,6 +9,11 @@ type ToolbarPosition = 'header' | 'footer';
9
9
  /** Control-band density: `default` (60px with 44px groups) or `compact`. */
10
10
  type ToolbarDensity = 'default' | 'compact';
11
11
  interface ToolbarProps {
12
+ /**
13
+ * The toolbar's id, so app code can address it: name it as a scrolling
14
+ * List's chrome in `wireScrollDividers({ targets })`, or find it in tests.
15
+ */
16
+ id?: string;
12
17
  leading?: KerfUiContent;
13
18
  center?: KerfUiContent;
14
19
  trailing?: KerfUiContent;
@@ -73,6 +78,6 @@ interface ToolbarProps {
73
78
  * toolbar) forwards, so the app configures that toolbar instead of styling it.
74
79
  */
75
80
  type ToolbarConfig = Pick<ToolbarProps, 'density' | 'dividerSides' | 'centerAlign' | 'responsive' | 'responsiveAt' | 'safeAreaEdges'>;
76
- declare function Toolbar({ leading, center, trailing, label, position, density, dividerSides, centerAlign, responsive, responsiveAt, safeAreaEdges, className, slot, }: ToolbarProps): kerfjs.SafeHtml;
81
+ declare function Toolbar({ id, leading, center, trailing, label, position, density, dividerSides, centerAlign, responsive, responsiveAt, safeAreaEdges, className, slot, }: ToolbarProps): kerfjs.SafeHtml;
77
82
 
78
83
  export { Sides, Toolbar, type ToolbarConfig, type ToolbarDensity, type ToolbarPosition, type ToolbarProps };
package/dist/toolbar.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Toolbar } from './chunk-CMLPFIC5.js';
1
+ export { Toolbar } from './chunk-ZN7KUMPY.js';
2
2
 
3
3
  //# sourceMappingURL=toolbar.js.map
@@ -1,4 +1,4 @@
1
- import { wireWorkbench } from './chunk-QLA25V2K.js';
1
+ import { wireWorkbench } from './chunk-WPNIKZMJ.js';
2
2
 
3
3
  import { wireScrollDividers } from './chunk-OZ6X76G5.js';
4
4
  import { loadUxReviewProject } from './chunk-6C6DLP3W.js';
@@ -1,5 +1,5 @@
1
- import { wireColorPickers } from './chunk-ACUKTVS3.js';
2
- import { ColorPicker } from './chunk-EKBMUXFV.js';
1
+ import { wireColorPickers } from './chunk-XWNXGFX3.js';
2
+ import { ColorPicker } from './chunk-3Z7N2NAZ.js';
3
3
 
4
4
 
5
5
  import { openAnchoredSurface } from './chunk-3WYREXW4.js';
@@ -19,9 +19,10 @@ interface WireColorPickersOptions {
19
19
  }
20
20
  /**
21
21
  * Make every ColorPicker under `root` interactive. The area takes pointer
22
- * drags and the arrow keys (Shift for steps of 10%); the hue and opacity
23
- * sliders are native ranges; channel, hex, and opacity fields commit on Enter
24
- * or blur; the model menu switches the channel fields; the screen sampler
22
+ * drags and the arrow keys (Shift for steps of 10%); the model's channel
23
+ * sliders and the opacity slider are native ranges, each changing only its
24
+ * own channel; channel, hex, and opacity fields commit on Enter or blur; the
25
+ * model menu switches the channel sliders and fields; the screen sampler
25
26
  * opens the EyeDropper where the browser has it; preset and sample swatches
26
27
  * apply their color. Each change updates the picker at once, dispatches a
27
28
  * bubbling `kui-color-input` (or `kui-color-change` when committed) event
@@ -1,4 +1,4 @@
1
- export { wireColorPickers } from './chunk-ACUKTVS3.js';
1
+ export { wireColorPickers } from './chunk-XWNXGFX3.js';
2
2
 
3
3
 
4
4
 
@@ -1,4 +1,4 @@
1
- export { wireComboboxes } from './chunk-GCAK23PU.js';
1
+ export { wireComboboxes } from './chunk-MVKHSAKE.js';
2
2
 
3
3
 
4
4
  //# sourceMappingURL=wire-comboboxes.js.map
@@ -1,4 +1,4 @@
1
- export { wireWorkbench } from './chunk-QLA25V2K.js';
1
+ export { wireWorkbench } from './chunk-WPNIKZMJ.js';
2
2
 
3
3
 
4
4
 
@@ -34,8 +34,17 @@ interface WorkbenchDialogProps {
34
34
  label: string;
35
35
  /** The dialog's primary title: an xl heading in the work area's toolbar. */
36
36
  title: string;
37
- /** A short supporting line under the title. */
37
+ /**
38
+ * A short supporting line under the title. It is the dialog's accessible
39
+ * description (`aria-describedby` to its `<id>-description`) unless
40
+ * `describedBy` names other content.
41
+ */
38
42
  description?: string;
43
+ /**
44
+ * Id of the element whose text is the dialog's accessible description, in
45
+ * place of `description`, such as app-owned summary copy in `main`.
46
+ */
47
+ describedBy?: string;
39
48
  /** Groups after the title in the work area's toolbar. */
40
49
  leading?: KerfUiContent;
41
50
  /** Header controls before the close control, such as a `ToolbarControlGroup`. */
@@ -92,6 +101,6 @@ interface WorkbenchDialogProps {
92
101
  * Escape or a press outside it before the dialog sees the Escape. Built from
93
102
  * `DialogSurface` and `Workbench`.
94
103
  */
95
- declare function WorkbenchDialog({ id, label, title, description, leading, actions, main, mainFooter, mainBottomToolbar, leftRail, rightRail, bottomDrawer, compact, closeLabel, lightDismiss, size, preferredWidth, height, maxHeight, viewportGutter, mainMinSize, rootAttributes, className, slot, }: WorkbenchDialogProps): kerfjs.SafeHtml;
104
+ declare function WorkbenchDialog({ id, label, title, description, describedBy, leading, actions, main, mainFooter, mainBottomToolbar, leftRail, rightRail, bottomDrawer, compact, closeLabel, lightDismiss, size, preferredWidth, height, maxHeight, viewportGutter, mainMinSize, rootAttributes, className, slot, }: WorkbenchDialogProps): kerfjs.SafeHtml;
96
105
 
97
106
  export { WorkbenchDialog, type WorkbenchDialogProps, type WorkbenchDialogRootAttributes };
@@ -1,7 +1,7 @@
1
- import { DialogSurface } from './chunk-TAHR457X.js';
1
+ import { DialogSurface } from './chunk-6CDILXF4.js';
2
2
  export { closeDialog, openDialog } from './chunk-ATGAJVZ6.js';
3
3
 
4
- import { Workbench } from './chunk-DDSCVEO5.js';
4
+ import { Workbench } from './chunk-CPSGUGAY.js';
5
5
 
6
6
 
7
7
 
@@ -10,7 +10,7 @@ import { ToolbarText } from './chunk-EKHDLBRP.js';
10
10
 
11
11
 
12
12
 
13
- import { ToolbarControlGroup } from './chunk-2PXZBFEC.js';
13
+ import { ToolbarControlGroup } from './chunk-5J6Z4BWY.js';
14
14
 
15
15
 
16
16
 
@@ -33,6 +33,7 @@ function WorkbenchDialog({
33
33
  label,
34
34
  title,
35
35
  description,
36
+ describedBy,
36
37
  leading,
37
38
  actions,
38
39
  main,
@@ -59,6 +60,7 @@ function WorkbenchDialog({
59
60
  {
60
61
  id,
61
62
  label,
63
+ describedBy: describedBy ?? (description === void 0 ? void 0 : `${id}-description`),
62
64
  withoutHeader: true,
63
65
  lightDismiss,
64
66
  closeLabel,
@@ -88,6 +90,7 @@ function WorkbenchDialog({
88
90
  {
89
91
  text: title,
90
92
  description,
93
+ descriptionId: description === void 0 ? void 0 : `${id}-description`,
91
94
  size: "xlarge",
92
95
  id: `${id}-title`,
93
96
  headingLevel: 2
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/surfaces/workbench-dialog/workbench-dialog.tsx"],"names":[],"mappings":"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","file":"workbench-dialog.js","sourcesContent":["import { X } from 'lucide';\n\nimport type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport type { CssLength } from '../../../shared/styles/css-values.js';\nimport { remify } from '../../../shared/styles/css-values.js';\nimport { ToolbarControlGroup } from '../../actions/toolbar-control-group/toolbar-control-group.js';\nimport { ToolbarText } from '../../actions/toolbar-text/toolbar-text.js';\nimport {\n Workbench,\n type WorkbenchMainBottomToolbar,\n type WorkbenchPanel,\n} from '../../layout/workbench/workbench.js';\nimport { LucideIcon } from '../../media/lucide-icon/lucide-icon.js';\nimport {\n DialogSurface,\n type DialogSurfaceHeight,\n type DialogSurfaceMaxHeight,\n type DialogSurfaceRootAttributes,\n type DialogSurfaceSize,\n} from '../surface-scaffold/surface-scaffold.js';\n\n// Open and close it like any DialogSurface, from the same import.\nexport {\n closeDialog,\n type DialogCloseSource,\n type DialogRequestCloseDetail,\n openDialog,\n} from '../surface-scaffold/surface-scaffold.js';\n\n/**\n * Application `data-*` identity on the dialog root, such as `data-key`; the\n * dialog's own configuration attributes stay protected.\n */\nexport type WorkbenchDialogRootAttributes = DialogSurfaceRootAttributes;\n\nexport interface WorkbenchDialogProps {\n /**\n * The dialog's id, for `openDialog(document.getElementById(id))`. The\n * Workbench inside is `<id>-workbench`, so its rails are\n * `<id>-workbench-left-rail` and `<id>-workbench-right-rail`.\n */\n id: string;\n /** The dialog's accessible name. */\n label: string;\n /** The dialog's primary title: an xl heading in the work area's toolbar. */\n title: string;\n /** A short supporting line under the title. */\n description?: string;\n /** Groups after the title in the work area's toolbar. */\n leading?: KerfUiContent;\n /** Header controls before the close control, such as a `ToolbarControlGroup`. */\n actions?: KerfUiContent;\n /** The work area. */\n main: KerfUiContent;\n /** Fixed content over the work area's bottom toolbar, such as a status line. */\n mainFooter?: KerfUiContent;\n /** The work area's bottom toolbar, usually the dialog's buttons. */\n mainBottomToolbar?: WorkbenchMainBottomToolbar;\n /**\n * Side rails with their own toolbars. On compact devices each presents as\n * an overlay over the work area; the app starts it collapsed and its\n * `toolbar.toggle` shows it.\n */\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n /** A bottom drawer under the work area. */\n bottomDrawer?: WorkbenchPanel;\n /**\n * Fill the screen with the rails as overlays: derive from\n * `deviceClass().value.compact`.\n */\n compact?: boolean;\n /** The close control's accessible name (default `Close`). */\n closeLabel?: string;\n /** Close on a press on the backdrop; otherwise the panel pulses. */\n lightDismiss?: boolean;\n /** Roomy preferred width preset (default `large`). */\n size?: DialogSurfaceSize;\n /** Roomy preferred width; takes precedence over `size`. */\n preferredWidth?: CssLength;\n /** Roomy definite height (default 560px), bounded by the viewport. */\n height?: DialogSurfaceHeight;\n /** Roomy height cap in place of a definite height. */\n maxHeight?: DialogSurfaceMaxHeight;\n /** Roomy edge clearance from the viewport. */\n viewportGutter?: CssLength;\n /** Minimum work-area width beside inline rails, in px (Workbench default 320). */\n mainMinSize?: number;\n rootAttributes?: WorkbenchDialogRootAttributes;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/** A rail presents as an overlay in a compact dialog, as the app left it otherwise. */\nfunction compactRail(\n panel: WorkbenchPanel | undefined,\n compact: boolean,\n): WorkbenchPanel | undefined {\n return panel && compact ? { ...panel, presentation: 'overlay' } : panel;\n}\n\n/**\n * A workbench dialog: a modal whose body is a `Workbench`. The work area's\n * toolbar carries the dialog's primary title, its actions, and the close\n * control; side rails keep their own toolbars and run the dialog's full\n * height. On compact devices it fills the screen and its rails become\n * overlays over the work area, shown by their toolbar toggles, so the work\n * area keeps its width. Open it with `openDialog()`; the app owns each\n * rail's `collapsed` state, and `wireWorkbench` closes an overlay rail on\n * Escape or a press outside it before the dialog sees the Escape. Built from\n * `DialogSurface` and `Workbench`.\n */\nexport function WorkbenchDialog({\n id,\n label,\n title,\n description,\n leading,\n actions,\n main,\n mainFooter,\n mainBottomToolbar,\n leftRail,\n rightRail,\n bottomDrawer,\n compact = false,\n closeLabel = 'Close',\n lightDismiss = false,\n size = 'large',\n preferredWidth,\n height,\n maxHeight,\n viewportGutter,\n mainMinSize,\n rootAttributes,\n className = '',\n slot,\n}: WorkbenchDialogProps) {\n return (\n <DialogSurface\n id={id}\n label={label}\n withoutHeader\n lightDismiss={lightDismiss}\n closeLabel={closeLabel}\n size={size}\n presentation={compact ? 'fullscreen' : 'modal'}\n preferredWidth={preferredWidth}\n height={\n compact\n ? undefined\n : maxHeight === undefined\n ? (height ?? remify(560))\n : height\n }\n maxHeight={compact ? undefined : maxHeight}\n viewportGutter={compact ? undefined : viewportGutter}\n bodyInset=\"none\"\n bodyLayout=\"fill\"\n rootAttributes={{\n ...rootAttributes,\n 'data-dialog-layout': 'workbench',\n }}\n className={className}\n slot={slot}\n >\n <Workbench\n id={`${id}-workbench`}\n label={label}\n mainToolbar={{\n label: title,\n title: (\n <ToolbarText\n text={title}\n description={description}\n size=\"xlarge\"\n id={`${id}-title`}\n headingLevel={2}\n />\n ),\n leading,\n trailing: actions,\n // After any hidden right rail's toggle, so Close stays outermost.\n end: (\n <ToolbarControlGroup appearance=\"borderless\" single>\n <button\n type=\"button\"\n aria-label={closeLabel}\n data-dialog-close=\"\"\n >\n <LucideIcon icon={X} name=\"x\" />\n </button>\n </ToolbarControlGroup>\n ),\n }}\n main={main}\n mainFooter={mainFooter}\n mainBottomToolbar={mainBottomToolbar}\n leftRail={compactRail(leftRail, compact)}\n rightRail={compactRail(rightRail, compact)}\n bottomDrawer={bottomDrawer}\n mainMinSize={mainMinSize}\n />\n </DialogSurface>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/surfaces/workbench-dialog/workbench-dialog.tsx"],"names":[],"mappings":"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","file":"workbench-dialog.js","sourcesContent":["import { X } from 'lucide';\n\nimport type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport type { CssLength } from '../../../shared/styles/css-values.js';\nimport { remify } from '../../../shared/styles/css-values.js';\nimport { ToolbarControlGroup } from '../../actions/toolbar-control-group/toolbar-control-group.js';\nimport { ToolbarText } from '../../actions/toolbar-text/toolbar-text.js';\nimport {\n Workbench,\n type WorkbenchMainBottomToolbar,\n type WorkbenchPanel,\n} from '../../layout/workbench/workbench.js';\nimport { LucideIcon } from '../../media/lucide-icon/lucide-icon.js';\nimport {\n DialogSurface,\n type DialogSurfaceHeight,\n type DialogSurfaceMaxHeight,\n type DialogSurfaceRootAttributes,\n type DialogSurfaceSize,\n} from '../surface-scaffold/surface-scaffold.js';\n\n// Open and close it like any DialogSurface, from the same import.\nexport {\n closeDialog,\n type DialogCloseSource,\n type DialogRequestCloseDetail,\n openDialog,\n} from '../surface-scaffold/surface-scaffold.js';\n\n/**\n * Application `data-*` identity on the dialog root, such as `data-key`; the\n * dialog's own configuration attributes stay protected.\n */\nexport type WorkbenchDialogRootAttributes = DialogSurfaceRootAttributes;\n\nexport interface WorkbenchDialogProps {\n /**\n * The dialog's id, for `openDialog(document.getElementById(id))`. The\n * Workbench inside is `<id>-workbench`, so its rails are\n * `<id>-workbench-left-rail` and `<id>-workbench-right-rail`.\n */\n id: string;\n /** The dialog's accessible name. */\n label: string;\n /** The dialog's primary title: an xl heading in the work area's toolbar. */\n title: string;\n /**\n * A short supporting line under the title. It is the dialog's accessible\n * description (`aria-describedby` to its `<id>-description`) unless\n * `describedBy` names other content.\n */\n description?: string;\n /**\n * Id of the element whose text is the dialog's accessible description, in\n * place of `description`, such as app-owned summary copy in `main`.\n */\n describedBy?: string;\n /** Groups after the title in the work area's toolbar. */\n leading?: KerfUiContent;\n /** Header controls before the close control, such as a `ToolbarControlGroup`. */\n actions?: KerfUiContent;\n /** The work area. */\n main: KerfUiContent;\n /** Fixed content over the work area's bottom toolbar, such as a status line. */\n mainFooter?: KerfUiContent;\n /** The work area's bottom toolbar, usually the dialog's buttons. */\n mainBottomToolbar?: WorkbenchMainBottomToolbar;\n /**\n * Side rails with their own toolbars. On compact devices each presents as\n * an overlay over the work area; the app starts it collapsed and its\n * `toolbar.toggle` shows it.\n */\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n /** A bottom drawer under the work area. */\n bottomDrawer?: WorkbenchPanel;\n /**\n * Fill the screen with the rails as overlays: derive from\n * `deviceClass().value.compact`.\n */\n compact?: boolean;\n /** The close control's accessible name (default `Close`). */\n closeLabel?: string;\n /** Close on a press on the backdrop; otherwise the panel pulses. */\n lightDismiss?: boolean;\n /** Roomy preferred width preset (default `large`). */\n size?: DialogSurfaceSize;\n /** Roomy preferred width; takes precedence over `size`. */\n preferredWidth?: CssLength;\n /** Roomy definite height (default 560px), bounded by the viewport. */\n height?: DialogSurfaceHeight;\n /** Roomy height cap in place of a definite height. */\n maxHeight?: DialogSurfaceMaxHeight;\n /** Roomy edge clearance from the viewport. */\n viewportGutter?: CssLength;\n /** Minimum work-area width beside inline rails, in px (Workbench default 320). */\n mainMinSize?: number;\n rootAttributes?: WorkbenchDialogRootAttributes;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/** A rail presents as an overlay in a compact dialog, as the app left it otherwise. */\nfunction compactRail(\n panel: WorkbenchPanel | undefined,\n compact: boolean,\n): WorkbenchPanel | undefined {\n return panel && compact ? { ...panel, presentation: 'overlay' } : panel;\n}\n\n/**\n * A workbench dialog: a modal whose body is a `Workbench`. The work area's\n * toolbar carries the dialog's primary title, its actions, and the close\n * control; side rails keep their own toolbars and run the dialog's full\n * height. On compact devices it fills the screen and its rails become\n * overlays over the work area, shown by their toolbar toggles, so the work\n * area keeps its width. Open it with `openDialog()`; the app owns each\n * rail's `collapsed` state, and `wireWorkbench` closes an overlay rail on\n * Escape or a press outside it before the dialog sees the Escape. Built from\n * `DialogSurface` and `Workbench`.\n */\nexport function WorkbenchDialog({\n id,\n label,\n title,\n description,\n describedBy,\n leading,\n actions,\n main,\n mainFooter,\n mainBottomToolbar,\n leftRail,\n rightRail,\n bottomDrawer,\n compact = false,\n closeLabel = 'Close',\n lightDismiss = false,\n size = 'large',\n preferredWidth,\n height,\n maxHeight,\n viewportGutter,\n mainMinSize,\n rootAttributes,\n className = '',\n slot,\n}: WorkbenchDialogProps) {\n return (\n <DialogSurface\n id={id}\n label={label}\n describedBy={\n describedBy ??\n (description === undefined ? undefined : `${id}-description`)\n }\n withoutHeader\n lightDismiss={lightDismiss}\n closeLabel={closeLabel}\n size={size}\n presentation={compact ? 'fullscreen' : 'modal'}\n preferredWidth={preferredWidth}\n height={\n compact\n ? undefined\n : maxHeight === undefined\n ? (height ?? remify(560))\n : height\n }\n maxHeight={compact ? undefined : maxHeight}\n viewportGutter={compact ? undefined : viewportGutter}\n bodyInset=\"none\"\n bodyLayout=\"fill\"\n rootAttributes={{\n ...rootAttributes,\n 'data-dialog-layout': 'workbench',\n }}\n className={className}\n slot={slot}\n >\n <Workbench\n id={`${id}-workbench`}\n label={label}\n mainToolbar={{\n label: title,\n title: (\n <ToolbarText\n text={title}\n description={description}\n descriptionId={\n description === undefined ? undefined : `${id}-description`\n }\n size=\"xlarge\"\n id={`${id}-title`}\n headingLevel={2}\n />\n ),\n leading,\n trailing: actions,\n // After any hidden right rail's toggle, so Close stays outermost.\n end: (\n <ToolbarControlGroup appearance=\"borderless\" single>\n <button\n type=\"button\"\n aria-label={closeLabel}\n data-dialog-close=\"\"\n >\n <LucideIcon icon={X} name=\"x\" />\n </button>\n </ToolbarControlGroup>\n ),\n }}\n main={main}\n mainFooter={mainFooter}\n mainBottomToolbar={mainBottomToolbar}\n leftRail={compactRail(leftRail, compact)}\n rightRail={compactRail(rightRail, compact)}\n bottomDrawer={bottomDrawer}\n mainMinSize={mainMinSize}\n />\n </DialogSurface>\n );\n}\n"]}
package/dist/workbench.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Workbench } from './chunk-DDSCVEO5.js';
1
+ export { Workbench } from './chunk-CPSGUGAY.js';
2
2
 
3
3
 
4
4
 
@@ -364,7 +364,16 @@ holds an optional icon `ToolbarControlGroup` and a direct extra-large
364
364
  `ToolbarText`; controls belong in a trailing `ToolbarControlGroup`. By default
365
365
  the title carries no native heading role, so for a **dialog or panel** the
366
366
  application connects the title id and optional supporting-copy id to the owning
367
- host through `aria-labelledby` and `aria-describedby`. For a **page or view**
367
+ host through `aria-labelledby` and `aria-describedby`. `DialogSurface` and
368
+ `PopoverDialogSurface` take these as `labelledBy` and `describedBy`, so the
369
+ supporting line (or app-owned summary copy in the body) is the dialog's
370
+ accessible description; `WorkbenchDialog` wires its title `description`
371
+ automatically (`<id>-description`), and `ListDetailDialog` takes an explicit
372
+ `describedBy`, since its detail description describes the selection, not the
373
+ dialog. A `DialogSurface` that asks before a destructive action ("Delete this
374
+ topic?") sets `alert`, which renders `role="alertdialog"` on the native
375
+ `<dialog>`; pair it with `describedBy` on the message so it is announced, and
376
+ leave `lightDismiss` off so the person answers it. For a **page or view**
368
377
  title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
369
378
  `role="heading"` with a matching `aria-level`, giving the view a heading landmark
370
379
  so screen-reader heading navigation and "main heading" semantics work — the same
@@ -473,7 +482,9 @@ controls; Enter and Space use native button activation. The application handles
473
482
  the supplied `data-action`, reads `data-segment-value`, updates `value`, and
474
483
  re-renders. Keep labels unique and meaningful even when `content` shows only an
475
484
  icon. The filled track uses the normal neutral text role for readable unselected
476
- labels in light and dark themes. Use tabs—not a segmented control—when choices switch page regions that
485
+ labels in light and dark themes. A whole-control `disabled` disables every
486
+ native button and marks the group `aria-disabled` while `aria-pressed` still
487
+ reports the current choice. Use tabs—not a segmented control—when choices switch page regions that
477
488
  need `tab`/`tabpanel` semantics.
478
489
 
479
490
  ## ChoiceChips
@@ -533,6 +544,12 @@ its label; the input itself reports `required`. The focus ring is drawn on the
533
544
  box around the input (`:focus-within`), 3px with a 1px offset like every Kerf
534
545
  control.
535
546
 
547
+ For `type="number"`, `min`, `max`, and `step` pass through to the native input.
548
+ Each accepts a number or string; `step="any"` allows arbitrary decimals.
549
+ They are omitted for other input types and `multiline` textareas. Native
550
+ constraint validation (`rangeUnderflow`, `rangeOverflow`, and `stepMismatch`)
551
+ then applies without custom field wiring.
552
+
536
553
  `clearable` adds a native `<button>` named by `clearLabel` (default "Clear")
537
554
  after the input. It is hidden while the field is empty, is a normal Tab stop,
538
555
  and with `wireTextFields(root)` empties the field, dispatches bubbling `input`
@@ -542,6 +559,36 @@ disabled or read-only field's clear button is disabled. `showCount` (with
542
559
  `wireTextFields` keeps current. Compact text grows to 16px on coarse-pointer
543
560
  touch screens so focusing a field never zooms the page in iOS.
544
561
 
562
+ ## Button and SplitButton focus
563
+
564
+ `Button` and each `SplitButton` half draw the 3px focus ring 1px outside the
565
+ box, against the surrounding surface, because a loud fill can share the ring's
566
+ hue (the brand fill matches the focus color in both schemes). An ancestor that
567
+ clips overflow would crop that ring, so `focusRingInset` draws it inside the
568
+ box instead, the name and meaning `Select` uses. An inset ring alone would
569
+ touch the fill (1.0:1 on brand, at most 2.3:1 on any loud fill), so the inset
570
+ ring is two-tone: the ring color on the outer 3px and a 3px surface-colored
571
+ line inside it. The ring then meets a surface color on both edges and clears
572
+ 3:1 against each on every tone (about 4.0:1 in light, 9.9:1 in dark against
573
+ the line), whatever the fill. Forced-colors mode drops the line and keeps the
574
+ system ring.
575
+
576
+ The line paints the surface the control sits on, not always the base surface:
577
+ a surface that changes the background behind its children publishes the
578
+ surface context (`--kui-color-surface-current`, `-region`, and `-tint`; see
579
+ `docs/component-contract.md`), and the line layers it the same way the page
580
+ does. On a sunken panel, a quiet `ContentItem`, a lowered `FramedList`, a
581
+ raised `List`, popup, or dialog, a highlighted `FramedList` row, a sunken
582
+ Pane, Workbench area, or TabNavigator scene, or a Pane or TabNavigator app
583
+ `background`, the line matches the pixels outside the box, so it reads as a
584
+ cut through the fill instead of a light halo. On a doubly sunken surface the
585
+ brand ring still clears 3:1 against the line (about 3.2:1 in light, 6.9:1 in
586
+ dark). A sunken surface's grain is not part of the line. Translucent glass
587
+ chrome (an overlay header, bar, or bottom toolbar, a dialog's floating
588
+ header) has no single color over scrolling content, so the line paints the
589
+ material over the surface beneath it: exact at rest, an approximation while
590
+ content scrolls under the bar.
591
+
545
592
  ## Checkbox, RadioGroup, and Switch
546
593
 
547
594
  Each control is a native input inside its `<label>`, positioned invisibly over
@@ -563,6 +610,13 @@ forced-colors mode paints checked state with `Highlight`.
563
610
  its `aria-description`; `error` sets `aria-invalid` on the group, `required`
564
611
  sets native `required` on each radio plus `aria-required` on the group, and a
565
612
  choice's `hint` is that radio's `aria-description`.
613
+ - `CheckboxGroup` is a `<fieldset>` (natively a group) of `Checkbox`es, named
614
+ by its `<legend>` (`label`), `ariaLabel`, or `labelledBy`. Each checkbox
615
+ is its own Tab stop and Space toggles it; every checked value submits under
616
+ `name`. The group's `hint` and `error` form its `aria-description`; `error`
617
+ sets `aria-invalid` on the group and the danger border on each box, and a
618
+ choice's `hint` is that checkbox's `aria-description`. `disabled` disables
619
+ the fieldset and so, natively, every checkbox in it.
566
620
  - `Switch` is an `<input type="checkbox" role="switch">`, so assistive
567
621
  technology announces on and off; Space toggles it. Its `hint` is its
568
622
  `aria-description`. The thumb's slide is skipped when reduced motion is
@@ -747,7 +801,8 @@ the Tab order while collapsed or not inline. `wireWorkbench`, given a panel's
747
801
  `collapsed` signal, returns focus stranded in a closing panel to the control that
748
802
  opened it, else the restore control, else a control whose `aria-controls` names the
749
803
  panel; an open overlay panel takes focus and keeps Tab inside it (the ARIA dialog
750
- pattern). See [`workbench.md`](workbench.md).
804
+ pattern), whether its own toggle or a persistent toggle outside the Workbench
805
+ opened it, as soon as the panel can take focus. See [`workbench.md`](workbench.md).
751
806
 
752
807
  ## Chrome material
753
808
 
@@ -26,7 +26,12 @@ help that leaves focus on its trigger. Escape and an outside press dismiss it;
26
26
  `placement` (`bottom` or `top`), `align` (`start` or `end`), `gap`, and
27
27
  `viewportMargin` feed the shared `kerfjs/overlay` positioning core: it flips
28
28
  vertically when the other side fits, keeps the surface `viewportMargin` (default
29
- 8px) inside the viewport edges, and follows scroll and resize. The surface fits
29
+ 8px) inside the viewport edges, and follows scroll, resize, and changes to the
30
+ anchor's own size. Anchor help for a field to the whole field rather than to a
31
+ button inside it: the surface then clears every line of a field that wraps, and
32
+ moves as the field grows (a SearchField's trailing help action opens beside
33
+ `[data-component="search-field"]`, which also keeps typing in the field from
34
+ dismissing it). The surface fits
30
35
  its height to the room beside the anchor: when neither side holds it, it opens
31
36
  on the roomier side, caps its height to that room, and scrolls its content.
32
37
  The room is exposed to the content as `--kui-anchored-surface-available-height`
package/docs/catalog.md CHANGED
@@ -219,7 +219,32 @@ for a control whose own inline padding contributes about 8px, and
219
219
  8px inset (such as a plain `PopupMenu` trigger), so its text lines up with the
220
220
  example label past the trigger's own padding and border. Use
221
221
  `align="none"` (the default) for a content item or composition that owns its
222
- geometry.
222
+ geometry. Every form field specimen (TextField, Select, ColorField, TagField,
223
+ Slider, Switch, Checkbox, RadioGroup, CheckboxGroup, ColorSwatches) uses
224
+ `inline-control`, so field borders and label text share one column across the
225
+ catalog; a field that already insets itself by 8px (Combobox) stays at `none`.
226
+ A field is a control a person fills in or picks a value with inline, swatch
227
+ pickers included. A field composition (fields in a `Grid` or a form `List`)
228
+ uses `inline-control` too. A width-sized content specimen — one that fills a
229
+ sized `viewport` or its container rather than its content, such as a
230
+ ProgressBar, a group-title `Text`, or Web Awesome's Progress Bar and Skeleton —
231
+ joins the same column with `inline-control`, so a bar's label lines up with the
232
+ example label and note text. Web Awesome integration specimens follow their
233
+ Kerf counterpart's demo: controls and control compositions (Button, Button
234
+ Group, Dropdown, Dropdown Item, the Tab family, Badge, Tag, Avatar, Progress
235
+ Ring, Toast and Toast Item, the dialog, drawer, popover, and tooltip triggers,
236
+ Animation, and the intersection, mutation, and resize observer compositions)
237
+ use `inline-control`, as the Kerf Button demo does, so the control box sits on
238
+ the 8px column; a specimen led by bare text or a bare glyph (Copy Button,
239
+ Breadcrumb and Breadcrumb Item, Divider, Markdown, Relative Time, Pagination's
240
+ summary, Stepper and Step markers, Spinner, Icon) uses `glyph`, as Kerf's flush
241
+ `Text`, `LoadingSpinner`, and `LucideIcon` demos do, so its text lands on the
242
+ text column instead of flush at the example's edge. A panel — a multi-part picker or
243
+ a drop zone such as ColorPicker and FilePicker, or a framed specimen such as
244
+ Web Awesome's Zoomable Frame — owns its geometry and stays flush at `none`, as
245
+ does an edge-to-edge composition (a toolbar, tab bar, or pane). A sized
246
+ `viewport` takes the alignment offset out of its width, so it still ends at the
247
+ example's inline-end edge.
223
248
 
224
249
  ```tsx
225
250
  import { CatalogExample, CatalogExampleStack } from "@kerfjs/ui/catalog";
@@ -3,8 +3,15 @@
3
3
  `@kerfjs/ui/color-picker` is a full inline color picker panel:
4
4
 
5
5
  - a saturation and brightness area;
6
- - a hue slider and, optionally, an opacity slider on a checkerboard;
7
- - a color-model menu (RGB, HSL, HSB, Grayscale) with its channel fields;
6
+ - a slider for each channel of the color model — red, green, and blue (RGB);
7
+ hue, saturation, and lightness (HSL); hue, saturation, and brightness
8
+ (HSB); or one gray level (Grayscale) — each track painting the colors that
9
+ channel produces while the others hold, except the hue track, which always
10
+ shows the full-saturation, full-brightness rainbow so a dark or gray color
11
+ still shows every hue; and, optionally, an opacity slider on a
12
+ checkerboard;
13
+ - a color-model menu (RGB, HSL, HSB, Grayscale) that switches the channel
14
+ sliders and fields together;
8
15
  - a hex field (8 digits below full opacity) and an opacity field;
9
16
  - a screen sampler where the browser offers the EyeDropper API;
10
17
  - optional preset and sample swatches.
@@ -28,7 +35,8 @@ const stop = wireColorPickers(root, {
28
35
  - `id` and `label` (required): identity and the picker's accessible name.
29
36
  - `value` (required): CSS color text (`#rrggbb[aa]`, `rgb()`, `hsl()`);
30
37
  invalid text shows black.
31
- - `mode`: `rgb` (default), `hsl`, `hsb`, or `gray` — which channel fields show.
38
+ - `mode`: `rgb` (default), `hsl`, `hsb`, or `gray` — which channel sliders
39
+ and fields show.
32
40
  - `alpha` (default true): the opacity slider and field. Without it the value
33
41
  is always opaque.
34
42
  - `name`, `form`: a hidden input submits the hex value.
@@ -49,7 +57,9 @@ picker with a new `value` (or `mode`), the picker adopts it.
49
57
 
50
58
  - **The area** takes pointer drags and the arrow keys (Shift for 10% steps);
51
59
  Page Up and Down change brightness, Home and End saturation.
52
- - **Hue and opacity** are native range inputs.
60
+ - **Channel and opacity sliders** are native range inputs (arrow keys, Page
61
+ Up and Down, Home and End). Each changes only its own channel; a slider and
62
+ the field of the same channel always agree.
53
63
  - **Fields** commit on Enter or blur; Escape restores the shown value; invalid
54
64
  text restores it too.
55
65
  - **Swatches** (presets and samples) apply their color.
@@ -59,13 +69,34 @@ Each change dispatches a bubbling `kui-color-input` (live) or
59
69
  `onInput` or `onChange`. The value is `#rrggbb`, or `#rrggbbaa` below full
60
70
  opacity. The picker keeps hue, saturation, and brightness as its own state,
61
71
  so a hue survives while the color is gray, and conversions round-trip without
62
- drift.
72
+ drift: switching models never changes the color, and a channel edit keeps
73
+ every other channel's exact value (an HSL saturation survives lightness
74
+ reaching black or white). The 2D area stays in every model as the primary
75
+ picking surface; the sliders are the precise, per-channel controls.
63
76
 
64
77
  ## Accessibility and geometry
65
78
 
66
79
  The picker is a named group. The area is a slider named "Saturation and
67
- Brightness" whose spoken value gives both; every field is named (Red, Hue,
68
- Hex Color, Opacity). The panel takes the shared block-content inset, fills
69
- its container up to 320px, and spaces its rows 12px apart.
80
+ Brightness" whose spoken value gives both. Each channel slider is named for
81
+ its channel (Red, Hue, Lightness, Gray Level) and speaks its unit (`210°`,
82
+ `40%`, or a plain 0–255 byte); the opacity slider speaks a percentage. Every
83
+ field is named the same way (Red, Hue, Hex Color, Opacity). The panel takes
84
+ the shared block-content inset, fills its container up to 320px, and spaces
85
+ its rows 12px apart; the sliders form one group 8px apart.
86
+
87
+ The saturation and brightness area and every slider track draw a 1px inner
88
+ edge halfway between the text color and the color under it, so an area or a
89
+ track as dark as a dark surface (the area's black bottom, a hue at low
90
+ brightness, a faint opacity) or as light as a light one keeps a boundary of at
91
+ least 3:1 against the surface around it. Increased contrast draws the edge in
92
+ the full text color and forced colors in `CanvasText`.
93
+
94
+ Every thumb draws a two-tone ring: a white border inside a dark outline. The
95
+ white holds at least 3:1 against a dark color and the outline against a light
96
+ one, so a thumb stays findable wherever it sits.
97
+
98
+ Every track runs in the writing direction, as the native range does: in
99
+ right-to-left text the gradient starts at the right, so the thumb at a
100
+ channel's minimum (or at no opacity) sits over the color it sets.
70
101
 
71
102
  Use `ColorSwatches` for a small fixed palette.
@@ -65,6 +65,14 @@ diagnostic even when both the wrapper and a rendered root reject that parent.
65
65
  When one catalog entry exports another JSX component with different placement,
66
66
  `jsxExports` gives that export its own parent contract. The extra export does
67
67
  not stand for the entry's root when a parent zone is checked.
68
+ An entry whose own geometry an owner already provides in some configuration
69
+ declares `insetFreeChildren`: `{ prop, equals }` conditions under which its
70
+ direct JSX children must bring no content-item inset (`Disclosure` with
71
+ `appearance="embedded"`). A child brings that inset when its
72
+ `layout.geometry` owns its margin, border, and padding (`self` on all three),
73
+ unless one of its own `insetFreeWhen` conditions holds (`Text` with `flush` or
74
+ `variant="span"`). `ui-composition` reports a child that statically brings the
75
+ inset as `KUI-L204` and names the child's inset-free form when it has one.
68
76
  Generated `boundaries.rootClass` prefers the public `kui-<entry-id>` class when
69
77
  present, so a sibling export's class cannot become the entry's root class.
70
78
 
@@ -411,6 +419,30 @@ a `Pane` overlay header, through `wireScrollDividers`, on its own Pane root).
411
419
  A scroll owner reads it only through its own `scrollClearance` prop, or, for an
412
420
  overlay-header Pane's content, through its own `headerPresentation`.
413
421
 
422
+ The surface context needs no exception either. A component that changes the
423
+ background behind its children publishes, on its own painted element, the
424
+ foundation tokens `--kui-color-surface-current` (the opaque surface beneath:
425
+ a raised `DialogSurface`, popover dialog, `AnchoredSurface`, or `List`
426
+ `surface`, and a sunken Pane's opaque auto chrome, set it and clear both
427
+ layers), `--kui-color-surface-current-region` (a layout region's fill: a sunken
428
+ `Pane`, `Workbench` area, or `TabNavigator` scene, and a `Pane`'s or
429
+ `TabNavigator`'s app `background`), and `--kui-color-surface-current-tint` (a
430
+ content surface's fill: a `SunkenPanel`, a sunken `Disclosure`, a lowered
431
+ `FramedList` and its highlighted row, a quiet, hovered, pressed, or selected
432
+ `ContentItem`, a `TabBar` strip, and translucent glass chrome at rest — a
433
+ `Pane` overlay header, `NavStack` overlay chrome or bottom toolbar, a
434
+ `TabNavigator` overlay bar, and a dialog's floating header; clear chrome
435
+ paints nothing and keeps the inherited context). Interactive rows (`ListItem`,
436
+ `ListActionRow`, `SummaryRow`) are buttons or links themselves, so no control
437
+ nests on their hover or selection fill and they publish nothing. They are undeclared at `:root`
438
+ and a scoped `[data-kui-tint]` resets them. A control that must match what
439
+ lies behind it (the inset focus ring's inner line on `Button` and
440
+ `SplitButton`) layers tint over region over the opaque color, falling back to
441
+ `--kui-color-surface`. Custom properties cannot fold an inherited value into a
442
+ new one, so a content surface inside a sunken region composes exactly while a
443
+ third translucent level keeps only the innermost content tint. An application
444
+ surface that paints its own background sets the same tokens on its own root.
445
+
414
446
  Applications and component packages built on the package follow the same
415
447
  rules, and the downstream tools enforce them against the composition catalog
416
448
  instead of package source: `kerf-ui-analyze` / `kerf-ui-doctor` report a rule
@@ -609,7 +641,14 @@ preferences remove that entrance animation.
609
641
  Use `visibility="yield-to-expanded-sibling"` on trailing groups that should
610
642
  leave a 480px-or-narrower toolbar while a sibling trailing group is expanded.
611
643
  They return when it collapses or the toolbar widens; leading and center zones
612
- remain visible.
644
+ remain visible. In a `responsive="trailing-priority"` Toolbar, use
645
+ `visibility="yield-to-priority-row"` instead: the group leaves exactly while
646
+ the expanded trailing control has its own row, from the same container query
647
+ as the row at the Toolbar's `responsiveAt` (832px for `narrow`, 704px for
648
+ `compact`, both inclusive). Do not restate that width with `hideBelow`, which
649
+ hides strictly below its threshold: at exactly 704px the row is active while
650
+ `hideBelow={rem(44)}` still shows the group, which then wraps onto a third
651
+ row.
613
652
  A group's controls are plain `<button>`s (a standalone `Button` belongs outside a group); a popup menu is a `PopupMenu`, which renders its own
614
653
  trigger. By default a group stays on one row: each icon sits beside its label,
615
654
  and the enclosing `Toolbar`'s responsive policy relocates whole groups. Set
@@ -624,14 +663,19 @@ compact segment never becomes an oval. An icon-only `Select` joins a group as
624
663
  one more segment with its siblings' inset, radius, hover, and focus ring; only a
625
664
  lone Select grows the group to its icon-and-caret pill.
626
665
 
627
- Select menus use native viewport shifting with 10px gutters: default menus grow to content (up to 20rem), while `fitMenu` matches trigger width and long labels wrap. Keep popup sizing and position repairs out of consumer CSS.
666
+ Select menus use native viewport shifting with 10px gutters: default menus grow to content (up to 20rem), while `fitMenu` matches trigger width and long labels wrap. A listbox (like a PopupMenu menu) shifts only along its trigger's edge, never over the trigger: when neither side fits, it opens on the side with more room and scrolls inside its capped height, so a second press on the trigger still closes it. Keep popup sizing and position repairs out of consumer CSS.
628
667
 
629
668
  For a rail view heading, use `Select presentation="title"`: its intrinsic
630
669
  36px borderless trigger has a bold large label flush with its leading edge and
631
670
  a caret beside that label. Set `focusRingInset` when a NavStack or panel clips
632
671
  outside painting; the Select keeps its focus outline 3px inside the trigger
633
672
  and drops it while the listbox is open. Both settings belong to Select, not an
634
- application `::part()` override.
673
+ application `::part()` override. `Button` and `SplitButton` take the same
674
+ `focusRingInset` for a clipping ancestor: the ring moves inside the box (each
675
+ half's, for a SplitButton) with a line inside it in the color of the surface
676
+ it sits on (the surface context), so it stays visible on a loud fill of its
677
+ own hue without a halo on a sunken or tinted surface. Never re-offset their outline from
678
+ application CSS.
635
679
 
636
680
  Set `menuInset` to configure the menu surface
637
681
  without application `::part(menu)` CSS; the default `standard` keeps each menu's