@keboola/design 11.2.0 → 11.4.0

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 (249) hide show
  1. package/AGENTS.md +42 -14
  2. package/CHANGELOG.md +424 -0
  3. package/COMPONENT-API.md +33 -32
  4. package/dist/{AIKit-DiS0CVOW.js → AIKit-DDg5DBCG.js} +156 -76
  5. package/dist/AIKit-DDg5DBCG.js.map +1 -0
  6. package/dist/{Alert-DfZq9kGe.js → Alert-DHJFrWnS.js} +3 -3
  7. package/dist/{Alert-DfZq9kGe.js.map → Alert-DHJFrWnS.js.map} +1 -1
  8. package/dist/{AlertModal-Dp_XuJBW.js → AlertModal-BGJdaRRj.js} +3 -3
  9. package/dist/{AlertModal-Dp_XuJBW.js.map → AlertModal-BGJdaRRj.js.map} +1 -1
  10. package/dist/{AppShell-B5GQN6Zs.js → AppShell-DRgJxpXW.js} +29 -10
  11. package/dist/AppShell-DRgJxpXW.js.map +1 -0
  12. package/dist/{BrandEditor-DyzgfVkl.js → BrandEditor-Go7w8Zj2.js} +7 -7
  13. package/dist/{BrandEditor-DyzgfVkl.js.map → BrandEditor-Go7w8Zj2.js.map} +1 -1
  14. package/dist/{BrandLogo-CJkRd0F8.js → BrandLogo-Dmi7jo21.js} +2 -2
  15. package/dist/{BrandLogo-CJkRd0F8.js.map → BrandLogo-Dmi7jo21.js.map} +1 -1
  16. package/dist/{Breadcrumb-Frst8MTA.js → Breadcrumb-uEkM6adI.js} +3 -3
  17. package/dist/{Breadcrumb-Frst8MTA.js.map → Breadcrumb-uEkM6adI.js.map} +1 -1
  18. package/dist/{Button-6xBX0nNl.js → Button-DsRrU5F1.js} +6 -5
  19. package/dist/Button-DsRrU5F1.js.map +1 -0
  20. package/dist/{Card-BAoZ35CV.js → Card-Dyo7055Q.js} +2 -2
  21. package/dist/{Card-BAoZ35CV.js.map → Card-Dyo7055Q.js.map} +1 -1
  22. package/dist/{Checkbox-GJ8-srt4.js → Checkbox-fC0BuO2B.js} +3 -3
  23. package/dist/{Checkbox-GJ8-srt4.js.map → Checkbox-fC0BuO2B.js.map} +1 -1
  24. package/dist/{Clipboard-CBJZiviP.js → Clipboard-ChPdiUy3.js} +2 -2
  25. package/dist/{Clipboard-CBJZiviP.js.map → Clipboard-ChPdiUy3.js.map} +1 -1
  26. package/dist/{ClipboardButton-ClcluT0C.js → ClipboardButton-D9pDjgkB.js} +3 -3
  27. package/dist/{ClipboardButton-ClcluT0C.js.map → ClipboardButton-D9pDjgkB.js.map} +1 -1
  28. package/dist/{CodeEditor-DyaN-Ij2.js → CodeEditor-Cxy3HnKH.js} +244 -113
  29. package/dist/CodeEditor-Cxy3HnKH.js.map +1 -0
  30. package/dist/{ColorInput-B8z4j4Y_.js → ColorInput-Bxj-1eLT.js} +3 -3
  31. package/dist/{ColorInput-B8z4j4Y_.js.map → ColorInput-Bxj-1eLT.js.map} +1 -1
  32. package/dist/{ColorModeSwitch-dZFZyqy1.js → ColorModeSwitch-CsWLiTBX.js} +3 -3
  33. package/dist/{ColorModeSwitch-dZFZyqy1.js.map → ColorModeSwitch-CsWLiTBX.js.map} +1 -1
  34. package/dist/{Combobox-Dgm6Z4ib.js → Combobox-Cs_1pj2q.js} +31 -24
  35. package/dist/Combobox-Cs_1pj2q.js.map +1 -0
  36. package/dist/{Command-C0aZfVTq.js → Command-B7ML9YMv.js} +6 -6
  37. package/dist/{Command-C0aZfVTq.js.map → Command-B7ML9YMv.js.map} +1 -1
  38. package/dist/{CopyField-BJ96zvXs.js → CopyField-BO_sh-HO.js} +2 -2
  39. package/dist/{CopyField-BJ96zvXs.js.map → CopyField-BO_sh-HO.js.map} +1 -1
  40. package/dist/{DateInput-B3UdzRfW.js → DateInput-ZdaMQo5i.js} +4 -4
  41. package/dist/{DateInput-B3UdzRfW.js.map → DateInput-ZdaMQo5i.js.map} +1 -1
  42. package/dist/{DatePicker-Dw3DpbUh.js → DatePicker-nA8YdE84.js} +2 -2
  43. package/dist/{DatePicker-Dw3DpbUh.js.map → DatePicker-nA8YdE84.js.map} +1 -1
  44. package/dist/{DateRangeInput-DTuySEiq.js → DateRangeInput-DkvNh_sT.js} +4 -4
  45. package/dist/{DateRangeInput-DTuySEiq.js.map → DateRangeInput-DkvNh_sT.js.map} +1 -1
  46. package/dist/{DateRangePicker-DCmMHg4s.js → DateRangePicker-DjSUFjh4.js} +6 -6
  47. package/dist/{DateRangePicker-DCmMHg4s.js.map → DateRangePicker-DjSUFjh4.js.map} +1 -1
  48. package/dist/{DropdownMenu-t90tVw6l.js → DropdownMenu-CASr-jgl.js} +2 -2
  49. package/dist/{DropdownMenu-t90tVw6l.js.map → DropdownMenu-CASr-jgl.js.map} +1 -1
  50. package/dist/{FieldItem-D-h1NeBo.js → FieldItem-CCd9-xnp.js} +3 -3
  51. package/dist/{FieldItem-D-h1NeBo.js.map → FieldItem-CCd9-xnp.js.map} +1 -1
  52. package/dist/{FieldItem-CAPj9lj3.d.ts → FieldItem-vhIJW4pj.d.ts} +2 -10
  53. package/dist/{FileUpload-BRv0TaZk.js → FileUpload-ABThVZyg.js} +2 -2
  54. package/dist/{FileUpload-BRv0TaZk.js.map → FileUpload-ABThVZyg.js.map} +1 -1
  55. package/dist/{Form-DHvcFoEi.js → Form-k6PH-em3.js} +8 -8
  56. package/dist/{Form-DHvcFoEi.js.map → Form-k6PH-em3.js.map} +1 -1
  57. package/dist/{FormGroup-C41n65cE.js → FormGroup-DbcDtrgU.js} +3 -3
  58. package/dist/{FormGroup-C41n65cE.js.map → FormGroup-DbcDtrgU.js.map} +1 -1
  59. package/dist/{FullScreenModal-Cb3PGgh4.js → FullScreenModal-BvPuYibe.js} +3 -3
  60. package/dist/{FullScreenModal-Cb3PGgh4.js.map → FullScreenModal-BvPuYibe.js.map} +1 -1
  61. package/dist/{HoverCard-WfDkLK1s.js → HoverCard-B9ZMJxjn.js} +2 -2
  62. package/dist/{HoverCard-WfDkLK1s.js.map → HoverCard-B9ZMJxjn.js.map} +1 -1
  63. package/dist/{InputGroup-CdTq8H7c.js → InputGroup-BnLHY0eq.js} +3 -3
  64. package/dist/{InputGroup-CdTq8H7c.js.map → InputGroup-BnLHY0eq.js.map} +1 -1
  65. package/dist/{Kbd-CxHAo1oo.js → Kbd-CvSM3D3-.js} +2 -2
  66. package/dist/{Kbd-CxHAo1oo.js.map → Kbd-CvSM3D3-.js.map} +1 -1
  67. package/dist/MarkedText-DNGEUVWc.js +67 -0
  68. package/dist/MarkedText-DNGEUVWc.js.map +1 -0
  69. package/dist/{MermaidDiagram-ChL3WIQT.js → MermaidDiagram-WkonYErO.js} +6 -87
  70. package/dist/MermaidDiagram-WkonYErO.js.map +1 -0
  71. package/dist/{Modal-wqG8nJiK.js → Modal-Coj-H02o.js} +16 -4
  72. package/dist/Modal-Coj-H02o.js.map +1 -0
  73. package/dist/{MultiActionButtonGroup-BiIIubiL.js → MultiActionButtonGroup-CxWH7yWK.js} +3 -3
  74. package/dist/{MultiActionButtonGroup-BiIIubiL.js.map → MultiActionButtonGroup-CxWH7yWK.js.map} +1 -1
  75. package/dist/{Navbar-BfaPeLUL.js → Navbar-B2UsolVU.js} +3 -3
  76. package/dist/{Navbar-BfaPeLUL.js.map → Navbar-B2UsolVU.js.map} +1 -1
  77. package/dist/{PageToolbar-OQg3mPF-.js → PageToolbar-DnbFt14Q.js} +3 -3
  78. package/dist/{PageToolbar-OQg3mPF-.js.map → PageToolbar-DnbFt14Q.js.map} +1 -1
  79. package/dist/{Pagination-Cjfda8KG.js → Pagination-C_b7Xhey.js} +2 -2
  80. package/dist/{Pagination-Cjfda8KG.js.map → Pagination-C_b7Xhey.js.map} +1 -1
  81. package/dist/{Popconfirm-D7_vfxFd.js → Popconfirm-CILjmzKI.js} +3 -3
  82. package/dist/{Popconfirm-D7_vfxFd.js.map → Popconfirm-CILjmzKI.js.map} +1 -1
  83. package/dist/{Popover-CzeHjvrB.js → Popover-BEbQrC_P.js} +22 -19
  84. package/dist/Popover-BEbQrC_P.js.map +1 -0
  85. package/dist/{RadioGroup-sIveSsNa.js → RadioGroup-XFzFd1ti.js} +3 -3
  86. package/dist/{RadioGroup-sIveSsNa.js.map → RadioGroup-XFzFd1ti.js.map} +1 -1
  87. package/dist/{Search-3UiqNZRj.js → Search-4Bxtt8hS.js} +2 -2
  88. package/dist/{Search-3UiqNZRj.js.map → Search-4Bxtt8hS.js.map} +1 -1
  89. package/dist/{SearchInput-C7dF_Ir-.js → SearchInput-f_oY9T4Z.js} +3 -3
  90. package/dist/{SearchInput-C7dF_Ir-.js.map → SearchInput-f_oY9T4Z.js.map} +1 -1
  91. package/dist/{SectionNav-8NoTOKO8.js → SectionNav-BpJlisbT.js} +4 -4
  92. package/dist/{SectionNav-8NoTOKO8.js.map → SectionNav-BpJlisbT.js.map} +1 -1
  93. package/dist/{Select-Is0VkePH.js → Select-DpcgKqKB.js} +32 -21
  94. package/dist/Select-DpcgKqKB.js.map +1 -0
  95. package/dist/{Sheet-Dw9BDvjI.js → Sheet-Chf0kX1E.js} +3 -3
  96. package/dist/{Sheet-Dw9BDvjI.js.map → Sheet-Chf0kX1E.js.map} +1 -1
  97. package/dist/{Sidebar-Dcb4rinA.js → Sidebar-Cm6I4dE1.js} +9 -7
  98. package/dist/{Sidebar-Dcb4rinA.js.map → Sidebar-Cm6I4dE1.js.map} +1 -1
  99. package/dist/{Slider-BOPfMOMm.js → Slider-pPC_3gz9.js} +3 -3
  100. package/dist/{Slider-BOPfMOMm.js.map → Slider-pPC_3gz9.js.map} +1 -1
  101. package/dist/{Switch-CX2A0v50.js → Switch-ChfPKcTE.js} +2 -2
  102. package/dist/{Switch-CX2A0v50.js.map → Switch-ChfPKcTE.js.map} +1 -1
  103. package/dist/{Tabs-UMQuDoW8.js → Tabs-eB974xJf.js} +2 -2
  104. package/dist/{Tabs-UMQuDoW8.js.map → Tabs-eB974xJf.js.map} +1 -1
  105. package/dist/{TextInput-CMuN1WfY.js → TextInput-DHM6dHmn.js} +3 -3
  106. package/dist/{TextInput-CMuN1WfY.js.map → TextInput-DHM6dHmn.js.map} +1 -1
  107. package/dist/{ThemeProvider-QncUrfJn.js → ThemeProvider-DrQJtzpd.js} +90 -14
  108. package/dist/ThemeProvider-DrQJtzpd.js.map +1 -0
  109. package/dist/{TimePicker-D9zktB8A.js → TimePicker-BGeqc-Ed.js} +2 -2
  110. package/dist/{TimePicker-D9zktB8A.js.map → TimePicker-BGeqc-Ed.js.map} +1 -1
  111. package/dist/{Toast-DQSbs3oA.js → Toast-Dr6Uw7ao.js} +2 -2
  112. package/dist/{Toast-DQSbs3oA.js.map → Toast-Dr6Uw7ao.js.map} +1 -1
  113. package/dist/{ZoomableSvg-CZEu6sAq.js → ZoomableSvg-BWnDkR61.js} +4 -4
  114. package/dist/{ZoomableSvg-CZEu6sAq.js.map → ZoomableSvg-BWnDkR61.js.map} +1 -1
  115. package/dist/{context-KxxeMHbR.js → context-D7X470dy.js} +5 -34
  116. package/dist/context-D7X470dy.js.map +1 -0
  117. package/dist/defaults-i_xG2F_1.js +32 -0
  118. package/dist/defaults-i_xG2F_1.js.map +1 -0
  119. package/dist/editors.d.ts +12 -2
  120. package/dist/editors.js +20 -12
  121. package/dist/editors.js.map +1 -1
  122. package/dist/findLiteralMatches-BGBw8Vbl.js +62 -0
  123. package/dist/findLiteralMatches-BGBw8Vbl.js.map +1 -0
  124. package/dist/flint.d.ts +15 -0
  125. package/dist/flint.js +212 -0
  126. package/dist/flint.js.map +1 -0
  127. package/dist/index.d.ts +725 -533
  128. package/dist/index.js +60 -57
  129. package/dist/internal/blocks/AIKit.js +2 -2
  130. package/dist/internal/blocks/AppShell.js +2 -2
  131. package/dist/internal/blocks/BrandEditor.js +1 -1
  132. package/dist/internal/blocks/ChartPanel.js +1 -1
  133. package/dist/internal/blocks/ClipboardButton.js +1 -1
  134. package/dist/internal/blocks/ColorInput.js +1 -1
  135. package/dist/internal/blocks/ColorModeSwitch.js +1 -1
  136. package/dist/internal/blocks/Command.js +1 -1
  137. package/dist/internal/blocks/CopyField.js +1 -1
  138. package/dist/internal/blocks/DateInput.js +1 -1
  139. package/dist/internal/blocks/DateRangeInput.js +1 -1
  140. package/dist/{DiagramSkeleton-lwBS2YBa.js → internal/blocks/DiagramShared/DiagramSkeleton.js} +3 -3
  141. package/dist/internal/blocks/DiagramShared/DiagramSkeleton.js.map +1 -0
  142. package/dist/internal/blocks/EmptyConfigurationActionCard.js +1 -1
  143. package/dist/internal/blocks/ErrorState.js +1 -1
  144. package/dist/internal/blocks/ExportMenu.js +5 -5
  145. package/dist/internal/blocks/ExportMenu.js.map +1 -1
  146. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +1 -1
  147. package/dist/internal/blocks/Form.js +1 -1
  148. package/dist/internal/blocks/FormGroup.js +1 -1
  149. package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
  150. package/dist/internal/blocks/MultiCombobox.js +15 -11
  151. package/dist/internal/blocks/MultiCombobox.js.map +1 -1
  152. package/dist/internal/blocks/NumberInput.js +1 -1
  153. package/dist/internal/blocks/PageToolbar.js +1 -1
  154. package/dist/internal/blocks/PasswordInput.js +2 -2
  155. package/dist/internal/blocks/SearchInput.js +1 -1
  156. package/dist/internal/blocks/SearchInputWithNavigation.js +3 -3
  157. package/dist/internal/blocks/SectionNav.js +1 -1
  158. package/dist/internal/blocks/SplitButton.js +2 -2
  159. package/dist/internal/blocks/StatCard.js +1 -1
  160. package/dist/internal/blocks/TextInput.js +1 -1
  161. package/dist/internal/blocks/Textarea.js +1 -1
  162. package/dist/internal/blocks/TimeInput.js +3 -3
  163. package/dist/internal/components/Alert.js +1 -1
  164. package/dist/internal/components/AlertModal.js +1 -1
  165. package/dist/internal/components/Avatar.js +1 -1
  166. package/dist/internal/components/BrandLogo.js +1 -1
  167. package/dist/internal/components/Breadcrumb.js +1 -1
  168. package/dist/internal/components/Button.js +1 -1
  169. package/dist/internal/components/Card.js +1 -1
  170. package/dist/internal/components/Checkbox.js +1 -1
  171. package/dist/internal/components/Clipboard.js +1 -1
  172. package/dist/internal/components/CollapsiblePanel.js +1 -1
  173. package/dist/internal/components/Combobox.js +1 -1
  174. package/dist/internal/components/DatePicker.js +1 -1
  175. package/dist/internal/components/DateRangePicker.js +1 -1
  176. package/dist/internal/components/DropdownMenu.js +1 -1
  177. package/dist/internal/components/FileUpload.js +1 -1
  178. package/dist/internal/components/FullScreenModal.js +1 -1
  179. package/dist/internal/components/HoverCard.js +1 -1
  180. package/dist/internal/components/InputGroup.js +1 -1
  181. package/dist/internal/components/Kbd.js +1 -1
  182. package/dist/internal/components/MarkedText.js +1 -1
  183. package/dist/internal/components/Modal.js +1 -1
  184. package/dist/internal/components/Navbar.js +1 -1
  185. package/dist/internal/components/Pagination.js +1 -1
  186. package/dist/internal/components/Popconfirm.js +1 -1
  187. package/dist/internal/components/Popover.js +1 -1
  188. package/dist/internal/components/RadioGroup.js +1 -1
  189. package/dist/internal/components/Search.js +1 -1
  190. package/dist/internal/components/Select.js +1 -1
  191. package/dist/internal/components/Sheet.js +1 -1
  192. package/dist/internal/components/Sidebar.js +1 -1
  193. package/dist/internal/components/Slider.js +1 -1
  194. package/dist/internal/components/Switch.js +1 -1
  195. package/dist/internal/components/Tabs.js +1 -1
  196. package/dist/internal/components/ThemeProvider.js +4 -3
  197. package/dist/internal/components/TimePicker.js +1 -1
  198. package/dist/internal/components/Toast.js +2 -2
  199. package/dist/internal/components/Toggle.js +3 -3
  200. package/dist/internal/components/Toggle.js.map +1 -1
  201. package/dist/internal/components/Tooltip.js +1 -1
  202. package/dist/internal/components/Truncated.js +16 -8
  203. package/dist/internal/components/Truncated.js.map +1 -1
  204. package/dist/internal/components/ZoomableSvg.js +1 -1
  205. package/dist/internal/utils.js +3 -3
  206. package/dist/markdown-editor.js +7 -7
  207. package/dist/mermaid.js +1 -1
  208. package/dist/rjsf.js +11 -11
  209. package/dist/{styles-DSUP_-9Z.js → styles-BAFscjSV.js} +4 -3
  210. package/dist/styles-BAFscjSV.js.map +1 -0
  211. package/dist/styles.css +1 -1
  212. package/dist/svgTransforms-C9njt92h.js +89 -0
  213. package/dist/svgTransforms-C9njt92h.js.map +1 -0
  214. package/dist/testing.js +4 -3
  215. package/dist/testing.js.map +1 -1
  216. package/dist/{themeTokens-BNg98Mu9.js → themeTokens-D70HeNVF.js} +13 -6
  217. package/dist/{themeTokens-BNg98Mu9.js.map → themeTokens-D70HeNVF.js.map} +1 -1
  218. package/dist/{toast-CjkURw0M.js → toast-DIC0iNRf.js} +3 -3
  219. package/dist/toast-DIC0iNRf.js.map +1 -0
  220. package/dist/types-BPcAjMG0.d.ts +10 -0
  221. package/dist/{useColorSchemeState-CYqcUfSH.js → useColorSchemeState-klihG9-i.js} +2 -2
  222. package/dist/{useColorSchemeState-CYqcUfSH.js.map → useColorSchemeState-klihG9-i.js.map} +1 -1
  223. package/dist/{utils-D0c5VAyF.js → utils-B5Z52hYs.js} +2 -2
  224. package/dist/{utils-D0c5VAyF.js.map → utils-B5Z52hYs.js.map} +1 -1
  225. package/dist/vega.d.ts +46 -0
  226. package/dist/vega.js +26 -0
  227. package/dist/vega.js.map +1 -0
  228. package/dist/vegaTheme-DfoEQhOy.js +146 -0
  229. package/dist/vegaTheme-DfoEQhOy.js.map +1 -0
  230. package/package.json +28 -9
  231. package/src/styles.css +27 -4
  232. package/dist/AIKit-DiS0CVOW.js.map +0 -1
  233. package/dist/AppShell-B5GQN6Zs.js.map +0 -1
  234. package/dist/Button-6xBX0nNl.js.map +0 -1
  235. package/dist/CodeEditor-DyaN-Ij2.js.map +0 -1
  236. package/dist/Combobox-Dgm6Z4ib.js.map +0 -1
  237. package/dist/DiagramSkeleton-lwBS2YBa.js.map +0 -1
  238. package/dist/MarkedText-Ckbft5mw.js +0 -44
  239. package/dist/MarkedText-Ckbft5mw.js.map +0 -1
  240. package/dist/MermaidDiagram-ChL3WIQT.js.map +0 -1
  241. package/dist/Modal-wqG8nJiK.js.map +0 -1
  242. package/dist/Popover-CzeHjvrB.js.map +0 -1
  243. package/dist/Select-Is0VkePH.js.map +0 -1
  244. package/dist/ThemeProvider-QncUrfJn.js.map +0 -1
  245. package/dist/context-KxxeMHbR.js.map +0 -1
  246. package/dist/escapeStringRegexp-8MxP2knR.js +0 -6
  247. package/dist/escapeStringRegexp-8MxP2knR.js.map +0 -1
  248. package/dist/styles-DSUP_-9Z.js.map +0 -1
  249. package/dist/toast-CjkURw0M.js.map +0 -1
