@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
@@ -344,6 +344,9 @@
344
344
  "--kui-color-surface-lowered",
345
345
  "--kui-sunken-texture",
346
346
  "--kui-sunken-surface",
347
+ "--kui-color-surface-current",
348
+ "--kui-color-surface-current-region",
349
+ "--kui-color-surface-current-tint",
347
350
  "--kui-chrome-material",
348
351
  "--kui-chrome-material-raised",
349
352
  "--kui-chrome-material-filter",
@@ -412,7 +415,8 @@
412
415
  "--kui-focus-ring",
413
416
  "--kui-shadow-s",
414
417
  "--kui-shadow-m",
415
- "--kui-shadow-l"
418
+ "--kui-shadow-l",
419
+ "--kui-color-brand"
416
420
  ]
417
421
  },
418
422
  "diagnostics": [],
@@ -2055,7 +2059,7 @@
2055
2059
  "obligations": [
2056
2060
  "DialogSurface is a native <dialog> opened modally (showModal): it is in the top layer, the rest of the page is inert, focus stays inside, and it returns to the opener on close. Focus starts on an autofocus element, otherwise on the panel.",
2057
2061
  "Name it with label (also its title text) or labelledBy (the id of a heading, such as a ToolbarText). The close button is named Close (closeLabel) and Escape asks to close; the cancelable kui-request-close event reports source close-button, escape, or backdrop.",
2058
- "Name a PopoverDialogSurface with label or labelledBy, and describe it with describedBy (rendered as aria-describedby), usually a title ToolbarText descriptionId.",
2062
+ "Name a PopoverDialogSurface with label or labelledBy. Describe either surface with describedBy (rendered as aria-describedby on the dialog root), usually the heading ToolbarText descriptionId or app-owned summary copy in the body; rootAttributes accepts only data-* attributes, so it never sets aria-describedby.",
2059
2063
  "A floating header's title uses the normal text color, which keeps WCAG AA over the 80% raised chrome material on any content; the material turns opaque under prefers-reduced-transparency, prefers-contrast: more, forced colors, or without backdrop-filter.",
2060
2064
  "A resizable bottom sheet's grabber is a focusable horizontal separator (\"Resize sheet\") with aria-valuemin, aria-valuenow, and aria-valuemax in visible px; the arrow keys, Page Up/Down, Home, and End resize it, and its slide motion is skipped under prefers-reduced-motion.",
2061
2065
  "Use popup surfaces only for real dropdown menus with named triggers."
@@ -2290,7 +2294,7 @@
2290
2294
  "accessibility": {
2291
2295
  "obligations": [
2292
2296
  "It is a native modal <dialog> named by label: the page is inert, focus stays inside, Escape asks to close, and focus returns to the opener on close.",
2293
- "On roomy devices the detail title is a level-2 heading; every presentation keeps a close control named Close (closeLabel), and the compact back control is named by backLabel."
2297
+ "Pass describedBy, the id of app-owned summary copy, for the dialog's accessible description; the detail description describes the selection and is not wired. On roomy devices the detail title is a level-2 heading; every presentation keeps a close control named Close (closeLabel), and the compact back control is named by backLabel."
2294
2298
  ]
2295
2299
  },
2296
2300
  "cssValueProps": [],
@@ -2389,7 +2393,7 @@
2389
2393
  "accessibility": {
2390
2394
  "obligations": [
2391
2395
  "It is a native modal <dialog> named by label: the page is inert, focus stays inside, Escape asks to close, and focus returns to the opener on close.",
2392
- "The title is a level-2 heading; the close control is named Close (closeLabel) and stays the outermost trailing control. An open overlay rail takes focus, keeps Tab inside it, and closes on Escape before the dialog does."
2396
+ "The title is a level-2 heading, and its description (id <id>-description) is the dialog's accessible description unless describedBy names other content; the close control is named Close (closeLabel) and stays the outermost trailing control. An open overlay rail takes focus, keeps Tab inside it, and closes on Escape before the dialog does."
2393
2397
  ]
2394
2398
  },
2395
2399
  "cssValueProps": [],
@@ -2653,8 +2657,12 @@
2653
2657
  "diagnostics": [],
2654
2658
  "provenance": {
2655
2659
  "selection": "ai/component-catalog.json",
2656
- "composition": "generated-permissive-default"
2657
- }
2660
+ "composition": "docs/disclosure.md#embedded-in-a-content-item"
2661
+ },
2662
+ "insetFreeWhen": [
2663
+ { "prop": "flush", "equals": true },
2664
+ { "prop": "variant", "equals": "span" }
2665
+ ]
2658
2666
  },
2659
2667
  {
2660
2668
  "key": "@kerfjs/ui:headers",
@@ -4148,7 +4156,8 @@
4148
4156
  "border": "self",
4149
4157
  "padding": "self",
4150
4158
  "notes": [
4151
- "The root takes the content-item geometry (8px inline margin, a 1px border, 8px padding); the content starts on the title's text column."
4159
+ "The root takes the content-item geometry (8px inline margin, a 1px border, 8px padding); the content starts on the title's text column.",
4160
+ "appearance embedded drops that geometry inside a ContentItem, which already owns it: the arrow sits on the item's content inset, the summary row is at least 24px tall, and the content (a flush Text or other content without an inset) starts on the title's text column 8px below the summary. Do not use embedded outside owner chrome."
4152
4161
  ]
4153
4162
  }
4154
4163
  },
@@ -4173,8 +4182,9 @@
4173
4182
  "diagnostics": [],
4174
4183
  "provenance": {
4175
4184
  "selection": "ai/component-catalog.json",
4176
- "composition": "generated-permissive-default"
4177
- }
4185
+ "composition": "docs/disclosure.md#embedded-in-a-content-item"
4186
+ },
4187
+ "insetFreeChildren": [{ "prop": "appearance", "equals": "embedded" }]
4178
4188
  },