package/AGENTS.md CHANGED
@@ -20,18 +20,20 @@ Working **on** the package (commands, layer boundary, token surface, the FA→Lu
20
20
 
21
21
  The root barrel (`@keboola/design`) is **not** the whole package. These live only at their subpath — an agent scanning the barrel (or `dist/index.d.ts`) misses them and wrongly concludes the capability is absent. The "always import from the package root" rule above holds for everything in the barrel; the rows below are the **deliberate exceptions** — import them from the subpath, not the root.
22
22
 
23
- | Subpath | Exports | Reach for it when |
24
- | --------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- |
25
- | `@keboola/design` | Components, blocks, hooks, `ThemeProvider`, forms | Default — everything below the listed exceptions |
26
- | `@keboola/design/icons` | Lucide re-export + Keboola-custom SVG icons | Any icon (`import { ChevronDown } from '@keboola/design/icons'`) |
27
- | `@keboola/design/editors` | `CodeEditor`, `SqlEditor`, `MergeEditor` | Real code editing — pulls the ~1.3 MB CodeMirror bundle |
28
- | `@keboola/design/charts` | `LineChart`, `BarChart`, `AreaChart`, `PieChart`, `ChartContainer` | Data-viz — themed recharts wrappers; **not** in the root barrel |
29
- | `@keboola/design/mermaid` | `MermaidDiagram` (`{ code }`) | Render a Mermaid diagram — **not** in the root barrel |
30
- | `@keboola/design/markdown-editor` | `MarkdownEditor` | Markdown editing — **not** in the root barrel |
31
- | `@keboola/design/rjsf` | `RjsfSchemaEditor`, `useRootFormData` | JSON-Schema-driven config forms (RJSF) — pulls `@rjsf/*` + ajv |
32
- | `@keboola/design/testing` | `renderWithTheme` | Unit tests that need a component wrapped in `ThemeProvider` |
33
- | `@keboola/design/styles` | Tailwind-extending stylesheet | App entry, Tailwind consumers (**default**) |
34
- | `@keboola/design/styles/compiled` | Pre-compiled CSS (~176 KB) | App entry, when you cannot run Tailwind |
23
+ | Subpath | Exports | Reach for it when |
24
+ | --------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------- |
25
+ | `@keboola/design` | Components, blocks, hooks, `ThemeProvider`, forms | Default — everything below the listed exceptions |
26
+ | `@keboola/design/icons` | Lucide re-export + Keboola-custom SVG icons | Any icon (`import { ChevronDown } from '@keboola/design/icons'`) |
27
+ | `@keboola/design/editors` | `CodeEditor`, `SqlEditor`, `MergeEditor` | Real code editing — pulls the ~1.3 MB CodeMirror bundle |
28
+ | `@keboola/design/charts` | `LineChart`, `BarChart`, `AreaChart`, `PieChart`, `ChartContainer` | Data-viz — themed recharts wrappers; **not** in the root barrel |
29
+ | `@keboola/design/mermaid` | `MermaidDiagram` (`{ code }`) | Render a Mermaid diagram — **not** in the root barrel |
30
+ | `@keboola/design/flint` | `FlintChart` (`{ code }`) | Render a Flint chart (JSON spec, Vega-Lite) — **not** in the root barrel |
31
+ | `@keboola/design/vega` | `vegaTheme(scheme)`, `useVegaTheme()` | Brand + scheme aware Vega-Lite `Config` for any chart you render yourself |
32
+ | `@keboola/design/markdown-editor` | `MarkdownEditor` | Markdown editing — **not** in the root barrel |
33
+ | `@keboola/design/rjsf` | `RjsfSchemaEditor`, `useRootFormData` | JSON-Schema-driven config forms (RJSF) — pulls `@rjsf/*` + ajv |
34
+ | `@keboola/design/testing` | `renderWithTheme` | Unit tests that need a component wrapped in `ThemeProvider` |
35
+ | `@keboola/design/styles` | Tailwind-extending stylesheet | App entry, Tailwind consumers (**default**) |
36
+ | `@keboola/design/styles/compiled` | Pre-compiled CSS (~176 KB) | App entry, when you cannot run Tailwind |
35
37
 
36
38
  ### design ≠ shadcn — API deltas
37
39
 
@@ -156,6 +158,15 @@ Charts ship from the **`@keboola/design/charts`** subpath (recharts under the ho
156
158
 
157
159
  Reach for it when **two or more series of similar magnitude sit far from zero** and their difference is the story — actual vs target, used vs quota. Don't reach for it for a **single series whose magnitude the reader is meant to judge**: zooming the axis turns ordinary noise into a dramatic trend. And it is deliberately absent from `BarChart` / `AreaChart` — a line encodes value by position, so the baseline is just a viewport, but a bar's length and an area's fill are measured _from_ the baseline, and truncating those makes the geometry misstate the ratios. If an area chart needs a non-zero baseline, it is a line chart. (`ChartContainer` + a raw recharts chart remains the escape hatch for anything the wrappers don't model.)
158
160
 
161
+ **Rendering a Vega-Lite spec yourself?** Take the config from **`@keboola/design/vega`** instead of hand-writing colours — `useVegaTheme()` in a component, `vegaTheme(scheme)` outside one. It carries the transparent background, the semantic axis/legend/title chrome, and `range.category` = `--chart-1..5` in series order, so a spec from anywhere lands on the same palette as the recharts wrappers and follows the active scheme and brand. Pass it as the compile/render `config`; a range set on the spec's own scale still wins.
162
+
163
+ ```tsx
164
+ const config = useVegaTheme();
165
+ const { spec } = compile(vegaLiteSpec, { config });
166
+ ```
167
+
168
+ The config holds resolved `rgb(…)` strings, not `var()` references — vega does its colour maths in JS and never consults the stylesheet. So a chart rendered once must be re-rendered when the config identity changes; `useVegaTheme` gives you a new one on every scheme or brand switch.
169
+
159
170
  ### Layout
160
171
 
161
172
  **No** `Stack` / `Flex` / `Box` component exists, by design — shadcn doesn't ship them, and adding wrappers creates a third way to do layout. Use Tailwind utility classes directly.
@@ -176,7 +187,7 @@ Vertical rhythm: prefer `tw:gap-*` on a flex column parent over `tw:mt-*` on chi
176
187
 
177
188
  **`Card` padding: the root owns it, or the slots do — never both.** `Card padding` (default `small`) pads the root and gaps its slots, which is right for a plain content card. Set `padding="none"` when a slot must bleed to the card edge — a table, an `EmptyState`, or a header with a rule under it — and the root also drops its gap so the bands sit flush. In that mode the slots pad themselves: `Card.Header divider` becomes its own padded band with a full-bleed bottom rule, and `Card.Content padded` takes the standard body padding. `Card.Header` distributes its children by `layout`: **`row`** (default) pushes them apart, title left and actions right; **`inline`** keeps them together for a title with an icon or badge; **`stack`** puts a description under the title. Don't hand-roll these with `className` — `cva` here concatenates rather than tailwind-merges, so a `tw:justify-start` in `className` will not reliably beat the `row` default. Use the prop.
178
189
 
179
- **Page gutter: `AppShell.Main` owns it, with an opinionated default.** `AppShell.Main` applies a default page gutter (`tw:p-6`) so content looks right out of the box. Because `cn` is tailwind-merge, override it from the call site when the app owns its own gutter — e.g. `className="tw:p-0"` (kbc-ui does this; it centralizes the gutter on its own shell wrapper). `PageLayout` still adds **no** gutter of its own, so header and content align inside whatever gutter `AppShell.Main` provides.
190
+ **Page gutter: `AppShell.Main` owns it, on a wrapper inside its scroll container.** `AppShell.Main` applies a default page gutter (`tw:p-6`) so content looks right out of the box. Override it from the call site through **`contentClassName`** — e.g. `contentClassName="tw:p-0"` (kbc-ui does this; it centralizes the gutter on its own shell wrapper). **Never put a `p-*` in `className`**: that lands on the scroll container, and sticky insets resolve against the nearest scrollport's _padding box_, so it parks every `position: sticky` descendant — `Table.Header` included — one gutter below the visible top edge, with scrolled content showing through the strip above. `className` is still the right place for the scroll behaviour itself (`tw:overflow-visible`). `PageLayout` adds **no** gutter of its own, so header and content align inside whatever gutter `AppShell.Main` provides.
180
191
 
181
192
  ```tsx
182
193
  // Default gutter — nothing to add:
@@ -185,7 +196,7 @@ Vertical rhythm: prefer `tw:gap-*` on a flex column parent over `tw:mt-*` on chi
185
196
  </AppShell.Main>
186
197
 
187
198
  // Own your gutter — opt out and add your own:
188
- <AppShell.Main className="tw:p-0">
199
+ <AppShell.Main contentClassName="tw:p-0">
189
200
  <div className="tw:px-8 tw:py-6">{/* custom gutter */}</div>
190
201
  </AppShell.Main>
191
202
  ```
@@ -486,6 +497,19 @@ Both router wirings (TanStack Router, and a custom named router) are in [`RECIPE
486
497
  />
487
498
  ```
488
499
 
500
+ **Pinned chip in a `MultiCombobox`** — a chip the control shows but the user does not own (a set of packages the runtime already ships, a role every member has). `pinnedChipLabels` is a render-only slot, so nothing enters `value`: there is no sentinel to strip on save and the control's own counting never sees a non-item.
501
+
502
+ ```tsx
503
+ <MultiCombobox
504
+ allowCreate
505
+ value={packages}
506
+ onValueChange={save}
507
+ pinnedChipLabels={['Default packages']}
508
+ />
509
+ ```
510
+
511
+ Don't fake one by prepending a sentinel to `value` — that costs the caller a strip on every change plus a label branch, and leaks the sentinel into the primitive's counting.
512
+
489
513
  **Sheet** — use `Sheet.Header title="…" description="…"` for a styled title + close icon; do **not** hand-roll with the raw `Sheet.Title`, which renders an unstyled `<h2>`. `Sheet.Body` provides horizontal padding only (`tw:px-4`) — add your own vertical rhythm/gap:
490
514
 
491
515
  ```tsx
@@ -524,6 +548,10 @@ Layer-0 colour usage splits into three buckets.
524
548
 
525
549
  A border **width** utility carries no colour of its own — a lone `tw:border` / `tw:border-t` / `tw:border-2` paints the element's _text_ colour, a near-black frame where a light-grey rule belongs. Pair every width with a colour: `tw:border tw:border-border` for chrome, `tw:border-input` on form controls. `pnpm test:stories` fails on any story that ships a width without one.
526
550
 
551
+ An **opacity modifier composites over `transparent`, not over the element's own rest fill.** On a control that is opaque at rest (`tw:bg-card`, `tw:bg-background`) a `tw:hover:bg-<token>/NN` therefore _replaces_ that fill, and the control shows whatever it overlaps — a flow-canvas edge, an `Alert` tint. Mix the state fill into the control's rest token instead: `tw:hover:bg-[color-mix(in_oklab,rgb(var(--muted-foreground))_15%,rgb(var(--card)))]`. Use the raw `--*` triples — `--color-*` is `@theme inline`, build-time only, and an unresolved one invalidates the declaration into `transparent`. A control that is **transparent at rest** (menu item, ghost button, table row) is the case the modifier is for; leave those alone.
552
+
553
+ A tint also cannot carry a **state change** on a light surface — a brand-primary tint stays within ~1.5:1 of the surface at any opacity that still reads as a tint. Step the border for that, and keep the roles apart: hover steps the chrome border (`border-input` → `border-muted-foreground`), `focus-visible` owns the brand border.
554
+
527
555
  **Status — themeable under argo, with AA-legible defaults.** The four semantic states — `success`, `warning`, `info`, and `destructive` (used for "error") — are brand-overridable tokens, not fixed scales. Alert `variant`, `HelpBlock` / field-error state, `ErrorState`, and `Stepper` status read them, so a brand recolours every status surface at once. **Rule:** consume the token as **tint + text/icon** — `tw:bg-<role>/10` for the surface, `tw:text-<role>` / `tw:border-<role>/25` for the mark. The default token values ship dark enough to be AA (≥4.5:1) as text/icon on their own tint, in light **and** dark mode — a brand overriding them should preserve that. Avoid solid fills with a white foreground (`tw:bg-<role>` + white): the token flips light in dark mode, so white-on-token fails there. A brand _can_ recolour "error" away from red — that is now allowed; the accessible-on-tint contract is what must hold, not the hue. **Legacy exception:** the themeable treatment is gated to argo (`data-[argo=true]:` / `group-data-[argo=true]/…:`). Under legacy chrome (`useArgoDesign()` → `false`) Alert and `ErrorState` keep their original fixed status scales (`blue-*`/`green-*`/`warning-*`/`error-*`), and the legacy brand retains its original status token hues — so legacy status colour is unchanged.
528
556
 
529
557
  **Decorative — stays fixed across brands.** The non-semantic palette that encodes category rather than state: `Badge` decorative colours (`purple`, `cyan`, `teal`, `blue`, …), chart colours, decorative tag palette. **Rule:** keep using primitive scales (`purple-500`, `cyan-500`, …). These are an identity/category contract with the consumer and do not re-skin.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,429 @@
1
1
  # @keboola/design
2
2
 
3
+ ## 11.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - The AIKit layer for Kai's redesign (DES-20, DES-170).
8
+ - `ThemeScope` (new): a subtree pins its own colour scheme regardless of the app's —
9
+ Kai's panel stays light while its minimized widget is dark. `useThemeScopeContainer`
10
+ hands portalling components the scope element, and `Popover.Content` now takes a
11
+ `container` so a portal stays inside the scope that styles it.
12
+ - `ToolCallStatusHeader` joins AIKit — the tool-call panel header kbc-ui carried,
13
+ promoted with tests and stories.
14
+ - `MessageBubble` drops its avatar; a user turn is a plain bubble. `user` is still accepted
15
+ (deprecated, ignored) so call sites keep compiling.
16
+ - `Command` takes a `variant`, so a command in a small surface (a popover) can opt out
17
+ of the full-screen palette styling the theme would pick.
18
+ - No platform token changes: Kai scopes its own sidebar accent in kbc-ui.
19
+ - The root barrel grows ~1 KB with the new exports; the size budget moves 342→344 KB.
20
+
21
+ - `CodeEditor` search matches literal text instead of a compiled pattern, and `findLiteralMatches` / `hasLiteralMatch` ship as utilities.
22
+
23
+ A search query is data, not a pattern. Compiling one meant escaping it first and living with V8's
24
+ ceiling on how large a pattern may be: a query of a few tens of kilobytes — what pasting a document
25
+ into a find box produces — threw `Regular expression too large` when the pattern was first executed.
26
+ `findMatches` now scans for the query literally, so escaping and that ceiling are both gone, and it
27
+ short-circuits before joining the document's rope when the query cannot fit in it.
28
+
29
+ `findLiteralMatches(text, query)` returns the start offsets of every case-insensitive occurrence and
30
+ `hasLiteralMatch(text, query)` answers whether there is one, stopping at the first. Both fold case
31
+ without changing the length of the string, so the offsets index the caller's own text.
32
+
33
+ Two behaviour notes for anyone matching the editor's counts elsewhere: `İ` (U+0130) now reads as a
34
+ plain `i` rather than matching only itself, and `SearchMatch` no longer carries the matched `text`,
35
+ which nothing read.
36
+
37
+ - `Markdown` accepts `remarkPlugins` and `components`.
38
+ - `remarkPlugins` runs after the built-in GFM one rather than in place of it, so tables, autolinks and task lists keep working whatever a caller adds.
39
+ - `components` is merged over the built-in element renderers by key. An override replaces that element's renderer rather than wrapping it, so a `table` override loses the design `Table` and an `a` override loses the new-tab treatment for absolute links.
40
+ - Together they are the seam an app's own inline syntax arrives through — an annotation an agent writes beside a link, read back by a renderer for that element — which is app product knowledge and does not belong in this package.
41
+ - Both are meant to be held in module constants: `Markdown` re-parses its source on every render, so a caller memoises it, and a prop object built during render defeats that.
42
+
43
+ - `MarkedText` highlights by literal scan instead of a compiled pattern, and `findLiteralMatches` takes an `isCaseSensitive` option.
44
+
45
+ The highlight query was escaped, split on spaces, and joined into one alternation regex — so a
46
+ space-free paste of a few tens of kilobytes (minified SQL, JSON, base64) exceeded V8's pattern
47
+ ceiling and threw `Regular expression too large` from a `useMemo`, taking the page down with it.
48
+ `createMarkedParts` now finds each space-separated term with `findLiteralMatches` and merges the
49
+ offsets, so no pattern is compiled at all.
50
+
51
+ One visible behaviour change: overlapping term matches now merge into one marked run — searching
52
+ `test testing` marks the whole `testing`, where the alternation used to mark only its `test` prefix
53
+ and leave `ing` plain. Adjacent matches merge the same way. Case-insensitive matching also folds
54
+ `İ` (U+0130) to a plain `i`, matching the editor search.
55
+
56
+ `escapeStringRegexp` is gone from the package's internals — nothing compiles user text as a pattern
57
+ anymore.
58
+
59
+ - `MultiCombobox`: a render-only slot for pinned chips, chip callbacks see the real item type, and a created value stops haunting the list.
60
+
61
+ `pinnedChipLabels` renders chips the caller keeps rather than the user — a set of packages the
62
+ runtime already ships, a role every member has. They sit ahead of the values, outside `maxVisible`,
63
+ carry no remove button, and never enter `value`.
64
+
65
+ `itemToChipLabel` and `itemToChipColor` now receive `Value` instead of `unknown`, so a call site
66
+ stops narrowing by hand. Existing call sites keep compiling; their `String(...)` wrappers become
67
+ redundant.
68
+
69
+ Created values were kept in a list that never shrank, so a value the user created and then removed —
70
+ or cleared — stayed in the listbox as an option for the rest of the session, and skewed
71
+ `allowSelectAll`'s "everything is selected" arithmetic. The list now tracks the selection.
72
+
73
+ - Add a `hint` slot to `Select` items, and let the top-level `Select` take `aria-label`.
74
+
75
+ `hint` renders a trailing muted note in the listbox row — "Default" beside the value the
76
+ platform falls back to. It sits outside Radix's `ItemText`, so the trigger keeps showing the
77
+ label alone once the option is picked; because Radix names an option from `ItemText` only
78
+ (`aria-labelledby`), the hint is wired into the item's `aria-describedby` instead, so a screen
79
+ reader still reads it.
80
+
81
+ `aria-label` was the one trigger a11y prop the convenience wrapper did not forward — it already
82
+ curated `aria-describedby`, `aria-invalid` and `aria-required`. Selects that stand without a
83
+ visible `Label` (a filter row, or several selects joined by "OR") need it.
84
+
85
+ - `UsageStats`: the corner alert badge now accepts a severity.
86
+
87
+ `alert` takes `boolean | 'warning' | 'error'` instead of a bare boolean. `'warning'` renders the badge
88
+ amber for an approaching-limit state that isn't at-limit yet; `'error'` (or the existing bare `true`)
89
+ keeps the red at-limit color, so existing call sites keep compiling unchanged.
90
+
91
+ ### Patch Changes
92
+
93
+ - `Button variant="outline"`: the hovered button stops dissolving into the surface behind it.
94
+
95
+ Both hover and pressed fills were tints of `transparent` (Tailwind's `bg-<token>/NN`), so hovering
96
+ replaced the variant's opaque rest fill and the button showed whatever it overlapped — an edge on a
97
+ flow canvas, the status tint inside an `Alert`. They now `color-mix` into the variant's own rest
98
+ token (`--card` under legacy chrome, `--background` under argo), which is the same colour on the
99
+ surface the variant assumes and stays opaque off it.
100
+
101
+ Argo compounded that by keeping `border-input` on hover while tinting the fill toward it, so the
102
+ button's edge contrast _dropped_ as the user reached it — measured 1.42:1 → 1.25:1 in light and
103
+ 2.28:1 → 1.62:1 in dark, against its own fill on a `--muted` surface. Its hover now steps the
104
+ border to `--muted-foreground` (4.00:1 / 5.24:1), the treatment legacy already shipped. A tint
105
+ cannot carry that signal: brand primary stays within ~1.5:1 of a light surface at any opacity that
106
+ still reads as a tint. `--primary` stays reserved for `focus-visible`, so hover and focus remain
107
+ distinguishable.
108
+
109
+ Consumers overriding the hover fill per call site to work around the leak can drop those
110
+ overrides. `packages/design/AGENTS.md` records the underlying rule in the Layer-0 chrome contract.
111
+
112
+ - Security patches for transitive dependencies: qs 6.16.0 in api-client, mermaid 11.17.2 and dompurify
113
+ 3.4.14 in design.
114
+
115
+ No API or behaviour change in either package.
116
+
117
+ - Every theme-aware subpath entry stops carrying `@keboola/brand-registry`.
118
+
119
+ `ThemeProvider/defaults.ts` imported two legacy brands for value, only to build a two-element array
120
+ of their ids — and that array had exactly one consumer, the `renderWithTheme` test helper. Because
121
+ `useThemeAttributes`, `useBrandState` and `useColorSchemeState` each read a constant from
122
+ `defaults.ts`, that one import put the whole registry in the graph of every entry that reads the
123
+ live theme.
124
+
125
+ The ids now live at the helper that uses them, so `defaults.ts` imports `Brand` as a type only. No
126
+ export moved and no behaviour changed — `keboolaLegacySidebar` / `keboolaLegacyTopnav` still resolve
127
+ from the root barrel for the consumers that render them.
128
+
129
+ What a consumer pays for a themed subpath, brotlied:
130
+
131
+ | Entry | Before | After | Δ |
132
+ | --------------------------------- | ---------: | ---------: | ----: |
133
+ | `@keboola/design/vega` | 61.9 kB | 0.9 kB | −98 % |
134
+ | `@keboola/design/editors` | 312.5 kB | 252.0 kB | −19 % |
135
+ | `@keboola/design/flint` | 439.7 kB | 380.2 kB | −14 % |
136
+ | `@keboola/design/rjsf` | 490.2 kB | 430.6 kB | −12 % |
137
+ | `@keboola/design/markdown-editor` | 1 333.9 kB | 1 272.0 kB | −5 % |
138
+ | `@keboola/design/mermaid` | 1 200.4 kB | 1 140.1 kB | −5 % |
139
+ | `{ useAppForm }` from the root | 135.2 kB | 73.9 kB | −45 % |
140
+
141
+ The root barrel is unchanged — it re-exports the brands deliberately, so an app that renders one
142
+ still pays for it once. The per-entry `size-limit` budgets drop to match, so they now measure the
143
+ entry rather than a shared dependency.
144
+
145
+ - `Modal.Footer` now lays its own actions out: right-aligned, 8px apart.
146
+
147
+ The footer owned only horizontal padding, so a bare `<Modal.Footer>` fell back to block flow — its
148
+ buttons sat left-aligned, separated by nothing but collapsed JSX whitespace. Nearly every footer in
149
+ this repo is bare, so that was the default look; a handful re-declared `tw:flex tw:justify-end
150
+ tw:gap-2` at the call site. Consumers can drop those.
151
+
152
+ Overriding the arrangement still works through `className`, but `cn` runs tailwind-merge, which
153
+ displaces only within a conflict group: `tw:justify-between` replaces `justify-end`, while
154
+ `tw:flex-col` leaves `items-center` and the gap in place, so a stacked footer wants
155
+ `tw:items-stretch` alongside it. The footer imposes no width on its children, so one that should
156
+ fill the row carries `tw:w-full` itself (`Button block`, `ButtonGroup variant="block"`).
157
+
158
+ One footer in this repo wanted the stacked arrangement — `wr-google-sheets`' `SheetModal`, which
159
+ puts a saving message above its buttons — and is the only one still passing layout classes.
160
+
161
+ `@keboola/kbc-ui` and `@keboola/control-ui` are private — nothing to release for them; they are
162
+ listed because their call sites changed.
163
+
164
+ - `PromptInput`: `autoFocus` no longer scrolls ancestors to reveal the textarea.
165
+
166
+ The prop was forwarded as the DOM attribute, and the browser's autofocus scrolls every scrollable
167
+ ancestor toward the element. A composer mounting inside a panel that is still sliding in — kbc-ui's
168
+ Kai rail opens this way — sits off-canvas at focus time, so the page scrolled sideways toward it,
169
+ shoving the whole content left until the slide finished and the overflow clamp snapped it back.
170
+ The focus is now applied imperatively on mount with `focus({ preventScroll: true })`: same focus,
171
+ same mount-only semantics, no ancestor scrolling.
172
+
173
+ ## 11.3.0
174
+
175
+ ### Minor Changes
176
+
177
+ - `AppShell.Main` takes `clampContent`, which bounds its content wrapper to the shell's height
178
+ instead of letting it grow with the content. Pass it when the page runs its own `overflow-auto`
179
+ scroller — a `flex-1` descendant then clamps to the scrollport rather than to the content, so the
180
+ page's scroller is the element that scrolls. Default behaviour is unchanged; no migration.
181
+
182
+ - `AppShell.Main` no longer carries its page gutter on the scroll container.
183
+
184
+ Sticky insets resolve against the nearest scrollport's padding box, so the gutter on the scrolling
185
+ element parked every `position: sticky` descendant one gutter below the scrollport's visible top
186
+ edge — `Table.Header` included. Scrolled rows showed through the strip above the header, on every
187
+ table in every app on this shell.
188
+
189
+ The gutter now sits on a content wrapper inside `Main`, and `contentClassName` overrides it.
190
+
191
+ **Migration** — only if you override `Main`'s padding through `className`:
192
+
193
+ ```diff
194
+ - <AppShell.Main className="tw:p-0">
195
+ + <AppShell.Main contentClassName="tw:p-0">
196
+ ```
197
+
198
+ With `asChild`, your element stays the scroll container and the gutter wrapper is nested inside it,
199
+ so the padding that used to land on your element now lands on a child `<div>`.
200
+
201
+ `className` still lands on the scroll container, so an `overflow-visible` reset stays where it is.
202
+ Overrides that mix the two need splitting:
203
+
204
+ ```diff
205
+ - <AppShell.Main className={cn('tw:p-0', { 'tw:overflow-visible': isLegacy })}>
206
+ + <AppShell.Main
207
+ + className={cn({ 'tw:overflow-visible': isLegacy })}
208
+ + contentClassName="tw:p-0"
209
+ + >
210
+ ```
211
+
212
+ - `appShellSlotSelector(slot)` builds the CSS selector for a shell slot's `data-app-shell-slot`
213
+ attribute, so a consumer measuring or observing `AppShell.Main` from outside the shell no longer
214
+ hand-types the attribute. Renaming it then breaks the build instead of silently matching nothing.
215
+
216
+ The slot names are exported as `AppShellSlot`.
217
+
218
+ - New lazy subpath export `@keboola/design/flint`: `FlintChart` renders a
219
+ [flint-chart](https://github.com/microsoft/flint-chart) JSON spec
220
+ (`ChartAssemblyInput`) to a static SVG via the Vega-Lite backend, displayed
221
+ through `ZoomableSvg` with the same zoom/fullscreen/export UX as
222
+ `MermaidDiagram`. `flint-chart`, `vega` and `vega-lite` ship only in the flint
223
+ chunk — the root barrel and every existing entry are unaffected.
224
+
225
+ - New subpath export `@keboola/design/vega`: `vegaTheme(scheme)` and
226
+ `useVegaTheme()` hand out the design system's chart look as a plain Vega-Lite
227
+ `Config` — transparent background, semantic axis/legend/title chrome, and
228
+ `range.category` filled with `--chart-1..5` in series order. Any consumer that
229
+ compiles or renders a Vega-Lite spec is now on-brand and follows the active
230
+ colour scheme and brand; previously the config was private to `FlintChart`, so
231
+ only a Flint spec could reach it. `FlintChart` imports it from the new module
232
+ and keeps only the Flint-specific series plumbing. Nothing about the rendered
233
+ Flint chart changes.
234
+
235
+ The config holds resolved `rgb(…)` strings rather than `var()` references —
236
+ vega does its colour maths in JS and never reads the stylesheet — so rebuild it
237
+ when the scheme or brand changes; `useVegaTheme()` does that for you.
238
+
239
+ ### Patch Changes
240
+
241
+ - Make the AI Kit `Question` card follow the colour scheme and the brand.
242
+
243
+ Every colour in the component was a primitive: the card was `bg-neutral-50`, its option
244
+ preview `border-neutral-200` + `bg-white`, the labels `text-neutral-800`/`-700`, the
245
+ header and step counter `text-neutral-500`, the row hovers `hover:bg-neutral-100` and the
246
+ footer rule `border-neutral-100`. None of those participate in the `[data-theme='dark']`
247
+ variable swap, so the whole card rendered as a light slab on the dark page — carrying the
248
+ dark scheme's light ink, which is light-on-light — and no customer brand reached any of
249
+ it.
250
+
251
+ They are now semantic tokens throughout: the card is `bg-card` + `text-card-foreground`,
252
+ the preview inset `bg-muted` + `text-foreground` + `border-border`, secondary text
253
+ `text-muted-foreground`, hovers `hover:bg-accent`, rules `border-border`. Labels that
254
+ just want the card's default ink now inherit it instead of restating a colour.
255
+
256
+ The card is `bg-card` rather than `bg-muted` because under the dark scheme `--muted` and
257
+ `--popover` are the same value (`` `#26262d` ``), so a muted card would collide with the
258
+ popover surface it can sit next to; `--card` (`` `#222228` ``) stays distinct from both
259
+ that and `background`. The inset takes `bg-muted`, matching how the AI Kit paints code
260
+ surfaces elsewhere.
261
+
262
+ Covered by a `Follows scheme (light)` / `Follows scheme (dark)` story pair that measures
263
+ rendered colours in a real browser: both painted surfaces must match the page's light/dark
264
+ polarity, and the question title and preview code must clear AA against the surface they
265
+ actually sit on. Reverting either fill fails the dark story.
266
+
267
+ - Make `CodeEditor` and `SqlEditor` legible under the dark colour scheme.
268
+
269
+ The theme called `githubLightInit` in both schemes, so only the editor chrome followed
270
+ the scheme while every syntax colour stayed a light-theme value. Measured on the two
271
+ dark surfaces — the plain variant paints on `background`, `variant="field"` on `muted` —
272
+ all eleven stock tokens failed AA on `muted` and only `atom`/`bool` cleared `background`.
273
+ The palette is now picked by scheme, and `githubDarkInit` clears AA for every token on
274
+ all four surfaces an editor paints on (tightest: `comment` at 4.89:1).
275
+
276
+ Also a light/dark pair for the two overrides that were single values: functions
277
+ (`purple[600]` → `300`, from 2.89:1) and special vars (`cyan` → `300`, from 3.81:1).
278
+ Special vars additionally move `700` → `800` in **light**, where the field surface was
279
+ 4.45:1 — the one visible light-scheme change.
280
+
281
+ The surfaces those editors paint follow, since a dark palette on a light slab is worse
282
+ than the light palette was:
283
+ - the unified merge wash forced `colors.white`, where the dark `foreground` is 1.25:1;
284
+ - search matches and the SQL run/error lines were opaque light pastels, and light search
285
+ matches were themselves below AA at 3.90:1 (now 5.42:1);
286
+ - the legacy `variant="field"` surface was a fixed `neutral[100]`, where the dark
287
+ `foreground` is 1.09:1;
288
+ - the read-only wash used `secondary`, which resolves lighter than `muted` under dark.
289
+
290
+ `caret` and `selectionMatch` are now pinned rather than inherited from whichever
291
+ initialiser ran — the caret was invisible in dark, painted black on the dark page.
292
+
293
+ Light gains four AA fixes of its own, each to the nearest step of our ramp in the same hue:
294
+ `keyword` (4.38:1 → 5.96:1), `comment` (4.40:1 on the field surface → 6.15:1), markdown
295
+ `quote` (4.43:1 → 7.15:1) and `invalid` (4.44:1 on the read-only wash → 7.81:1).
296
+
297
+ One light colour is deliberately left: `atom`/`bool` — JSON `true`/`false`/`null` — at
298
+ 3.34:1. The darker orange that would clear AA is the amber already carrying `string`, so
299
+ JSON would render `true` and `"text"` alike; it needs a hue rather than a ramp step, which
300
+ is a design call rather than a measurement.
301
+
302
+ `CONTRAST-AUDIT.md` grows an editor-palette section: every tag, both schemes, all four
303
+ surfaces an editor is painted on. Dark has no sub-AA rows; light has only the one above.
304
+
305
+ - Add `withCompletions` to `CodeEditor`, for a field where nothing should be completed.
306
+
307
+ An editable editor installs `autocompletion()`, so any language that publishes a word
308
+ list through language data opens a popup without the caller asking for one. Shell is the
309
+ only such mode we use as a stand-in for freeform text: `text/x-sh` offers 88 words —
310
+ `if`, `export`, plus `ls`, `curl`, `sudo`, `vim`, `zsh` and the rest of the command list.
311
+ `withCompletions={false}` silences every source, the language's included.
312
+
313
+ The gate only works because the plugin is now installed once. `basicSetup` was adding a
314
+ second, option-less `autocompletion()` on top of the one each editor installs itself, so
315
+ `CodeEditor`'s `readOnly ? [] : [autocompletion()]` had been deciding nothing since it was
316
+ written, and `SqlEditor`'s configured copy from `installExtensions` was competing with an
317
+ unconfigured one. `buildBasicSetup` turns basicSetup's off and leaves each editor its own.
318
+
319
+ A read-only editor therefore no longer gets the plugin at all, which is what that line
320
+ always claimed: until now Ctrl-Space could open a popup on a viewer.
321
+
322
+ - Update the CodeMirror suite (`@codemirror/view` 6.43.9, `@codemirror/commands` 6.11.0, `@uiw/codemirror-theme-github` 4.25.11) to pick up the upstream `posAtCoords` and Firefox 153+ selection fixes behind the editor crashes on click and drag-select
323
+
324
+ - The filled primary button clears WCAG-AA in the dark colour scheme.
325
+
326
+ Under `[data-theme='dark']` the default filled `Button` rendered white ink on the brand blue
327
+ (`--primary`, `#1f8fff`) at 14px / weight 500 — **3.27:1**, which axe-core reports `serious` and
328
+ which AA wants at 4.5:1. It was not one component's problem: every filled primary control shared
329
+ the pairing, and `components-button--default` only looked clean because that story renders the
330
+ tinted variant. The light scheme was never affected (white on `#2563eb` is 5.17:1).
331
+
332
+ The **label** carries the fix, not the fill: `--primary` is the brand's own colour, so darkening it
333
+ would cost brand fidelity and make `--primary` worse as link text on the dark background. Dark
334
+ `--primary-foreground` and `--sidebar-primary-foreground` become a hue-matched near-black
335
+ (`10 26 43`, i.e. `#0a1a2b`) — **5.38:1** — which is the pairing `gargoola`, `keboola-portal` and
336
+ `oncall` already ship in their own `colorsDark`. `keboola`'s `colorsDark.primaryForeground` moves
337
+ with the stylesheet; brands without a `colorsDark` block (`example-customer`, both legacy brands)
338
+ inherit it. Measured on a running Storybook, the three stories that carried the violation —
339
+ `components-button--block`, `patterns-modalform--default`, `patterns-authscreen--signed-out` —
340
+ now report zero axe `color-contrast` violations in dark for both `keboola` and `example-customer`.
341
+
342
+ `ToastAction` consumed `--primary-foreground` as its chip _surface_ rather than as ink on a primary
343
+ fill, so it takes a fixed `bg-white` instead. All four non-`default` toast variants are painted with
344
+ a scheme-blind primitive (`bg-error-550`, `bg-blue-600`, `bg-green-700`, `bg-warning-550`), so the
345
+ chip on them must not move with the scheme. Under `keboola` this renders identically to before; under
346
+ `gargoola`, `keboola-portal` and `oncall` — which already ship near-black dark `primaryForeground` —
347
+ the chip was a dark block on those fills, so for them it is a fix rather than a no-op.
348
+
349
+ Both dark `--sidebar-primary-foreground` and its light counterpart become `var(--primary-foreground)`,
350
+ matching `--sidebar-primary: var(--primary)` — the sidebar badge's ink now genuinely follows the
351
+ button's instead of repeating it as a literal.
352
+
353
+ Regression cover: `checkInteractiveContrast(keboola)` no longer reports
354
+ `dark:primary/primaryForeground`, and a new `tokenBlocks.test.ts` derives every `--x` /
355
+ `--x-foreground` pair out of `styles.css` — 13 per palette — and measures each against AA, so a
356
+ token pair added later is covered without editing the test. `CONTRAST-AUDIT.md` reports the same
357
+ ratios but cannot fail on one, so the gate is the test. The CSS block parser it needs is lifted out
358
+ of `scripts/contrast-audit.ts` into `src/tokenBlocks.ts` — one parser, now type-checked, and it
359
+ throws on an unclosed block instead of parsing to EOF. `KNOWN_BELOW_AA` records the ratio of each
360
+ pair still below AA (`light:muted` 4.34, `dark:destructive` 3.91, `dark:info` 2.37, `dark:success`
361
+ 2.01) and asserts it both ways, so a pinned pair cannot quietly get worse or stay pinned once fixed.
362
+
363
+ `contrast.test.ts` also picks up `oncall`, which had no contrast coverage at all; its one residual
364
+ (`light:accent/accentForeground`, 3.74:1) is now pinned.
365
+
366
+ - Paint native chrome in the theme the app pinned, not the operating system's.
367
+
368
+ `color-scheme` was declared nowhere, so a browser drew light-scheme scrollbars, spinners and form controls over a dark page — most visibly as a pale band along the bottom edge of any scrolling `Modal.Body`. `:root` now declares `light` and `[data-theme='dark']` declares `dark`, per theme rather than `light dark`, so the OS preference cannot override what `ThemeProvider` chose.
369
+
370
+ Embeds are held neutral. `color-scheme` inherits, and a third-party widget that ships a transparent body declares no scheme of its own; when an iframe element's scheme and its document's disagree, the browser stops honouring that transparency and paints the embedded canvas opaque. Left alone, the Jira Service Desk launcher became a white tile over the dark page.
371
+
372
+ - A Flint chart with a quantitative colour renders its data instead of solid black.
373
+
374
+ Every colour in the Flint theme was serialized space-separated (`rgb(31 143 255)`), and vega parses colours with d3-color 3.1, which answers `null` for that form. A flat fill survived it — the string reaches the SVG attribute and the browser parses it — but the sequential ramp behind a quantitative `color` field did not: both endpoints collapsed and the chart painted `rgb(0, 0, 0)`. The theme now emits the comma-separated form, and an integration test renders a heatmap through the real pipeline to hold it.
375
+
376
+ - `AIKit/Markdown` code surfaces follow the colour scheme and the brand.
377
+
378
+ Fenced blocks and inline code were painted with `neutral-50` / `neutral-800`, literals that cannot take part in the `[data-theme='dark']` variable swap. In dark mode `prose-invert` flipped the surrounding text to light while the pinned fill stayed near-white, leaving inline code unreadable. Both now use the semantic `muted` / `foreground` pair, the same one the stylesheet already gives a prose blockquote.
379
+
380
+ Typography's inverted palette pinned the ink of a fenced block and of code in a quote to `#c7254e` regardless of the fill beneath it — 2.72:1 once that fill follows the dark scheme. Both now inherit the surface they sit on, so the ink moves with the fill.
381
+
382
+ - `MultiCombobox` renders its Base UI root with `autoHighlight`, matching `Combobox`. Without it
383
+ nothing was highlighted while filtering, so Enter closed the popup without selecting — a typed
384
+ free-form value was dropped silently when `allowCreate` was on.
385
+
386
+ - Legacy (non-argo) `Select` chrome now follows the colour scheme.
387
+
388
+ `Select.Trigger` and `DateRangePicker`'s trigger share `overlayTriggerVariants`, whose `primary`
389
+ variant painted a fixed white fill with `neutral-800` ink and a `neutral-200` edge. The `neutral`
390
+ ramp is scheme-blind — `[data-theme='dark']` redefines only the semantic tokens — so wherever a
391
+ subtree renders legacy chrome, dark mode left the trigger as a white slab with near-black text
392
+ inside a dark panel. It now uses `input-background` / `foreground` / `input`, the same trio the
393
+ `primary` `InputGroup` beside it already used, so a trigger and an input in one row stay the same
394
+ colour.
395
+
396
+ The highlighted option in `Select` and `Combobox` had the matching defect: a fixed `neutral-100`
397
+ fill under a scheme-following `text-foreground`, leaving the label at roughly 1.05:1 in dark. It
398
+ now uses the `secondary` pair, which carries its own foreground. (`muted` is not usable there — in
399
+ dark it equals `popover`, so the band would disappear into the listbox instead.) `Combobox`'s check
400
+ indicator moves with the label it pairs with.
401
+
402
+ Scope: only the `primary` variant. `secondary` keeps its fixed `neutral-100` fill under
403
+ `neutral-800` ink and stays light-pinned in dark.
404
+
405
+ Light-scheme note: the trigger fill is unchanged (`input-background` resolves to white), but its
406
+ ink and border now come from the brand's `foreground` / `input` slots instead of the `neutral`
407
+ ramp, and the option highlight from `secondary` instead of `neutral-100`. Storybook visual
408
+ baselines for `Select`, `Combobox` and `DateRangePicker` move accordingly.
409
+
410
+ - `Truncated` now ellipsizes and reveals its tooltip when the text cannot wrap
411
+
412
+ `-webkit-line-clamp` draws its ellipsis only on text it wrapped itself, so text that
413
+ cannot wrap — an inherited `white-space: nowrap`, which `Button`'s non-legacy base class
414
+ set applies — was cut off mid-word with no ellipsis at all. Truncation is now also marked
415
+ with `text-overflow: ellipsis`, which covers the single-line case; the wrapped case is
416
+ unchanged.
417
+
418
+ The tooltip had the matching gap: overflow was measured on the vertical axis only, so a
419
+ single unwrappable line never counted as truncated and the full text stayed unreachable.
420
+ It is now detected on either axis.
421
+
422
+ - Updated dependencies:
423
+ - @keboola/codemirror-lang-sql@0.7.3
424
+ - @keboola/brand-registry@1.9.4
425
+ - @keboola/tailwind-config@1.1.2
426
+
3
427
  ## 11.2.0
4
428
 
5
429
  ### Minor Changes