4179
4189
  {
4180
4190
  "key": "@kerfjs/ui:application-tabs",
@@ -4747,14 +4757,17 @@
4747
4757
  "border": "self",
4748
4758
  "padding": "self",
4749
4759
  "notes": [
4750
- "The panel takes the shared block-content inset and a 320px maximum width; its rows are 12px apart."
4760
+ "The panel takes the shared block-content inset and a 320px maximum width; its rows are 12px apart and its sliders form one group 8px apart."
4751
4761
  ]
4752
4762
  }
4753
4763
  },
4754
4764
  "accessibility": {
4755
4765
  "obligations": [
4756
4766
  "The area is a slider named Saturation and Brightness with a spoken value; the arrow keys move it (Shift for 10% steps), Page Up and Down change brightness, Home and End saturation.",
4757
- "Hue and opacity are native range inputs; every field is named (Red, Hue, Hex Color, Opacity); the picker is a named group."
4767
+ "Every channel slider and the opacity slider is a native range input named for its channel (Red, Hue, Lightness, Gray Level, Opacity) with a spoken value in the channel's unit (210°, 40%, or a 0–255 byte); every field is named (Red, Hue, Hex Color, Opacity); the picker is a named group.",
4768
+ "The saturation and brightness area and every slider track carry a 1px inner edge halfway to the text color, so an area or track as dark or as light as the surface around it keeps at least 3:1 non-text contrast against it; increased contrast draws the edge in the full text color and forced colors in CanvasText.",
4769
+ "Every thumb draws a two-tone ring, a white border inside a dark outline, so it keeps at least 3:1 non-text contrast against light and dark area and track colors alike.",
4770
+ "Every slider track runs in the writing direction, as the native range does: in right-to-left text its gradient starts at the right, under the thumb at the channel's minimum."
4758
4771
  ]
4759
4772
  },
4760
4773
  "cssValueProps": [],
@@ -4765,7 +4778,8 @@
4765
4778
  "kui-color-picker__surface",
4766
4779
  "kui-color-picker__area",
4767
4780
  "kui-color-picker__area-thumb",
4768
- "kui-color-picker__hue",
4781
+ "kui-color-picker__sliders",
4782
+ "kui-color-picker__slider",
4769
4783
  "kui-color-picker__opacity",
4770
4784
  "kui-color-picker__controls",
4771
4785
  "kui-color-picker__mode-wrap",
@@ -4884,8 +4898,15 @@
4884
4898
  "contexts": ["chat-composer", "dialog-floating-footer"],
4885
4899
  "zones": [
4886
4900
  {
4887
- "id": "actions",
4888
- "jsx": { "prop": "actions" },
4901
+ "id": "leading",
4902
+ "jsx": { "prop": "leading" },
4903
+ "accepts": ["KerfUiContent"],
4904
+ "cardinality": { "min": 0, "max": "unbounded" },
4905
+ "exclusiveWith": []
4906
+ },
4907
+ {
4908
+ "id": "trailing",
4909
+ "jsx": { "prop": "trailing" },
4889
4910
  "accepts": ["KerfUiContent"],
4890
4911
  "cardinality": { "min": 1, "max": "unbounded" },
4891
4912
  "exclusiveWith": []
@@ -5547,7 +5568,7 @@
5547
5568
  "border": "self",
5548
5569
  "padding": "none",
5549
5570
  "notes": [
5550
- "A 20px box (18px compact) 8px (7px) before its label; hint and error align with the label text."
5571
+ "A 20px box (18px compact) 8px (7px) before its label; hint and error sit directly under the label, on its text column, like a grouped choice hint. Stack standalone Checkboxes in a List gap=\"xs\" (8px), the same 28px pitch as RadioGroup choices."
5551
5572
  ]
5552
5573
  }
5553
5574
  },
@@ -5592,6 +5613,82 @@
5592
5613
  "composition": "generated-permissive-default"
5593
5614
  }
5594
5615
  },
5616
+ {
5617
+ "key": "@kerfjs/ui:checkbox-group",
5618
+ "package": "@kerfjs/ui",
5619
+ "id": "checkbox-group",
5620
+ "name": "CheckboxGroup",
5621
+ "kind": "component",
5622
+ "source": "kerf",
5623
+ "parents": { "mode": "any", "entries": [] },
5624
+ "contexts": ["application-ui"],
5625
+ "zones": [],
5626
+ "children": { "mode": "any", "concepts": [], "requiredConcepts": [] },
5627
+ "state": [
5628
+ { "id": "checked values", "owner": "application", "required": false },
5629
+ {
5630
+ "id": "choices and their copy",
5631
+ "owner": "application",
5632
+ "required": false
5633
+ },
5634
+ {
5635
+ "id": "validation and error copy",
5636
+ "owner": "application",
5637
+ "required": false
5638
+ }
5639
+ ],
5640
+ "wiring": {
5641
+ "required": false,
5642
+ "helpers": [],
5643
+ "obligations": [],
5644
+ "stateAttributes": []
5645
+ },
5646
+ "responsive": { "owner": "not-applicable", "behaviors": [] },
5647
+ "layout": {
5648
+ "roles": ["controls"],
5649
+ "geometry": {
5650
+ "margin": "none",
5651
+ "border": "none",
5652
+ "padding": "self",
5653
+ "notes": [
5654
+ "Laid out exactly like a RadioGroup: the label and hint sit on the text column (1px + 8px); each choice's 20px box (18px compact) sits on the group's inline-start edge 8px (7px) before its label; choices stack 8px apart (--kui-space-xs), the same 28px pitch as RadioGroup choices, wrap with a 24px column gap when horizontal, and a choice hint sits directly under its label text."
5655
+ ]
5656
+ }
5657
+ },
5658
+ "accessibility": {
5659
+ "obligations": [
5660
+ "A native <fieldset> (a group) named by its legend (label), ariaLabel, or labelledBy; each choice is a Kerf Checkbox, a native checkbox inside its <label>, invisibly over the drawn box. Every checkbox is its own Tab stop and Space toggles it; each checked value submits under name.",
5661
+ "The group's hint and error form its aria-description; an error sets aria-invalid on the group and the danger border on each box. A choice's hint is that checkbox's aria-description. disabled disables the fieldset, and so every checkbox, natively."
5662
+ ]
5663
+ },
5664
+ "cssValueProps": [],
5665
+ "boundaries": {
5666
+ "rootClass": "kui-checkbox-group",
5667
+ "publicClasses": [
5668
+ "kui-checkbox-group",
5669
+ "kui-checkbox-group__label",
5670
+ "kui-checkbox-group__choices",
5671
+ "kui-checkbox-group__hint",
5672
+ "kui-checkbox-group__error"
5673
+ ],
5674
+ "rootElement": "fieldset",
5675
+ "publicTokens": [
5676
+ "--kui-color-neutral-on-quiet",
5677
+ "--kui-color-text-quiet",
5678
+ "--kui-color-danger-on-quiet",
5679
+ "--kui-font-m",
5680
+ "--kui-font-s",
5681
+ "--kui-font-xs",
5682
+ "--kui-layout-item-padding",
5683
+ "--kui-space-xs"
5684
+ ]
5685
+ },
5686
+ "diagnostics": [],
5687
+ "provenance": {
5688
+ "selection": "ai/component-catalog.json",
5689
+ "composition": "generated-permissive-default"
5690
+ }
5691
+ },
5595
5692
  {
5596
5693
  "key": "@kerfjs/ui:radio-group",
5597
5694
  "package": "@kerfjs/ui",
@@ -5630,7 +5727,7 @@
5630
5727
  "border": "none",
5631
5728
  "padding": "self",
5632
5729
  "notes": [
5633
- "The label and hint sit on the text column (1px + 8px); the choices sit on the 8px control column, 12px apart."
5730
+ "The label and hint sit on the text column (1px + 8px); each choice's 20px circle (18px compact) sits on the group's inline-start edge, where a Checkbox draws its box, 8px (7px) before its label; choices stack 8px apart (--kui-space-xs), the same 28px pitch as Checkboxes in a List gap=\"xs\", and a choice hint aligns with its label text."
5634
5731
  ]
5635
5732
  }
5636
5733
  },
@@ -5671,7 +5768,8 @@
5671
5768
  "--kui-font-s",
5672
5769
  "--kui-font-xs",
5673
5770
  "--kui-color-neutral-on-quiet",
5674
- "--kui-layout-item-padding"
5771
+ "--kui-layout-item-padding",
5772
+ "--kui-space-xs"
5675
5773
  ]
5676
5774
  },
5677
5775
  "diagnostics": [],
@@ -5713,7 +5811,7 @@
5713
5811
  "border": "self",
5714
5812
  "padding": "none",
5715
5813
  "notes": [
5716
- "A 35×20 track (31.5×18 compact) 8px (7px) before its label; the hint aligns with the label text."
5814
+ "A 35×20 track (31.5×18 compact) 8px (7px) before its label; the hint sits directly under the label, on its text column, like a Checkbox hint."
5717
5815
  ]
5718
5816
  }
5719
5817
  },
@@ -5995,8 +6093,8 @@
5995
6093
  },
5996
6094
  "accessibility": {
5997
6095
  "obligations": [
5998
- "A native <button> (type=\"button\" unless set) or, with href, an <a>; its label names it, or ariaLabel when the label is not enough. Focus shows the standard focus ring.",
5999
- "Loading shows a spinner in place of the label, which still names the button, and sets aria-busy=\"true\". A disabled link drops its href and reports aria-disabled=\"true\".",
6096
+ "A native <button> (type=\"button\" unless set) or, with href, an <a>; its label names it, or ariaLabel when the label is not enough. Focus shows the standard focus ring 1px outside the box; with focusRingInset it is drawn inside the box, two-tone with a surface-colored line inside the ring color, so it keeps 3:1 contrast on a loud fill of its own hue.",
6097
+ "Loading shows a spinner in place of the label, which still names the button, and sets aria-busy=\"true\". A disabled link drops its href and reports aria-disabled=\"true\". Disabled success uses a quiet success fill and readable on-fill text instead of fading the label.",
6000
6098
  "With pressed, it is a toggle: aria-pressed reports the state, and the pressed look adds a doubled outline (secondary and plain) or a darkened fill with an inset ring (primary), so the state never relies on hue alone; forced-colors mode paints it Highlight. An icon-only Button is named by ariaLabel, and help tags show that name on hover and keyboard focus. A title gives native hover text that says more than the name and replaces the help tag."
6001
6099
  ]
6002
6100
  },
@@ -6021,6 +6119,11 @@
6021
6119
  "--kui-color-brand-fill-quiet",
6022
6120
  "--kui-color-brand-on-quiet",
6023
6121
  "--kui-color-brand-border-loud",
6122
+ "--kui-color-success-fill-loud",
6123
+ "--kui-color-success-on-loud",
6124
+ "--kui-color-success-fill-quiet",
6125
+ "--kui-color-success-on-fill",
6126
+ "--kui-color-success-border-loud",
6024
6127
  "--kui-color-danger-fill-loud",
6025
6128
  "--kui-color-danger-on-loud",
6026
6129
  "--kui-color-danger-fill-quiet",
@@ -6079,7 +6182,8 @@
6079
6182
  },
6080
6183
  "accessibility": {
6081
6184
  "obligations": [
6082
- "Both halves are native buttons in a group; the options trigger exposes aria-haspopup, aria-expanded, and optional aria-controls, and needs optionsLabel. busy sets aria-busy on the primary."
6185
+ "Both halves are native buttons in a group; the options trigger exposes aria-haspopup, aria-expanded, and optional aria-controls, and needs optionsLabel. busy sets aria-busy on the primary.",
6186
+ "Each half draws the focus ring 1px outside its box; with focusRingInset it is drawn inside, two-tone with a surface-colored line inside the ring color, so it keeps 3:1 contrast on every loud fill."
6083
6187
  ]
6084
6188
  },
6085
6189
  "cssValueProps": [],
@@ -6105,6 +6209,7 @@
6105
6209
  "--kui-color-danger-on-loud",
6106
6210
  "--kui-focus-ring",
6107
6211
  "--kui-font-weight-semibold",
6212
+ "--kui-layout-pill-radius",
6108
6213
  "--kui-layout-rounded-radius",
6109
6214
  "--kui-space-s",
6110
6215
  "--kui-space-xs"
@@ -169,6 +169,18 @@
169
169
  "minItems": 1,
170
170
  "uniqueItems": true
171
171
  },
172
+ "insetFreeChildren": {
173
+ "description": "Prop values under which the entry's direct JSX children must bring no content-item inset. While any condition holds, a child whose layout.geometry owns its margin, border, and padding (self on all three) double-insets the entry's own geometry unless one of the child's insetFreeWhen conditions holds; eslint-plugin-kerfjs reports it as KUI-L204.",
174
+ "type": "array",
175
+ "items": { "$ref": "#/$defs/propCondition" },
176
+ "minItems": 1
177
+ },
178
+ "insetFreeWhen": {
179
+ "description": "Prop values that render this inset-bearing entry with no margin, border, or padding of its own (Text flush). Any one condition makes the element inset-free.",
180
+ "type": "array",
181
+ "items": { "$ref": "#/$defs/propCondition" },
182
+ "minItems": 1
183
+ },
172
184
  "rendersAs": {
173
185
  "description": "For a wrapper component: the cataloged roots it renders (package-qualified keys), any one of them or nothing. Composition checks treat an element of this entry as each declared root: a zone accepts it only if it accepts every root, and each root's parent contract applies to where it is placed.",
174
186
  "type": "array",
@@ -369,6 +381,26 @@
369
381
  },
370
382
  "additionalProperties": false
371
383
  },
384
+ "propCondition": {
385
+ "type": "object",
386
+ "required": ["prop", "equals"],
387
+ "properties": {
388
+ "prop": {
389
+ "type": "string",
390
+ "pattern": "^[A-Za-z][A-Za-z0-9]*$",
391
+ "description": "A JSX prop of the element."
392
+ },
393
+ "equals": {
394
+ "anyOf": [
395
+ { "type": "string" },
396
+ { "type": "number" },
397
+ { "type": "boolean" }
398
+ ],
399
+ "description": "The statically written value that satisfies the condition. A bare boolean prop (<Text flush />) equals true."
400
+ }
401
+ },
402
+ "additionalProperties": false
403
+ },
372
404
  "cssValueProp": {
373
405
  "type": "object",
374
406
  "required": [
@@ -73,12 +73,13 @@ Exact prop and callback types live in [`public-api-signatures-v1.md`](../public-
73
73
  - [Combobox](./combobox.md) — Let people pick an existing value or create a new one by typing (tags, labels, recipients), with the themed field chrome, the ARIA combobox pattern, and a touch-safe font size, without a native datalist or app CSS.
74
74
  - [TextField](./text-field.md) — Collect free text (names, addresses, notes, comments) with Kerf's field chrome, a visible or accessible label, supporting and validation text, and native input semantics.
75
75
  - [Checkbox](./checkbox.md) — Turn one independent option on or off in a form or settings list, including a parent whose children are partly checked, without Web Awesome.
76
+ - [CheckboxGroup](./checkbox-group.md) — Choose any number of a few related options that should stay visible under one group label, with native keyboard behavior and form participation.
76
77
  - [RadioGroup](./radio-group.md) — Choose exactly one of a few options that should stay visible with room for a label and hint each, with native keyboard behavior and form participation.
77
78
  - [Switch](./switch.md) — Turn a setting on or off the moment it changes, such as live updates or a preference, without Web Awesome.
78
79
  - [Slider](./slider.md) — Let a person pick a value from a range where the relative position matters more than an exact number (volume, zoom, opacity, a threshold).
79
80
  - [PopupMenu](./popup-menu.md) — A typed action menu with nested choices, selected state, and optional pointer-anchored context opening, drawn by Kerf as a native menu in the top layer.
80
81
  - [Button](./button.md) — Trigger an action from a dialog footer, a form, or content outside any Toolbar, with Kerf's button chrome and native button semantics.
81
- - [SplitButton](./split-button.md) — Render a prominent primary action plus an options trigger (provider, model, target) as one control with a semantic tone, full-width sizing, a busy presentation, and native aria-expanded on the trigger, outside any Toolbar.
82
+ - [SplitButton](./split-button.md) — Render a prominent primary action plus an options trigger (provider, model, target) as one control with a semantic tone, full-width sizing, a rounded or pill shape, a busy presentation, and native aria-expanded on the trigger, outside any Toolbar.
82
83
  - [FilePicker](./file-picker.md) — Let people choose or drop files (attachments, evidence, imports) without app CSS: a link-toned browse trigger and a dashed drop target that highlights while dragging, both around visually hidden native file inputs.
83
84
 
84
85
  ## Kerf UI — Feedback
@@ -107,7 +108,7 @@ Exact prop and callback types live in [`public-api-signatures-v1.md`](../public-
107
108
  ## Web Awesome — Forms
108
109
 
109
110
  - [Checkbox](./wa-checkbox.md) (avoid) — Ecosystem checkbox; use Kerf Checkbox.
110
- - [Checkbox Group](./wa-checkbox-group.md) (avoid) — Ecosystem checkbox group; use Kerf Checkbox rows or ChoiceChips.
111
+ - [Checkbox Group](./wa-checkbox-group.md) (avoid) — Ecosystem checkbox group; use Kerf CheckboxGroup or ChoiceChips.
111
112
  - [Color Picker](./wa-color-picker.md) (avoid) — Ecosystem color picker; use the Kerf ColorField or ColorPicker.
112
113
  - [Input](./wa-input.md) (avoid) — Ecosystem text input; use Kerf TextField.
113
114
  - [Known Date](./wa-known-date.md) — Experimental segmented field for partially or fully known dates.
@@ -4,15 +4,16 @@
4
4
 
5
5
  `@kerfjs/ui:button` · Kerf UI · component · Controls
6
6
 
7
- A standalone action button: a native button (or a link with href) in a neutral, brand, or danger tone and a primary, secondary, or plain emphasis, at the 44px control height or compact.
7
+ A standalone action button: a native button (or a link with href) in a neutral, brand, success, or danger tone and a primary, secondary, or plain emphasis, at the 44px control height or compact.
8
8
 
9
9
  ## When to use
10
10
 
11
11
  Trigger an action from a dialog footer, a form, or content outside any Toolbar, with Kerf's button chrome and native button semantics.
12
12
 
13
- - A dialog footer, a form, or content needs an action button: tone="brand" for the main action, tone="danger" for a destructive one, and emphasis="secondary" or "plain" for the quieter actions beside it.
13
+ - A dialog footer, a form, or content needs an action button: tone="brand" for the main action, tone="success" for a positive action such as a pricing check, tone="danger" for a destructive one, and emphasis="secondary" or "plain" for the quieter actions beside it.
14
14
  - A form submits or resets: type="submit" or "reset" (the default type never submits), with name, value, and form when needed.
15
15
  - An action navigates: href renders a link with the same chrome.
16
+ - A single primary action spans its form, sheet, or narrow column: fill makes it span the parent's available inline width, label centered, as SplitButton's fill does.
16
17
 
17
18
  **Not when:**
18
19
 
@@ -37,10 +38,11 @@ Exact prop names and types: [`@kerfjs/ui/button`](../public-api-signatures-v1.md
37
38
 
38
39
  **Variants (configure, do not restyle):**
39
40
 
40
- - neutral, brand, or danger tone
41
+ - neutral, brand, success, or danger tone
41
42
  - primary, secondary, or plain emphasis
42
43
  - default or compact size
43
44
  - rounded (default) or pill shape
45
+ - content width (default) or fill: spans the parent's available inline width, label centered and ellipsized when it no longer fits
44
46
  - leading icon
45
47
  - icon-only square (icon and ariaLabel, no label)
46
48
  - pressed toggle (aria-pressed) on any tone and emphasis
@@ -48,6 +50,7 @@ Exact prop names and types: [`@kerfjs/ui/button`](../public-api-signatures-v1.md
48
50
  - disabled
49
51
  - link (href)
50
52
  - badge (a count, short text, or true for a dot) over the corner, named through badgeLabel, with badgeTone
53
+ - focusRingInset: the focus ring drawn inside the box for an ancestor that clips overflow
51
54
 
52
55
  ## Composition
53
56
 
@@ -68,8 +71,8 @@ Margin: none · border: self · padding: self (layout role: controls). `self` me
68
71
 
69
72
  ## Accessibility
70
73
 
71
- - A native \<button> (type="button" unless set) or, with href, an \<a>; its label names it, or ariaLabel when the label is not enough. Focus shows the standard focus ring.
72
- - Loading shows a spinner in place of the label, which still names the button, and sets aria-busy="true". A disabled link drops its href and reports aria-disabled="true".
74
+ - A native \<button> (type="button" unless set) or, with href, an \<a>; its label names it, or ariaLabel when the label is not enough. Focus shows the standard focus ring 1px outside the box; with focusRingInset it is drawn inside the box, two-tone with a surface-colored line inside the ring color, so it keeps 3:1 contrast on a loud fill of its own hue.
75
+ - Loading shows a spinner in place of the label, which still names the button, and sets aria-busy="true". A disabled link drops its href and reports aria-disabled="true". Disabled success uses a quiet success fill and readable on-fill text instead of fading the label.
73
76
  - With pressed, it is a toggle: aria-pressed reports the state, and the pressed look adds a doubled outline (secondary and plain) or a darkened fill with an inset ring (primary), so the state never relies on hue alone; forced-colors mode paints it Highlight. An icon-only Button is named by ariaLabel, and help tags show that name on hover and keyboard focus. A title gives native hover text that says more than the name and replaces the help tag.
74
77
 
75
78
  ## Styling boundary
@@ -80,7 +83,7 @@ Public class hooks (select for layout placement only, never to change the compon
80
83
 
81
84
  Never put `kui-button`, `kui-button__icon`, `kui-button__label`, `kui-button__spinner` on an element you write; render `Button` instead (`KUI-L103`).
82
85
 
83
- Public tokens it reads: `--kui-color-neutral-fill-loud`, `--kui-color-neutral-on-loud`, `--kui-color-neutral-fill-quiet`, `--kui-color-neutral-on-quiet`, `--kui-color-neutral-border-loud`, `--kui-color-brand-fill-loud`, `--kui-color-brand-on-loud`, `--kui-color-brand-fill-quiet`, `--kui-color-brand-on-quiet`, `--kui-color-brand-border-loud`, `--kui-color-danger-fill-loud`, `--kui-color-danger-on-loud`, `--kui-color-danger-fill-quiet`, `--kui-color-danger-on-quiet`, `--kui-color-danger-border-loud`, `--kui-focus-ring`, `--kui-font-m`, `--kui-font-s`, `--kui-layout-rounded-radius`. Set a token only where its public contract allows; prefer a prop.
86
+ Public tokens it reads: `--kui-color-neutral-fill-loud`, `--kui-color-neutral-on-loud`, `--kui-color-neutral-fill-quiet`, `--kui-color-neutral-on-quiet`, `--kui-color-neutral-border-loud`, `--kui-color-brand-fill-loud`, `--kui-color-brand-on-loud`, `--kui-color-brand-fill-quiet`, `--kui-color-brand-on-quiet`, `--kui-color-brand-border-loud`, `--kui-color-success-fill-loud`, `--kui-color-success-on-loud`, `--kui-color-success-fill-quiet`, `--kui-color-success-on-fill`, `--kui-color-success-border-loud`, `--kui-color-danger-fill-loud`, `--kui-color-danger-on-loud`, `--kui-color-danger-fill-quiet`, `--kui-color-danger-on-quiet`, `--kui-color-danger-border-loud`, `--kui-focus-ring`, `--kui-font-m`, `--kui-font-s`, `--kui-layout-rounded-radius`. Set a token only where its public contract allows; prefer a prop.
84
87
 
85
88
  Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-*` selectors (`KUI-L001`, `KUI-L010`), unknown or private tokens (`KUI-L002`, `KUI-L012`), and any application rule that restyles the component, forced dimensions included (`KUI-L019`).
86
89
 
@@ -0,0 +1,89 @@
1
+ # CheckboxGroup
2
+
3
+ <!-- Generated by scripts/sync-component-docs.mjs from ai/component-catalog.json and ai/component-composition.json. Do not edit; run `npm run catalog:sync`. -->
4
+
5
+ `@kerfjs/ui:checkbox-group` · Kerf UI · component · Controls
6
+
7
+ A native fieldset of Kerf Checkboxes with the group label above, per-choice hints, and hint and error text below, stacked or wrapping like a RadioGroup.
8
+
9
+ ## When to use
10
+
11
+ Choose any number of a few related options that should stay visible under one group label, with native keyboard behavior and form participation.
12
+
13
+ - A form asks for any of two to about eight related options under one label, each with a readable label and an optional hint, and every checked value submits under one name.
14
+
15
+ **Not when:**
16
+
17
+ - Do not use for one choice among several; use RadioGroup.
18
+ - Do not use for a single independent option or a parent with a mixed state; use Checkbox.
19
+ - Do not use for settings that apply the moment they change; use Switch.
20
+ - Do not use for many short tag-like or wrapping pill choices; use ChoiceChips multiple.
21
+
22
+ **Alternatives:**
23
+
24
+ - [Checkbox Group](./wa-checkbox-group.md) (prefer-over)
25
+ - [Checkbox](./checkbox.md) (nearest) — Use Checkbox for one independent option, or a mixed parent above its children.
26
+ - [RadioGroup](./radio-group.md) (nearest) — Use RadioGroup when exactly one option may be chosen.
27
+ - [ChoiceChips](./choice-chips.md) (nearest) — Use ChoiceChips multiple for wrapping pill choices.
28
+
29
+ ## Imports
30
+
31
+ - JavaScript: `@kerfjs/ui/checkbox-group` — exports `CheckboxGroup`.
32
+ - CSS: the browser build of `@kerfjs/ui/checkbox-group` pulls its CSS automatically; import `@kerfjs/ui/checkbox-group.css` manually only without the `browser` export condition.
33
+ - Declared side effects: browser-condition-css.
34
+
35
+ ## Props
36
+
37
+ Exact prop names and types: [`@kerfjs/ui/checkbox-group`](../public-api-signatures-v1.md#kerfjsuicheckbox-group) in `public-api-signatures-v1.md`. Do not infer props from examples.
38
+
39
+ **Variants (configure, do not restyle):**
40
+
41
+ - vertical or horizontal
42
+ - per-choice hint
43
+ - disabled choice
44
+ - whole group disabled
45
+ - hint
46
+ - error
47
+ - default or compact size
48
+
49
+ ## Composition
50
+
51
+ - Parents: any.
52
+ - Contexts: `application-ui`.
53
+ - Children: unrestricted.
54
+ - Built from: [Checkbox](./checkbox.md).
55
+
56
+ ## State and wiring
57
+
58
+ **The app owns:** checked values; choices and their copy; validation and error copy.
59
+
60
+ No wiring helper.
61
+
62
+ ## Geometry
63
+
64
+ Margin: none · border: none · padding: self (layout role: controls). `self` means the component already owns it — do not add wrapper padding, margin, or borders around it.
65
+
66
+ - Laid out exactly like a RadioGroup: the label and hint sit on the text column (1px + 8px); each choice's 20px box (18px compact) sits on the group's inline-start edge 8px (7px) before its label; choices stack 8px apart (--kui-space-xs), the same 28px pitch as RadioGroup choices, wrap with a 24px column gap when horizontal, and a choice hint sits directly under its label text.
67
+
68
+ ## Accessibility
69
+
70
+ - A native \<fieldset> (a group) named by its legend (label), ariaLabel, or labelledBy; each choice is a Kerf Checkbox, a native checkbox inside its \<label>, invisibly over the drawn box. Every checkbox is its own Tab stop and Space toggles it; each checked value submits under name.
71
+ - The group's hint and error form its aria-description; an error sets aria-invalid on the group and the danger border on each box. A choice's hint is that checkbox's aria-description. disabled disables the fieldset, and so every checkbox, natively.
72
+
73
+ ## Styling boundary
74
+
75
+ The component owns its own styles. Configure it through its props and variants; do not override its internals.
76
+
77
+ Public class hooks (select for layout placement only, never to change the component's look): `kui-checkbox-group`, `kui-checkbox-group__label`, `kui-checkbox-group__choices`, `kui-checkbox-group__hint`, `kui-checkbox-group__error`.
78
+
79
+ Never put `kui-checkbox-group`, `kui-checkbox-group__label`, `kui-checkbox-group__choices`, `kui-checkbox-group__hint`, `kui-checkbox-group__error` on an element you write; render `CheckboxGroup` instead (`KUI-L103`).
80
+
81
+ Public tokens it reads: `--kui-color-neutral-on-quiet`, `--kui-color-text-quiet`, `--kui-color-danger-on-quiet`, `--kui-font-m`, `--kui-font-s`, `--kui-font-xs`, `--kui-layout-item-padding`, `--kui-space-xs`. Set a token only where its public contract allows; prefer a prop.
82
+
83
+ Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-*` selectors (`KUI-L001`, `KUI-L010`), unknown or private tokens (`KUI-L002`, `KUI-L012`), and any application rule that restyles the component, forced dimensions included (`KUI-L019`).
84
+
85
+ ## Related
86
+
87
+ - Components: [Checkbox](./checkbox.md), [Checkbox Group](./wa-checkbox-group.md), [RadioGroup](./radio-group.md), [ChoiceChips](./choice-chips.md).
88
+ - Docs: [`docs/component-selection.md`](../../docs/component-selection.md), [`docs/component-selection.md#problem-to-component-matrix`](../../docs/component-selection.md#problem-to-component-matrix).
89
+ - UX catalog route: `?component=checkbox-group`.
@@ -22,6 +22,7 @@ Turn one independent option on or off in a form or settings list, including a pa
22
22
  **Alternatives:**
23
23
 
24
24
  - [Checkbox](./wa-checkbox.md) (prefer-over)
25
+ - [CheckboxGroup](./checkbox-group.md) (nearest) — Use CheckboxGroup for a labeled set of related options.
25
26
  - [Switch](./switch.md) (nearest) — Use Switch for a setting that applies immediately.
26
27
  - [ChoiceChips](./choice-chips.md) (nearest) — Use ChoiceChips multiple for several visible, wrapping choices.
27
28
 
@@ -63,7 +64,7 @@ Exact prop names and types: [`@kerfjs/ui/checkbox`](../public-api-signatures-v1.
63
64
 
64
65
  Margin: none · border: self · padding: none (layout role: controls). `self` means the component already owns it — do not add wrapper padding, margin, or borders around it.
65
66
 
66
- - A 20px box (18px compact) 8px (7px) before its label; hint and error align with the label text.
67
+ - A 20px box (18px compact) 8px (7px) before its label; hint and error sit directly under the label, on its text column, like a grouped choice hint. Stack standalone Checkboxes in a List gap="xs" (8px), the same 28px pitch as RadioGroup choices.
67
68
 
68
69
  ## Accessibility
69
70
 
@@ -84,6 +85,6 @@ Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-
84
85
 
85
86
  ## Related
86
87
 
87
- - Components: [LucideIcon](./lucide-icon.md), [Checkbox](./wa-checkbox.md), [Switch](./switch.md), [ChoiceChips](./choice-chips.md).
88
+ - Components: [LucideIcon](./lucide-icon.md), [Checkbox](./wa-checkbox.md), [CheckboxGroup](./checkbox-group.md), [Switch](./switch.md), [ChoiceChips](./choice-chips.md).
88
89
  - Docs: [`docs/component-selection.md`](../../docs/component-selection.md), [`docs/component-selection.md#problem-to-component-matrix`](../../docs/component-selection.md#problem-to-component-matrix).
89
90
  - UX catalog route: `?component=checkbox`.
@@ -38,6 +38,7 @@ Exact prop names and types: [`@kerfjs/ui/chip`](../public-api-signatures-v1.md#k
38
38
  - pill or rounded shape
39
39
  - compact or default size
40
40
  - removable, link, action, or plain, enabled or disabled
41
+ - rootAttributes: safe data-\* identity on the plain, link, or action root
41
42
 
42
43
  ## Composition
43
44
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  `@kerfjs/ui:color-picker` · Kerf UI · component · Controls
6
6
 
7
- A full inline color picker: a saturation and brightness area, hue and opacity sliders, an RGB/HSL/HSB/Grayscale model menu with channel fields, a hex field (8 digits below full opacity) and opacity field, a screen sampler where the EyeDropper API exists, and optional preset and sample swatches.
7
+ A full inline color picker: a saturation and brightness area, a slider per channel of the color model (red, green, and blue; hue, saturation, and lightness or brightness; or one gray level) whose tracks paint the colors each channel produces while the others hold (the hue track a fixed full-saturation rainbow), an opacity slider, an RGB/HSL/HSB/Grayscale model menu that switches the sliders and channel fields together, a hex field (8 digits below full opacity) and opacity field, a screen sampler where the EyeDropper API exists, and optional preset and sample swatches.
8
8
 
9
9
  ## When to use
10
10
 
@@ -25,7 +25,7 @@ Let a person choose any color precisely, by dragging, typing channel or hex valu
25
25
 
26
26
  ## Imports
27
27
 
28
- - JavaScript: `@kerfjs/ui/color-picker` — exports `ColorPicker`, `ColorPickerProps`, `ColorPickerMode`, `ColorPickerSamples`, `ColorPickerRootAttributes`, `colorPickerChannels`, `colorPickerAreaText`, `colorPickerStyle`.
28
+ - JavaScript: `@kerfjs/ui/color-picker` — exports `ColorPicker`, `ColorPickerProps`, `ColorPickerMode`, `ColorPickerSamples`, `ColorPickerRootAttributes`, `colorPickerChannels`, `colorPickerSlider`, `ColorPickerSliderState`, `colorPickerAreaText`, `colorPickerStyle`.
29
29
  - CSS: the browser build of `@kerfjs/ui/color-picker` pulls its CSS automatically; import `@kerfjs/ui/color-picker.css` manually only without the `browser` export condition.
30
30
  - Wiring: `wireColorPickers` from `@kerfjs/ui/wire-color-pickers` (required).
31
31
  - Declared side effects: browser-condition-css.
@@ -37,7 +37,7 @@ Exact prop names and types: [`@kerfjs/ui/color-picker`](../public-api-signatures
37
37
  **Variants (configure, do not restyle):**
38
38
 
39
39
  - with or without opacity
40
- - RGB, HSL, HSB, or Grayscale channel fields
40
+ - RGB, HSL, HSB, or Grayscale channel sliders and fields
41
41
  - presets and titled sample groups with an add button
42
42
  - screen sampler where the EyeDropper API exists
43
43
  - disabled
@@ -66,20 +66,23 @@ Exact prop names and types: [`@kerfjs/ui/color-picker`](../public-api-signatures
66
66
 
67
67
  Margin: none · border: self · padding: self (layout role: controls). `self` means the component already owns it — do not add wrapper padding, margin, or borders around it.
68
68
 
69
- - The panel takes the shared block-content inset and a 320px maximum width; its rows are 12px apart.
69
+ - The panel takes the shared block-content inset and a 320px maximum width; its rows are 12px apart and its sliders form one group 8px apart.
70
70
 
71
71
  ## Accessibility
72
72
 
73
73
  - The area is a slider named Saturation and Brightness with a spoken value; the arrow keys move it (Shift for 10% steps), Page Up and Down change brightness, Home and End saturation.
74
- - Hue and opacity are native range inputs; every field is named (Red, Hue, Hex Color, Opacity); the picker is a named group.
74
+ - Every channel slider and the opacity slider is a native range input named for its channel (Red, Hue, Lightness, Gray Level, Opacity) with a spoken value in the channel's unit (210°, 40%, or a 0–255 byte); every field is named (Red, Hue, Hex Color, Opacity); the picker is a named group.
75
+ - The saturation and brightness area and every slider track carry a 1px inner edge halfway to the text color, so an area or track as dark or as light as the surface around it keeps at least 3:1 non-text contrast against it; increased contrast draws the edge in the full text color and forced colors in CanvasText.
76
+ - Every thumb draws a two-tone ring, a white border inside a dark outline, so it keeps at least 3:1 non-text contrast against light and dark area and track colors alike.
77
+ - Every slider track runs in the writing direction, as the native range does: in right-to-left text its gradient starts at the right, under the thumb at the channel's minimum.
75
78
 
76
79
  ## Styling boundary
77
80
 
78
81
  The component owns its own styles. Configure it through its props and variants; do not override its internals.
79
82
 
80
- Public class hooks (select for layout placement only, never to change the component's look): `kui-color-picker`, `kui-color-picker__surface`, `kui-color-picker__area`, `kui-color-picker__area-thumb`, `kui-color-picker__hue`, `kui-color-picker__opacity`, `kui-color-picker__controls`, `kui-color-picker__mode-wrap`, `kui-color-picker__mode-icon`, `kui-color-picker__mode`, `kui-color-picker__tool`, `kui-color-picker__channels`, `kui-color-picker__hex-row`, `kui-color-picker__field`, `kui-color-picker__field-label`, `kui-color-picker__field--hex`.
83
+ Public class hooks (select for layout placement only, never to change the component's look): `kui-color-picker`, `kui-color-picker__surface`, `kui-color-picker__area`, `kui-color-picker__area-thumb`, `kui-color-picker__sliders`, `kui-color-picker__slider`, `kui-color-picker__opacity`, `kui-color-picker__controls`, `kui-color-picker__mode-wrap`, `kui-color-picker__mode-icon`, `kui-color-picker__mode`, `kui-color-picker__tool`, `kui-color-picker__channels`, `kui-color-picker__hex-row`, `kui-color-picker__field`, `kui-color-picker__field-label`, `kui-color-picker__field--hex`.
81
84
 
82
- Never put `kui-color-picker`, `kui-color-picker__surface`, `kui-color-picker__area`, `kui-color-picker__area-thumb`, `kui-color-picker__hue`, `kui-color-picker__opacity`, `kui-color-picker__controls`, `kui-color-picker__mode-wrap`, `kui-color-picker__mode-icon`, `kui-color-picker__mode`, `kui-color-picker__tool`, `kui-color-picker__channels`, `kui-color-picker__hex-row`, `kui-color-picker__field`, `kui-color-picker__field-label`, `kui-color-picker__field--hex` on an element you write; render `ColorPicker` instead (`KUI-L103`).
85
+ Never put `kui-color-picker`, `kui-color-picker__surface`, `kui-color-picker__area`, `kui-color-picker__area-thumb`, `kui-color-picker__sliders`, `kui-color-picker__slider`, `kui-color-picker__opacity`, `kui-color-picker__controls`, `kui-color-picker__mode-wrap`, `kui-color-picker__mode-icon`, `kui-color-picker__mode`, `kui-color-picker__tool`, `kui-color-picker__channels`, `kui-color-picker__hex-row`, `kui-color-picker__field`, `kui-color-picker__field-label`, `kui-color-picker__field--hex` on an element you write; render `ColorPicker` instead (`KUI-L103`).
83
86
 
84
87
  Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-*` selectors (`KUI-L001`, `KUI-L010`), unknown or private tokens (`KUI-L002`, `KUI-L012`), and any application rule that restyles the component, forced dimensions included (`KUI-L019`).
85
88