@keyfunc/keybricks-ds 1.0.4 → 2.3.1

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 (381) hide show
  1. package/DEPLOY.md +14 -5
  2. package/DESIGN.md +245 -59
  3. package/INTEGRATION.md +243 -49
  4. package/SKILL.md +13 -11
  5. package/_adherence.oxlintrc.json +183 -70
  6. package/_scripts/build-index.mjs +24 -4
  7. package/_scripts/build-tokens.mjs +5 -2
  8. package/assets/icons/icon-index.js +1 -1
  9. package/assets/icons/icon-index.json +1 -1
  10. package/components/_cards/accordion.card.html +2 -2
  11. package/components/_cards/action-chip.card.html +11 -9
  12. package/components/_cards/activity.card.html +3 -3
  13. package/components/_cards/alert.card.html +3 -3
  14. package/components/_cards/app-chrome.card.html +4 -4
  15. package/components/_cards/banner.card.html +1 -1
  16. package/components/_cards/breadcrumb.card.html +5 -5
  17. package/components/_cards/button-1-base.card.html +23 -26
  18. package/components/_cards/button-2-states.card.html +27 -25
  19. package/components/_cards/button-3-sizes-icons.card.html +20 -19
  20. package/components/_cards/button-4-group.card.html +14 -31
  21. package/components/_cards/canvas-zoom-controls.card.html +1 -1
  22. package/components/_cards/cards-indicators.card.html +2 -2
  23. package/components/_cards/charts-1-sparkline.card.html +5 -5
  24. package/components/_cards/charts-10-gauge.card.html +5 -5
  25. package/components/_cards/charts-11-funnel.card.html +3 -3
  26. package/components/_cards/charts-12-treemap.card.html +3 -3
  27. package/components/_cards/charts-14-radar.card.html +3 -3
  28. package/components/_cards/charts-15-distribution.card.html +2 -2
  29. package/components/_cards/charts-16-pareto.card.html +3 -3
  30. package/components/_cards/charts-17-candlestick.card.html +3 -3
  31. package/components/_cards/charts-18-sankey.card.html +3 -3
  32. package/components/_cards/charts-19-chord.card.html +3 -3
  33. package/components/_cards/charts-2-line.card.html +4 -4
  34. package/components/_cards/charts-20-sunburst.card.html +3 -3
  35. package/components/_cards/charts-21-worldmap.card.html +4 -4
  36. package/components/_cards/charts-22-gantt.card.html +3 -3
  37. package/components/_cards/charts-23-bar3d.card.html +3 -3
  38. package/components/_cards/charts-24-pie3d.card.html +3 -3
  39. package/components/_cards/charts-25-surface3d.card.html +3 -3
  40. package/components/_cards/charts-26-scatter3d.card.html +3 -3
  41. package/components/_cards/charts-27-ribbon3d.card.html +3 -3
  42. package/components/_cards/charts-3-bar.card.html +7 -7
  43. package/components/_cards/charts-4-barlist.card.html +3 -3
  44. package/components/_cards/charts-5-donut.card.html +4 -4
  45. package/components/_cards/charts-6-scatter.card.html +3 -3
  46. package/components/_cards/charts-7-heatmap.card.html +3 -3
  47. package/components/_cards/charts-8-waterfall.card.html +3 -3
  48. package/components/_cards/charts-9-bullet.card.html +3 -3
  49. package/components/_cards/charts-99-fullpage.card.html +5 -5
  50. package/components/_cards/checkbox.card.html +9 -6
  51. package/components/_cards/codeblock.card.html +2 -2
  52. package/components/_cards/combobox.card.html +6 -3
  53. package/components/_cards/context-menu.card.html +6 -3
  54. package/components/_cards/date-picker.card.html +10 -4
  55. package/components/_cards/diffvalue.card.html +3 -3
  56. package/components/_cards/divider.card.html +4 -4
  57. package/components/_cards/drawer.card.html +4 -4
  58. package/components/_cards/dropdown-menu.card.html +7 -7
  59. package/components/_cards/dropzone.card.html +5 -3
  60. package/components/_cards/empty-state.card.html +6 -6
  61. package/components/_cards/filter-bar.card.html +5 -5
  62. package/components/_cards/icon-button.card.html +14 -14
  63. package/components/_cards/icons-1-core.card.html +4 -4
  64. package/components/_cards/icons-2-feather.card.html +4 -4
  65. package/components/_cards/icons-3-unicons.card.html +4 -4
  66. package/components/_cards/input.card.html +5 -2
  67. package/components/_cards/kbd.card.html +2 -2
  68. package/components/_cards/keyvalue.card.html +2 -2
  69. package/components/_cards/listrow.card.html +2 -2
  70. package/components/_cards/loading.card.html +3 -3
  71. package/components/_cards/log-viewer.card.html +3 -3
  72. package/components/_cards/modal.card.html +9 -9
  73. package/components/_cards/multi-select.card.html +6 -3
  74. package/components/_cards/number-stepper.card.html +9 -5
  75. package/components/_cards/pagination.card.html +6 -3
  76. package/components/_cards/people.card.html +4 -4
  77. package/components/_cards/pipeline-node.card.html +8 -8
  78. package/components/_cards/pipeline-palette.card.html +5 -5
  79. package/components/_cards/radio.card.html +12 -7
  80. package/components/_cards/search.card.html +6 -3
  81. package/components/_cards/segmented-control.card.html +11 -10
  82. package/components/_cards/select.card.html +8 -4
  83. package/components/_cards/slider.card.html +2 -2
  84. package/components/_cards/smart-table.card.html +3 -3
  85. package/components/_cards/spreadsheet.card.html +3 -3
  86. package/components/_cards/stepper.card.html +3 -3
  87. package/components/_cards/table.card.html +165 -0
  88. package/components/_cards/tabs.card.html +6 -3
  89. package/components/_cards/tag.card.html +6 -3
  90. package/components/_cards/textarea.card.html +2 -2
  91. package/components/_cards/timeline.card.html +3 -3
  92. package/components/_cards/toast.card.html +10 -8
  93. package/components/_cards/toggles.card.html +15 -10
  94. package/components/_cards/tooltip.card.html +7 -7
  95. package/components/_cards/tree.card.html +3 -3
  96. package/components/_shared/card-kit.css +25 -0
  97. package/components/_shared/card-kit.js +50 -0
  98. package/components/_shared/src-buttons.js +1 -1
  99. package/components/_shared/src-charts.js +1 -1
  100. package/components/_shared/src-data.js +1 -1
  101. package/components/_shared/src-display.js +1 -1
  102. package/components/_shared/src-feedback.js +1 -1
  103. package/components/_shared/src-forms.js +1 -1
  104. package/components/_shared/src-icons.js +1 -1
  105. package/components/_shared/src-navigation.js +1 -1
  106. package/components/_shared/src-pipeline.js +1 -1
  107. package/components/_shared/src-tabs.js +1 -1
  108. package/components/_shared/view-source.js +3 -3
  109. package/components/_vue/index.mjs +16 -5
  110. package/components/buttons/ActionChip.d.ts +3 -1
  111. package/components/buttons/ActionChip.jsx +4 -4
  112. package/components/buttons/ActionChip.vue +4 -4
  113. package/components/buttons/Button.d.ts +5 -2
  114. package/components/buttons/Button.jsx +15 -18
  115. package/components/buttons/Button.vue +17 -18
  116. package/components/buttons/ButtonGroup.vue +1 -1
  117. package/components/buttons/IconButton.d.ts +2 -1
  118. package/components/buttons/IconButton.jsx +13 -9
  119. package/components/buttons/IconButton.vue +12 -9
  120. package/components/buttons/buttons.prompt.md +6 -5
  121. package/components/charts/Bar3D.jsx +2 -2
  122. package/components/charts/Bar3D.vue +3 -3
  123. package/components/charts/BarChart.jsx +4 -4
  124. package/components/charts/BarChart.vue +6 -6
  125. package/components/charts/BarList.jsx +4 -4
  126. package/components/charts/BarList.vue +4 -4
  127. package/components/charts/BoxPlot.jsx +1 -1
  128. package/components/charts/BoxPlot.vue +3 -3
  129. package/components/charts/BulletChart.jsx +6 -6
  130. package/components/charts/BulletChart.vue +6 -6
  131. package/components/charts/CandlestickChart.jsx +2 -2
  132. package/components/charts/CandlestickChart.vue +5 -5
  133. package/components/charts/ChartKit.jsx +8 -7
  134. package/components/charts/ChartLegend.vue +16 -0
  135. package/components/charts/ChartTooltip.vue +16 -0
  136. package/components/charts/ChordChart.jsx +2 -2
  137. package/components/charts/ChordChart.vue +3 -3
  138. package/components/charts/DonutChart.jsx +1 -1
  139. package/components/charts/DonutChart.vue +2 -2
  140. package/components/charts/FunnelChart.jsx +3 -3
  141. package/components/charts/FunnelChart.vue +4 -4
  142. package/components/charts/GanttChart.jsx +2 -2
  143. package/components/charts/GanttChart.vue +3 -3
  144. package/components/charts/GaugeChart.jsx +2 -2
  145. package/components/charts/GaugeChart.vue +2 -2
  146. package/components/charts/HeatmapChart.jsx +2 -2
  147. package/components/charts/HeatmapChart.vue +2 -2
  148. package/components/charts/Histogram.jsx +2 -2
  149. package/components/charts/Histogram.vue +4 -4
  150. package/components/charts/LineChart.jsx +1 -1
  151. package/components/charts/LineChart.vue +4 -4
  152. package/components/charts/ParetoChart.jsx +3 -3
  153. package/components/charts/ParetoChart.vue +5 -5
  154. package/components/charts/Pie3D.jsx +1 -1
  155. package/components/charts/Pie3D.vue +2 -2
  156. package/components/charts/PlotGrid.vue +18 -0
  157. package/components/charts/RadarChart.jsx +1 -1
  158. package/components/charts/RadarChart.vue +2 -2
  159. package/components/charts/Ribbon3D.jsx +2 -2
  160. package/components/charts/Ribbon3D.vue +3 -3
  161. package/components/charts/SankeyChart.jsx +2 -2
  162. package/components/charts/SankeyChart.vue +3 -3
  163. package/components/charts/Scatter3D.jsx +1 -1
  164. package/components/charts/Scatter3D.vue +2 -2
  165. package/components/charts/ScatterChart.jsx +3 -3
  166. package/components/charts/ScatterChart.vue +5 -5
  167. package/components/charts/Sparkline.jsx +1 -1
  168. package/components/charts/Sparkline.vue +1 -1
  169. package/components/charts/SunburstChart.jsx +1 -1
  170. package/components/charts/SunburstChart.vue +2 -2
  171. package/components/charts/Surface3D.jsx +2 -2
  172. package/components/charts/Surface3D.vue +3 -3
  173. package/components/charts/TreemapChart.jsx +2 -2
  174. package/components/charts/TreemapChart.vue +3 -3
  175. package/components/charts/WaterfallChart.jsx +2 -2
  176. package/components/charts/WaterfallChart.vue +6 -5
  177. package/components/charts/WorldMap.jsx +4 -4
  178. package/components/charts/WorldMap.vue +5 -5
  179. package/components/charts/XLabels.vue +11 -0
  180. package/components/data/CodeBlock.jsx +1 -1
  181. package/components/data/CodeBlock.vue +1 -1
  182. package/components/data/DiffValue.d.ts +1 -0
  183. package/components/data/DiffValue.jsx +1 -1
  184. package/components/data/DiffValue.vue +1 -1
  185. package/components/data/FilterBar.d.ts +2 -0
  186. package/components/data/FilterBar.jsx +10 -9
  187. package/components/data/FilterBar.vue +9 -8
  188. package/components/data/KeyValue.d.ts +1 -1
  189. package/components/data/KeyValue.jsx +4 -4
  190. package/components/data/KeyValue.vue +4 -4
  191. package/components/data/ListRow.jsx +2 -2
  192. package/components/data/ListRow.vue +2 -2
  193. package/components/data/Pagination.d.ts +5 -1
  194. package/components/data/Pagination.jsx +16 -12
  195. package/components/data/Pagination.vue +14 -9
  196. package/components/data/SmartTable.d.ts +4 -1
  197. package/components/data/SmartTable.jsx +16 -14
  198. package/components/data/SmartTable.vue +26 -23
  199. package/components/data/Spreadsheet.d.ts +2 -0
  200. package/components/data/Spreadsheet.jsx +16 -15
  201. package/components/data/Spreadsheet.vue +19 -18
  202. package/components/data/Table.d.ts +101 -0
  203. package/components/data/Table.jsx +223 -0
  204. package/components/data/Table.vue +153 -0
  205. package/components/data/data.prompt.md +3 -3
  206. package/components/display/Avatar.jsx +2 -2
  207. package/components/display/Avatar.vue +3 -2
  208. package/components/display/AvatarGroup.vue +3 -2
  209. package/components/display/Badge.d.ts +4 -1
  210. package/components/display/Badge.jsx +10 -5
  211. package/components/display/Badge.vue +1 -1
  212. package/components/display/Divider.vue +3 -3
  213. package/components/display/Donut.vue +3 -2
  214. package/components/display/IconTile.vue +10 -4
  215. package/components/display/Kbd.d.ts +1 -1
  216. package/components/display/Kbd.jsx +1 -1
  217. package/components/display/Kbd.vue +2 -2
  218. package/components/display/ProgressBar.jsx +1 -1
  219. package/components/display/ProgressBar.vue +1 -1
  220. package/components/display/StatCard.vue +1 -1
  221. package/components/display/Tag.d.ts +3 -1
  222. package/components/display/Tag.jsx +4 -4
  223. package/components/display/Tag.vue +4 -4
  224. package/components/feedback/Accordion.jsx +2 -2
  225. package/components/feedback/Accordion.vue +2 -2
  226. package/components/feedback/ActivityItem.d.ts +3 -1
  227. package/components/feedback/ActivityItem.jsx +6 -6
  228. package/components/feedback/ActivityItem.vue +5 -4
  229. package/components/feedback/Alert.jsx +4 -4
  230. package/components/feedback/Alert.vue +3 -3
  231. package/components/feedback/Banner.jsx +5 -5
  232. package/components/feedback/Banner.vue +5 -5
  233. package/components/feedback/ContextMenu.d.ts +3 -1
  234. package/components/feedback/ContextMenu.jsx +5 -5
  235. package/components/feedback/ContextMenu.vue +5 -5
  236. package/components/feedback/DotActivity.vue +3 -3
  237. package/components/feedback/Drawer.d.ts +2 -0
  238. package/components/feedback/Drawer.jsx +6 -6
  239. package/components/feedback/Drawer.vue +22 -15
  240. package/components/feedback/EmptyState.vue +2 -2
  241. package/components/feedback/LogViewer.vue +1 -1
  242. package/components/feedback/Modal.d.ts +2 -0
  243. package/components/feedback/Modal.jsx +12 -11
  244. package/components/feedback/Modal.vue +10 -8
  245. package/components/feedback/Skeleton.vue +2 -1
  246. package/components/feedback/Spinner.vue +2 -1
  247. package/components/feedback/Stepper.vue +1 -1
  248. package/components/feedback/Timeline.jsx +1 -1
  249. package/components/feedback/Timeline.vue +3 -3
  250. package/components/feedback/Toast.vue +4 -4
  251. package/components/feedback/ToastStack.vue +11 -0
  252. package/components/feedback/Tooltip.vue +19 -0
  253. package/components/feedback/feedback.prompt.md +4 -4
  254. package/components/forms/Checkbox.d.ts +3 -2
  255. package/components/forms/Checkbox.jsx +7 -7
  256. package/components/forms/Checkbox.vue +8 -8
  257. package/components/forms/Combobox.d.ts +2 -0
  258. package/components/forms/Combobox.jsx +12 -10
  259. package/components/forms/Combobox.vue +11 -10
  260. package/components/forms/DatePicker.d.ts +4 -0
  261. package/components/forms/DatePicker.jsx +16 -12
  262. package/components/forms/DatePicker.vue +15 -11
  263. package/components/forms/Dropzone.jsx +2 -2
  264. package/components/forms/Dropzone.vue +5 -5
  265. package/components/forms/Field.d.ts +5 -1
  266. package/components/forms/Field.jsx +4 -4
  267. package/components/forms/Field.vue +4 -4
  268. package/components/forms/Input.d.ts +5 -1
  269. package/components/forms/Input.jsx +8 -8
  270. package/components/forms/Input.vue +3 -3
  271. package/components/forms/MultiSelect.d.ts +2 -0
  272. package/components/forms/MultiSelect.jsx +13 -11
  273. package/components/forms/MultiSelect.vue +12 -11
  274. package/components/forms/NumberStepper.d.ts +3 -1
  275. package/components/forms/NumberStepper.jsx +7 -7
  276. package/components/forms/NumberStepper.vue +8 -7
  277. package/components/forms/Radio.d.ts +5 -3
  278. package/components/forms/Radio.jsx +13 -15
  279. package/components/forms/Radio.vue +9 -10
  280. package/components/forms/RadioGroup.vue +5 -5
  281. package/components/forms/SearchInput.d.ts +3 -1
  282. package/components/forms/SearchInput.jsx +6 -6
  283. package/components/forms/SearchInput.vue +7 -6
  284. package/components/forms/Select.d.ts +3 -2
  285. package/components/forms/Select.jsx +6 -7
  286. package/components/forms/Select.vue +6 -6
  287. package/components/forms/Slider.jsx +3 -3
  288. package/components/forms/Slider.vue +3 -3
  289. package/components/forms/Switch.d.ts +2 -2
  290. package/components/forms/Switch.jsx +20 -22
  291. package/components/forms/Switch.vue +22 -18
  292. package/components/forms/Textarea.vue +5 -5
  293. package/components/forms/forms.prompt.md +1 -1
  294. package/components/forms/input-base.js +2 -1
  295. package/components/icons/Icon.jsx +17 -1
  296. package/components/icons/Icon.prompt.md +2 -0
  297. package/components/icons/Icon.vue +1 -1
  298. package/components/icons/icon-paths.js +15 -0
  299. package/components/index.d.ts +1 -2
  300. package/components/index.js +4 -5
  301. package/components/navigation/Breadcrumb.jsx +6 -10
  302. package/components/navigation/Breadcrumb.vue +6 -12
  303. package/components/navigation/DocTree.jsx +3 -3
  304. package/components/navigation/DocTree.vue +3 -3
  305. package/components/navigation/PageHeading.vue +1 -1
  306. package/components/navigation/PageToolbar.vue +7 -6
  307. package/components/navigation/Sidebar.jsx +4 -23
  308. package/components/navigation/Sidebar.vue +5 -10
  309. package/components/navigation/ThemeToggle.vue +2 -1
  310. package/components/navigation/TopBar.d.ts +3 -1
  311. package/components/navigation/TopBar.jsx +6 -6
  312. package/components/navigation/Tree.jsx +5 -5
  313. package/components/navigation/Tree.vue +13 -8
  314. package/components/navigation/nav-model.js +23 -0
  315. package/components/navigation/theme-store.js +9 -0
  316. package/components/pipeline/ConfigPanel.d.ts +2 -0
  317. package/components/pipeline/ConfigPanel.jsx +9 -9
  318. package/components/pipeline/ConfigPanel.vue +9 -9
  319. package/components/pipeline/DataPreview.d.ts +3 -1
  320. package/components/pipeline/DataPreview.jsx +7 -7
  321. package/components/pipeline/DataPreview.vue +7 -7
  322. package/components/pipeline/NodeCard.jsx +3 -3
  323. package/components/pipeline/NodeCard.vue +3 -3
  324. package/components/pipeline/PaletteItem.vue +2 -2
  325. package/components/pipeline/PaletteListItem.vue +1 -1
  326. package/components/pipeline/PalettePanel.jsx +2 -2
  327. package/components/pipeline/PalettePanel.vue +1 -1
  328. package/components/pipeline/Port.vue +15 -0
  329. package/components/pipeline/PortLabel.vue +15 -0
  330. package/components/pipeline/ZoomControls.vue +1 -1
  331. package/components/pipeline/port-skin.js +13 -0
  332. package/components/px.js +4 -0
  333. package/components/tabs/PanelTabs.vue +5 -4
  334. package/components/tabs/PillTabs.vue +5 -4
  335. package/components/tabs/SegmentedControl.d.ts +2 -2
  336. package/components/tabs/SegmentedControl.jsx +7 -7
  337. package/components/tabs/SegmentedControl.vue +7 -7
  338. package/components/tabs/Tabs.d.ts +5 -3
  339. package/components/tabs/Tabs.jsx +15 -15
  340. package/components/tabs/Tabs.vue +5 -5
  341. package/components/tabs/ViewToggle.d.ts +3 -1
  342. package/components/tabs/ViewToggle.jsx +5 -5
  343. package/components/tabs/ViewToggle.vue +5 -5
  344. package/components/tabs/tabs.prompt.md +1 -1
  345. package/keybricks.css +4 -0
  346. package/package.json +6 -3
  347. package/readme.md +91 -122
  348. package/readme.zh-CN.md +121 -0
  349. package/tokens/alpha.css +3 -1
  350. package/tokens/charts.css +20 -17
  351. package/tokens/colors.css +3 -3
  352. package/tokens/dark.css +3 -3
  353. package/tokens/hover.css +32 -0
  354. package/tokens/interaction.css +3 -1
  355. package/tokens/layout.css +31 -7
  356. package/tokens/tokens.d.ts +58 -0
  357. package/tokens/tokens.js +179 -92
  358. package/tokens/tokens.json +327 -114
  359. package/tokens/typography.css +45 -15
  360. package/components/_cards/brand-visual.card.html +0 -43
  361. package/components/_cards/datatable.card.html +0 -48
  362. package/components/_shared/src-brand.js +0 -1
  363. package/components/brand/BrandVisual.d.ts +0 -17
  364. package/components/brand/BrandVisual.jsx +0 -29
  365. package/components/brand/BrandVisual.vue +0 -34
  366. package/components/brand/_dir.card.html +0 -13
  367. package/components/brand/brandMotifs.js +0 -210
  368. package/components/buttons/_dir.card.html +0 -13
  369. package/components/charts/ChartKit.vue +0 -47
  370. package/components/charts/_dir.card.html +0 -7
  371. package/components/data/DataTable.d.ts +0 -28
  372. package/components/data/DataTable.jsx +0 -32
  373. package/components/data/DataTable.vue +0 -25
  374. package/components/data/_dir.card.html +0 -13
  375. package/components/display/_dir.card.html +0 -13
  376. package/components/feedback/_dir.card.html +0 -13
  377. package/components/forms/_dir.card.html +0 -13
  378. package/components/icons/_dir.card.html +0 -13
  379. package/components/navigation/_dir.card.html +0 -13
  380. package/components/pipeline/_dir.card.html +0 -13
  381. package/components/tabs/_dir.card.html +0 -13
@@ -1,15 +1,16 @@
1
1
  <script setup>
2
2
  import IconTile from '../display/Badge.vue'
3
- // Activity feed row: 32px icon tile + 13px/500 title + 12px meta + optional right action.
3
+ // Activity feed row: 32px icon tile + 14px/500 title + 12px meta + optional right text action (emits `action`).
4
4
  defineProps({ icon: { type: String, default: 'file-text' }, title: String, meta: String, action: String, style: Object })
5
+ const emit = defineEmits(['action'])
5
6
  </script>
6
7
  <template>
7
- <div :style="{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 16px', ...style }">
8
+ <div :style="{ display: 'flex', alignItems: 'center', gap: '12px', padding: '12px 16px', ...style }">
8
9
  <IconTile :icon="icon" kind="row" style="background:var(--info-bg);border:none;color:var(--info-fg)" />
9
10
  <div style="display:flex;flex-direction:column;flex:1;min-width:0">
10
11
  <span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ title }}</span>
11
- <span v-if="meta" style="font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-secondary)">{{ meta }}</span>
12
+ <span v-if="meta" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);color:var(--text-secondary)">{{ meta }}</span>
12
13
  </div>
13
- <span v-if="action" style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);letter-spacing:var(--tracking-base);color:var(--text-secondary);cursor:pointer">{{ action }}</span>
14
+ <button v-if="action" type="button" data-ink="" @click="emit('action')" style="border:none;background:transparent;padding:0;font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--ink, var(--text-secondary));cursor:pointer;white-space:nowrap">{{ action }}</button>
14
15
  </div>
15
16
  </template>
@@ -8,15 +8,15 @@ const alertTones = {
8
8
  danger: { bg: "var(--danger-bg)", fg: "var(--danger-text)", icon: "triangle-alert" },
9
9
  };
10
10
 
11
- /** Inline banner — tinted, no border, 13px title + 12px body. */
11
+ /** Inline banner — tinted, no border; title and body at the base size. The icon is centred on the first text line, never on the block. */
12
12
  export function Alert({ tone = "info", title, children, action, style }) {
13
13
  const t = alertTones[tone];
14
14
  return (
15
15
  <div style={{ display: "flex", gap: 10, alignItems: "flex-start", background: t.bg, borderRadius: "var(--radius-md)", padding: "10px 12px", ...style }}>
16
- <span style={{ color: t.fg, display: "inline-flex", marginTop: 1 }}><Icon name={t.icon} size={15} /></span>
16
+ <span style={{ color: t.fg, display: "inline-flex", alignItems: "center", flexShrink: 0, height: "var(--leading-base)" }}><Icon name={t.icon} size={15} /></span>
17
17
  <div style={{ display: "flex", flexDirection: "column", gap: 2, flex: 1, minWidth: 0 }}>
18
18
  {title ? <span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: t.fg }}>{title}</span> : null}
19
- {children ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: "var(--text-body)" }}>{children}</span> : null}
19
+ {children ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-body)" }}>{children}</span> : null}
20
20
  </div>
21
21
  {action}
22
22
  </div>
@@ -29,7 +29,7 @@ export function EmptyState({ icon = "file-text", title, hint, action, style }) {
29
29
  <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 6, padding: "40px 24px", textAlign: "center", ...style }}>
30
30
  <span style={{ color: "var(--text-muted)", marginBottom: 6 }}><Icon name={icon} size={22} strokeWidth={1.5} /></span>
31
31
  <span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{title}</span>
32
- {hint ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: "var(--text-secondary)", maxWidth: 320 }}>{hint}</span> : null}
32
+ {hint ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-secondary)", maxWidth: 320 }}>{hint}</span> : null}
33
33
  {action ? <span style={{ paddingTop: 10 }}>{action}</span> : null}
34
34
  </div>
35
35
  );
@@ -12,11 +12,11 @@ const props = defineProps({ tone: { type: String, default: 'info' }, title: Stri
12
12
  const t = computed(() => alertTones[props.tone])
13
13
  </script>
14
14
  <template>
15
- <div :style="{ display: 'flex', gap: 10, alignItems: 'flex-start', background: t.bg, borderRadius: 'var(--radius-md)', padding: '10px 12px', ...style }">
16
- <span :style="{ color: t.fg, display: 'inline-flex', marginTop: 1 }"><Icon :name="t.icon" :size="15" /></span>
15
+ <div :style="{ display: 'flex', gap: '10px', alignItems: 'flex-start', background: t.bg, borderRadius: 'var(--radius-md)', padding: '10px 12px', ...style }">
16
+ <span :style="{ color: t.fg, display: 'inline-flex', alignItems: 'center', flexShrink: 0, height: 'var(--leading-base)' }"><Icon :name="t.icon" :size="15" /></span>
17
17
  <div style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0">
18
18
  <span v-if="title" :style="{ fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-base)', lineHeight: 'var(--leading-base)', letterSpacing: 'var(--tracking-base)', color: t.fg }">{{ title }}</span>
19
- <span style="font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-body)"><slot /></span>
19
+ <span style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-body)"><slot /></span>
20
20
  </div>
21
21
  <slot name="action" />
22
22
  </div>
@@ -13,13 +13,13 @@ const bannerTones = {
13
13
  export function Banner({ tone = "info", title, action, onAction, dismissible, onDismiss, children, style }) {
14
14
  const t = bannerTones[tone];
15
15
  return (
16
- <div style={{ display: "flex", alignItems: "center", gap: 10, background: t.bg, borderLeft: `3px solid ${t.fg}`, padding: "10px 12px 10px 14px", boxSizing: "border-box", ...style }}>
17
- <span style={{ color: t.fg, display: "inline-flex", flexShrink: 0 }}><Icon name={t.icon} size={16} /></span>
18
- <span style={{ flex: 1, minWidth: 0, fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: "var(--text-body)" }}>
16
+ <div style={{ display: "flex", alignItems: "flex-start", gap: 10, background: t.bg, borderLeft: `3px solid ${t.fg}`, padding: "10px 12px 10px 14px", boxSizing: "border-box", ...style }}>
17
+ <span style={{ color: t.fg, display: "inline-flex", alignItems: "center", flexShrink: 0, height: "var(--leading-base)" }}><Icon name={t.icon} size={16} /></span>
18
+ <span style={{ flex: 1, minWidth: 0, fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-body)" }}>
19
19
  {title && <strong style={{ fontWeight: "var(--weight-medium)", color: t.fg, paddingRight: 6 }}>{title}</strong>}{children}
20
20
  </span>
21
- {action && <button type="button" onClick={onAction} style={{ border: "none", background: "transparent", cursor: "pointer", padding: 0, fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-sm)", color: t.fg, textDecoration: "underline", whiteSpace: "nowrap", flexShrink: 0 }}>{action}</button>}
22
- {dismissible && <IconButton icon="x" size="xs" muted label="Dismiss" onClick={onDismiss} />}
21
+ {action && <button type="button" onClick={onAction} data-link="" style={{ border: "none", background: "transparent", cursor: "pointer", padding: 0, height: "var(--leading-base)", fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", color: t.fg, textDecorationLine: "underline", textDecorationThickness: "var(--link-thick, 1px)", textUnderlineOffset: 2, whiteSpace: "nowrap", flexShrink: 0 }}>{action}</button>}
22
+ {dismissible && <IconButton icon="x" size="sm" muted label="Dismiss" onClick={onDismiss} style={{ margin: "-2px 0" }} />}
23
23
  </div>
24
24
  );
25
25
  }
@@ -14,12 +14,12 @@ const emit = defineEmits(['action', 'dismiss'])
14
14
  const t = computed(() => bannerTones[props.tone])
15
15
  </script>
16
16
  <template>
17
- <div :style="{ display: 'flex', alignItems: 'center', gap: '10px', background: t.bg, borderLeft: `3px solid ${t.fg}`, padding: '10px 12px 10px 14px', boxSizing: 'border-box', ...style }">
18
- <span :style="{ color: t.fg, display: 'inline-flex', flexShrink: 0 }"><Icon :name="t.icon" :size="16" /></span>
19
- <span style="flex:1;min-width:0;font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-body)">
17
+ <div :style="{ display: 'flex', alignItems: 'flex-start', gap: '10px', background: t.bg, borderLeft: `3px solid ${t.fg}`, padding: '10px 12px 10px 14px', boxSizing: 'border-box', ...style }">
18
+ <span :style="{ color: t.fg, display: 'inline-flex', alignItems: 'center', flexShrink: 0, height: 'var(--leading-base)' }"><Icon :name="t.icon" :size="16" /></span>
19
+ <span style="flex:1;min-width:0;font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-body)">
20
20
  <strong v-if="title" :style="{ fontWeight: 'var(--weight-medium)', color: t.fg, paddingRight: '6px' }">{{ title }}</strong><slot />
21
21
  </span>
22
- <button v-if="action" type="button" @click="emit('action')" :style="{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 0, fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-sm)', color: t.fg, textDecoration: 'underline', whiteSpace: 'nowrap', flexShrink: 0 }">{{ action }}</button>
23
- <IconButton v-if="dismissible" icon="x" size="xs" muted label="Dismiss" @click="emit('dismiss')" />
22
+ <button v-if="action" type="button" data-link="" @click="emit('action')" :style="{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 0, height: 'var(--leading-base)', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-base)', color: t.fg, textDecorationLine: 'underline', textDecorationThickness: 'var(--link-thick, 1px)', textUnderlineOffset: '2px', whiteSpace: 'nowrap', flexShrink: 0 }">{{ action }}</button>
23
+ <IconButton v-if="dismissible" icon="x" size="sm" muted label="Dismiss" :style="{ margin: '-2px 0' }" @click="emit('dismiss')" />
24
24
  </div>
25
25
  </template>
@@ -1,8 +1,10 @@
1
1
  import * as React from "react";
2
- /** Floating context menu — r10, soft shadow, 13px rows, red danger row. */
2
+ /** Floating context menu — r10, soft shadow, control-height rows at the region's tier (32px / 14px at md), red danger row. */
3
3
  export interface ContextMenuProps {
4
4
  items?: { label?: string; icon?: string; danger?: boolean; divider?: boolean }[];
5
5
  onSelect?: (label: string) => void;
6
+ /** Size tier of the rows. Omit to inherit the tier of the control that opened the menu (the default). */
7
+ size?: "sm" | "md" | "lg";
6
8
  style?: React.CSSProperties;
7
9
  }
8
10
  export declare function ContextMenu(props: ContextMenuProps): JSX.Element;
@@ -1,16 +1,16 @@
1
1
  import React from "react";
2
2
  import { Icon } from "../icons/Icon.jsx";
3
3
 
4
- /** Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}]. */
5
- export function ContextMenu({ items = [], onSelect, style }) {
4
+ /** Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}]. Rows are control-height at the region's tier (a menu inherits the tier of the control that opened it); `size` overrides. */
5
+ export function ContextMenu({ items = [], onSelect, size, style }) {
6
6
  const [hover, setHover] = React.useState(-1);
7
7
  return (
8
- <div style={{ background: "var(--bg-surface)", borderRadius: "var(--radius-lg)", border: "1px solid var(--border-card)", boxShadow: "var(--shadow-menu)", padding: 4, minWidth: 148, display: "inline-flex", flexDirection: "column", ...style }}>
8
+ <div role="menu" data-size={size} style={{ background: "var(--bg-surface)", borderRadius: "var(--radius-lg)", border: "1px solid var(--border-card)", boxShadow: "var(--shadow-menu)", padding: 4, minWidth: 148, display: "inline-flex", flexDirection: "column", ...style }}>
9
9
  {items.map((it, i) => it.divider ? <div key={i} style={{ height: 1, background: "var(--border-subtle)", margin: "4px 0" }} /> : (
10
10
  <button key={i} type="button" onClick={() => onSelect && onSelect(it.label)}
11
11
  onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(-1)}
12
- style={{ display: "flex", alignItems: "center", gap: 10, padding: "7px 10px", borderRadius: "var(--radius-md)", border: "none", cursor: "pointer", background: hover === i ? "var(--bg-hover)" : "transparent", fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: it.danger ? "var(--danger)" : "var(--text-heading)", textAlign: "left" }}>
13
- {it.icon ? <Icon name={it.icon} size={15} /> : null}{it.label}
12
+ role="menuitem" style={{ display: "flex", alignItems: "center", gap: 10, height: "var(--control-h)", padding: "0 10px", boxSizing: "border-box", borderRadius: "var(--radius-md)", border: "none", cursor: "pointer", background: hover === i ? "var(--bg-hover)" : "transparent", fontFamily: "var(--font-sans)", fontSize: "var(--control-text)", lineHeight: "var(--control-leading)", letterSpacing: "var(--control-tracking)", color: it.danger ? "var(--danger)" : "var(--text-heading)", textAlign: "left", whiteSpace: "nowrap" }}>
13
+ {it.icon ? <Icon name={it.icon} size={16} style={{ width: "var(--control-icon)", height: "var(--control-icon)" }} /> : null}{it.label}
14
14
  </button>
15
15
  ))}
16
16
  </div>
@@ -1,18 +1,18 @@
1
1
  <script setup>
2
2
  import { ref } from 'vue'
3
3
  import Icon from '../icons/Icon.vue'
4
- // Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}].
5
- defineProps({ items: { type: Array, default: () => [] }, style: Object })
4
+ // Right-click context menu (Preview / Duplicate / Delete). items: [{label, icon, danger, divider}]. Rows are control-height at the region's tier (a menu inherits the tier of the control that opened it); `size` overrides.
5
+ defineProps({ items: { type: Array, default: () => [] }, size: String, style: Object })
6
6
  const emit = defineEmits(['select'])
7
7
  const hover = ref(-1)
8
8
  </script>
9
9
  <template>
10
- <div :style="{ background: 'var(--bg-surface)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-menu)', padding: 4, minWidth: 148, display: 'inline-flex', flexDirection: 'column', ...style }">
10
+ <div role="menu" :data-size="size" :style="{ background: 'var(--bg-surface)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-menu)', padding: '4px', minWidth: '148px', display: 'inline-flex', flexDirection: 'column', ...style }">
11
11
  <template v-for="(it, i) in items" :key="i">
12
12
  <div v-if="it.divider" style="height:1px;background:var(--border-subtle);margin:4px 0" />
13
13
  <button v-else type="button" @click="emit('select', it.label)" @mouseenter="hover = i" @mouseleave="hover = -1"
14
- :style="{ display: 'flex', alignItems: 'center', gap: 10, padding: '7px 10px', borderRadius: 'var(--radius-md)', border: 'none', cursor: 'pointer', background: hover === i ? 'var(--bg-hover)' : 'transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-base)', lineHeight: 'var(--leading-base)', letterSpacing: 'var(--tracking-base)', color: it.danger ? 'var(--danger)' : 'var(--text-heading)', textAlign: 'left' }">
15
- <Icon v-if="it.icon" :name="it.icon" :size="15" />{{ it.label }}
14
+ role="menuitem" :style="{ display: 'flex', alignItems: 'center', gap: '10px', height: 'var(--control-h)', padding: '0 10px', boxSizing: 'border-box', borderRadius: 'var(--radius-md)', border: 'none', cursor: 'pointer', background: hover === i ? 'var(--bg-hover)' : 'transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--control-text)', lineHeight: 'var(--control-leading)', letterSpacing: 'var(--control-tracking)', color: it.danger ? 'var(--danger)' : 'var(--text-heading)', textAlign: 'left', whiteSpace: 'nowrap' }">
15
+ <Icon v-if="it.icon" :name="it.icon" :size="16" :style="{ width: 'var(--control-icon)', height: 'var(--control-icon)' }" />{{ it.label }}
16
16
  </button>
17
17
  </template>
18
18
  </div>
@@ -5,10 +5,10 @@ const props = defineProps({ tone: { type: String, default: 'success' }, text: St
5
5
  const dot = computed(() => tonesMap[props.tone])
6
6
  </script>
7
7
  <template>
8
- <div :style="{ display: 'flex', gap: 8, alignItems: 'flex-start', ...style }">
9
- <span :style="{ width: 8, height: 8, borderRadius: 'var(--radius-full)', background: dot, marginTop: 5, flexShrink: 0 }" />
8
+ <div :style="{ display: 'flex', gap: '8px', alignItems: 'flex-start', ...style }">
9
+ <span :style="{ width: '8px', height: '8px', borderRadius: 'var(--radius-full)', background: dot, marginTop: '5px', flexShrink: 0 }" />
10
10
  <div style="display:flex;flex-direction:column;min-width:0">
11
- <span style="font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-heading);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ text }}</span>
11
+ <span style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-heading);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ text }}</span>
12
12
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);letter-spacing:var(--tracking-xs);color:var(--text-muted)">{{ time }}</span>
13
13
  </div>
14
14
  </div>
@@ -11,6 +11,8 @@ export interface DrawerProps {
11
11
  inline?: boolean;
12
12
  /** Inline height (default 420) */
13
13
  height?: number | string;
14
+ /** Size tier of the drawer region. Omit to inherit (→ md): drawers are working surfaces, not dialogs. */
15
+ size?: "sm" | "md" | "lg";
14
16
  footer?: React.ReactNode;
15
17
  onClose?: () => void;
16
18
  children?: React.ReactNode;
@@ -1,20 +1,20 @@
1
1
  import React from "react";
2
2
  import { IconButton } from "../buttons/IconButton.jsx";
3
3
 
4
- /** Right slide-out panel over a scrim: 17.5px title + subtitle, scrollable body, right-aligned footer slot. inline=true renders it in-flow for embedding in cards/demos. */
5
- export function Drawer({ open = true, title, subtitle, width = 400, inline, height, footer, onClose, children, style }) {
4
+ /** Right slide-out panel over a scrim: title + subtitle, scrollable body, right-aligned footer slot. inline=true renders it in-flow for embedding in cards/demos. A size region (default inherit → md: drawers are working surfaces, not dialogs); pass `size` to override. */
5
+ export function Drawer({ open = true, title, subtitle, width = 400, inline, height, size, footer, onClose, children, style }) {
6
6
  if (!open) return null;
7
7
  const panel = (
8
- <div style={{ width, maxWidth: "100%", height: inline ? (height || 420) : "100%", background: "var(--bg-raised)", borderLeft: "1px solid var(--border-card)", boxShadow: inline ? "var(--shadow-menu)" : "-8px 0 24px rgba(0,0,0,.12)", display: "flex", flexDirection: "column", boxSizing: "border-box", ...(inline ? { border: "1px solid var(--border-card)", borderRadius: "var(--radius-lg)" } : null), ...style }}>
8
+ <div role="dialog" aria-label={title} data-size={size} style={{ width, maxWidth: "100%", height: inline ? (height || 420) : "100%", background: "var(--bg-raised)", borderLeft: "1px solid var(--border-card)", boxShadow: inline ? "var(--shadow-menu)" : "-8px 0 24px rgba(0,0,0,.12)", display: "flex", flexDirection: "column", boxSizing: "border-box", ...(inline ? { border: "1px solid var(--border-card)", borderRadius: "var(--radius-lg)" } : null), ...style }}>
9
9
  <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", padding: "16px 16px 12px", borderBottom: "1px solid var(--border-card)", flexShrink: 0 }}>
10
10
  <div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
11
11
  <span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-semibold)", fontSize: "var(--text-md)", lineHeight: "var(--leading-md)", letterSpacing: "var(--tracking-md)", color: "var(--text-heading)" }}>{title}</span>
12
- {subtitle && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: "var(--text-secondary)" }}>{subtitle}</span>}
12
+ {subtitle && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", color: "var(--text-secondary)" }}>{subtitle}</span>}
13
13
  </div>
14
- <IconButton icon="x" size="md" label="Close" onClick={onClose} />
14
+ <IconButton icon="x" label="Close" onClick={onClose} style={{ margin: "calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0" }} />
15
15
  </div>
16
16
  <div style={{ flex: 1, overflowY: "auto", padding: 16 }}>{children}</div>
17
- {footer && <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, padding: "12px 16px", borderTop: "1px solid var(--border-card)", flexShrink: 0 }}>{footer}</div>}
17
+ {footer && <div style={{ display: "flex", justifyContent: "flex-end", gap: "var(--control-gap)", padding: "12px 16px", borderTop: "1px solid var(--border-card)", flexShrink: 0 }}>{footer}</div>}
18
18
  </div>
19
19
  );
20
20
  if (inline) return panel;
@@ -1,24 +1,31 @@
1
1
  <script setup>
2
+ import { px } from '../px.js'
2
3
  import IconButton from '../buttons/IconButton.vue'
3
- // Right slide-out panel over a scrim; inline=true renders in-flow for demos. Header, scrollable body, footer slot.
4
- defineProps({ open: { type: Boolean, default: true }, title: String, subtitle: String, width: { type: Number, default: 400 }, inline: Boolean, height: { type: [Number, String], default: 420 }, style: Object })
4
+ // Right slide-out panel over a scrim: title + subtitle, scrollable body, right-aligned footer slot.
5
+ // inline=true renders it in-flow (cards, demos); otherwise it teleports to <body> over a scrim. Click the scrim or × to emit close.
6
+ // A size region (default inherit → md: drawers are working surfaces, not dialogs); pass `size` to override.
7
+ const props = defineProps({ open: { type: Boolean, default: true }, title: String, subtitle: String, width: { type: [Number, String], default: 400 }, inline: Boolean, height: { type: [Number, String], default: 420 }, size: String, style: Object })
5
8
  const emit = defineEmits(['close'])
9
+ const contents = { display: 'contents' }
6
10
  </script>
7
11
  <template>
8
- <component :is="inline ? 'div' : 'teleport'" v-if="open" v-bind="inline ? {} : { to: 'body' }">
9
- <div v-if="!inline" @click="emit('close')" style="position:fixed;inset:0;z-index:var(--z-modal);background:var(--bg-scrim);display:flex;justify-content:flex-end;animation:kb-fade-in var(--dur-fast) var(--ease-out)">
10
- <div @click.stop style="height:100%;animation:kb-slide-in var(--dur) var(--ease-out)"><DrawerPanel /></div>
11
- </div>
12
- <div :style="{ width: width + 'px', height: inline ? (typeof height === 'number' ? height + 'px' : height) : '100%', background: 'var(--bg-raised)', border: '1px solid var(--border-card)', borderRadius: inline ? 'var(--radius-lg)' : 0, boxShadow: 'var(--shadow-menu)', display: 'flex', flexDirection: 'column', boxSizing: 'border-box', ...style }">
13
- <div style="display:flex;align-items:flex-start;justify-content:space-between;padding:16px 16px 12px;border-bottom:1px solid var(--border-card);flex-shrink:0">
14
- <div style="display:flex;flex-direction:column;gap:3px">
15
- <span style="font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-md);color:var(--text-heading)">{{ title }}</span>
16
- <span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-sm);color:var(--text-secondary)">{{ subtitle }}</span>
12
+ <Teleport to="body" :disabled="inline">
13
+ <div v-if="open" @click="!inline && emit('close')"
14
+ :style="inline ? contents : { position: 'fixed', inset: 0, zIndex: 'var(--z-modal)', background: 'var(--bg-scrim)', display: 'flex', justifyContent: 'flex-end', animation: 'kb-fade-in var(--dur-fast) var(--ease-out)' }">
15
+ <div @click.stop :style="inline ? contents : { height: '100%', animation: 'kb-slide-in var(--dur) var(--ease-out)' }">
16
+ <div role="dialog" :aria-label="title" :data-size="size"
17
+ :style="{ width: px(width), maxWidth: '100%', height: inline ? px(height) : '100%', background: 'var(--bg-raised)', borderLeft: '1px solid var(--border-card)', boxShadow: inline ? 'var(--shadow-menu)' : '-8px 0 24px rgba(0,0,0,.12)', display: 'flex', flexDirection: 'column', boxSizing: 'border-box', ...(inline ? { border: '1px solid var(--border-card)', borderRadius: 'var(--radius-lg)' } : null), ...style }">
18
+ <div style="display:flex;align-items:flex-start;justify-content:space-between;padding:16px 16px 12px;border-bottom:1px solid var(--border-card);flex-shrink:0">
19
+ <div style="display:flex;flex-direction:column;gap:3px">
20
+ <span style="font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-md);line-height:var(--leading-md);letter-spacing:var(--tracking-md);color:var(--text-heading)">{{ title }}</span>
21
+ <span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);color:var(--text-secondary)">{{ subtitle }}</span>
22
+ </div>
23
+ <IconButton icon="x" label="Close" :style="{ margin: 'calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0' }" @click="emit('close')" />
24
+ </div>
25
+ <div style="flex:1;overflow-y:auto;padding:16px"><slot /></div>
26
+ <div v-if="$slots.footer" style="display:flex;justify-content:flex-end;gap:var(--control-gap);padding:12px 16px;border-top:1px solid var(--border-card);flex-shrink:0"><slot name="footer" /></div>
17
27
  </div>
18
- <IconButton icon="x" size="md" label="Close" @click="emit('close')" />
19
28
  </div>
20
- <div style="flex:1;overflow-y:auto;padding:16px"><slot /></div>
21
- <div v-if="$slots.footer" style="display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border-card);flex-shrink:0"><slot name="footer" /></div>
22
29
  </div>
23
- </component>
30
+ </Teleport>
24
31
  </template>
@@ -3,10 +3,10 @@ import Icon from '../icons/Icon.vue'
3
3
  defineProps({ icon: { type: String, default: 'file-text' }, title: String, hint: String, style: Object })
4
4
  </script>
5
5
  <template>
6
- <div :style="{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 6, padding: '40px 24px', textAlign: 'center', ...style }">
6
+ <div :style="{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '6px', padding: '40px 24px', textAlign: 'center', ...style }">
7
7
  <span style="color:var(--text-muted);margin-bottom:6px"><Icon :name="icon" :size="22" :stroke-width="1.5" /></span>
8
8
  <span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ title }}</span>
9
- <span v-if="hint" style="font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-secondary);max-width:320px">{{ hint }}</span>
9
+ <span v-if="hint" style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-secondary);max-width:320px">{{ hint }}</span>
10
10
  <span v-if="$slots.action" style="padding-top:10px"><slot name="action" /></span>
11
11
  </div>
12
12
  </template>
@@ -2,7 +2,7 @@
2
2
  defineProps({ lines: { type: Array, default: () => [] }, height: [Number, String], style: Object })
3
3
  </script>
4
4
  <template>
5
- <div :style="{ background: 'var(--bg-inset)', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-subtle)', padding: '10px 12px', display: 'flex', flexDirection: 'column', gap: 6, overflowY: 'auto', height, boxSizing: 'border-box', ...style }">
5
+ <div :style="{ background: 'var(--bg-inset)', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-subtle)', padding: '10px 12px', display: 'flex', flexDirection: 'column', gap: '6px', overflowY: 'auto', height, boxSizing: 'border-box', ...style }">
6
6
  <span v-for="(l, i) in lines" :key="i" :style="{ fontFamily: 'var(--font-mono)', fontSize: 'var(--text-xs)', lineHeight: 'var(--leading-xs)', color: typeof l === 'object' ? (l.color || 'var(--text-body)') : 'var(--text-body)', whiteSpace: 'pre' }">{{ typeof l === 'object' ? l.text : l }}</span>
7
7
  </div>
8
8
  </template>
@@ -16,6 +16,8 @@ export interface ModalProps {
16
16
  /** Right-aligned footer buttons */
17
17
  footer?: React.ReactNode;
18
18
  width?: number;
19
+ /** Size tier of the dialog region. Defaults to "lg" (36px controls) regardless of the page behind it — a dialog does not compete for space. */
20
+ size?: "sm" | "md" | "lg";
19
21
  children?: React.ReactNode;
20
22
  style?: React.CSSProperties;
21
23
  }
@@ -22,21 +22,22 @@ export function Tooltip({ label, placement = "top", children, style }) {
22
22
  }
23
23
 
24
24
  /** Centered modal dialog with scrim. width defaults to 440. */
25
- export function Modal({ open, title, subtitle, onClose, footer, width = 440, children, style }) {
25
+ /** Modal dialog. A size region that defaults to lg (40px controls) whatever the page behind it uses — a dialog does not compete for space; pass `size` to override. 24px padding, footer buttons 8px apart. */
26
+ export function Modal({ open, title, subtitle, onClose, footer, width = 440, size = "lg", children, style }) {
26
27
  if (!open) return null;
27
28
  return (
28
29
  <div style={{ position: "fixed", inset: 0, zIndex: "var(--z-modal)", background: "var(--bg-scrim)", display: "flex", alignItems: "center", justifyContent: "center", animation: "kb-fade-in var(--dur-fast) var(--ease-out)" }} onClick={onClose}>
29
- <div onClick={(e) => e.stopPropagation()}
30
+ <div role="dialog" aria-modal="true" data-size={size} onClick={(e) => e.stopPropagation()}
30
31
  style={{ width, maxWidth: "92vw", background: "var(--bg-raised)", borderRadius: "var(--radius-lg)", border: "1px solid var(--border-card)", boxShadow: "var(--shadow-modal)", animation: "kb-rise var(--dur) var(--ease-out)", ...style }}>
31
- <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", padding: "17px 17px 0" }}>
32
- <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
32
+ <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16, padding: "24px 24px 0" }}>
33
+ <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
33
34
  <span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-semibold)", fontSize: "var(--text-md)", lineHeight: "var(--leading-md)", letterSpacing: "var(--tracking-md)", color: "var(--text-heading)" }}>{title}</span>
34
- {subtitle ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: "var(--text-secondary)" }}>{subtitle}</span> : null}
35
+ {subtitle ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", color: "var(--text-secondary)" }}>{subtitle}</span> : null}
35
36
  </div>
36
- <IconButton icon="x" size="md" onClick={onClose} label="Close" />
37
+ <IconButton icon="x" onClick={onClose} label="Close" style={{ margin: "calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0" }} />
37
38
  </div>
38
- {children ? <div style={{ padding: "16px 17px 0" }}>{children}</div> : null}
39
- <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, padding: 17 }}>{footer}</div>
39
+ {children ? <div style={{ padding: "20px 24px 0" }}>{children}</div> : null}
40
+ <div style={{ display: "flex", justifyContent: "flex-end", gap: "var(--control-gap)", padding: 24 }}>{footer}</div>
40
41
  </div>
41
42
  </div>
42
43
  );
@@ -49,12 +50,12 @@ export function Toast({ tone = "success", title, meta, onClose, style }) {
49
50
  const t = toastTones[tone];
50
51
  return (
51
52
  <div style={{ display: "flex", alignItems: "flex-start", gap: 10, minWidth: 280, maxWidth: 380, background: "var(--bg-raised)", border: "1px solid var(--border-card)", borderRadius: "var(--radius-lg)", boxShadow: "var(--shadow-menu)", padding: "12px 12px 12px 14px", animation: "kb-toast-in var(--dur) var(--ease-out)", ...style }}>
52
- <span style={{ color: t.color, display: "inline-flex", marginTop: 1 }}><Icon name={t.icon} size={16} /></span>
53
+ <span style={{ color: t.color, display: "inline-flex", alignItems: "center", flexShrink: 0, height: "var(--leading-base)" }}><Icon name={t.icon} size={16} /></span>
53
54
  <div style={{ display: "flex", flexDirection: "column", flex: 1, minWidth: 0 }}>
54
55
  <span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{title}</span>
55
- {meta ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: "var(--text-secondary)" }}>{meta}</span> : null}
56
+ {meta ? <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", color: "var(--text-secondary)" }}>{meta}</span> : null}
56
57
  </div>
57
- {onClose ? <IconButton icon="x" size="xs" muted onClick={onClose} label="Dismiss" /> : null}
58
+ {onClose ? <IconButton icon="x" size="sm" muted onClick={onClose} label="Dismiss" style={{ margin: "-2px 0" }} /> : null}
58
59
  </div>
59
60
  );
60
61
  }
@@ -1,20 +1,22 @@
1
1
  <script setup>
2
+ import { px } from '../px.js'
2
3
  import IconButton from '../buttons/IconButton.vue'
3
- defineProps({ open: Boolean, title: String, subtitle: String, width: { type: Number, default: 440 }, style: Object })
4
+ // Modal dialog. A size region that defaults to lg (40px controls) whatever the page behind it uses — a dialog does not compete for space; pass `size` to override. 24px padding, footer buttons 8px apart.
5
+ defineProps({ open: Boolean, title: String, subtitle: String, width: { type: [Number, String], default: 440 }, size: { type: String, default: 'lg' }, style: Object })
4
6
  const emit = defineEmits(['close'])
5
7
  </script>
6
8
  <template>
7
9
  <div v-if="open" style="position:fixed;inset:0;z-index:var(--z-modal);background:var(--bg-scrim);display:flex;align-items:center;justify-content:center;animation:kb-fade-in var(--dur-fast) var(--ease-out)" @click="emit('close')">
8
- <div @click.stop :style="{ width, maxWidth: '92vw', background: 'var(--bg-raised)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-modal)', animation: 'kb-rise var(--dur) var(--ease-out)', ...style }">
9
- <div style="display:flex;align-items:flex-start;justify-content:space-between;padding:17px 17px 0">
10
- <div style="display:flex;flex-direction:column;gap:4px">
10
+ <div role="dialog" aria-modal="true" :data-size="size" @click.stop :style="{ width: px(width), maxWidth: '92vw', background: 'var(--bg-raised)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-card)', boxShadow: 'var(--shadow-modal)', animation: 'kb-rise var(--dur) var(--ease-out)', ...style }">
11
+ <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:24px 24px 0">
12
+ <div style="display:flex;flex-direction:column;gap:2px">
11
13
  <span style="font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-md);line-height:var(--leading-md);letter-spacing:var(--tracking-md);color:var(--text-heading)">{{ title }}</span>
12
- <span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-secondary)">{{ subtitle }}</span>
14
+ <span v-if="subtitle" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);color:var(--text-secondary)">{{ subtitle }}</span>
13
15
  </div>
14
- <IconButton icon="x" size="md" label="Close" @click="emit('close')" />
16
+ <IconButton icon="x" label="Close" :style="{ margin: 'calc((var(--leading-md) - var(--control-h)) / 2) -6px 0 0' }" @click="emit('close')" />
15
17
  </div>
16
- <div v-if="$slots.default" style="padding:16px 17px 0"><slot /></div>
17
- <div style="display:flex;justify-content:flex-end;gap:8px;padding:17px"><slot name="footer" /></div>
18
+ <div v-if="$slots.default" style="padding:20px 24px 0"><slot /></div>
19
+ <div style="display:flex;justify-content:flex-end;gap:var(--control-gap);padding:24px"><slot name="footer" /></div>
18
20
  </div>
19
21
  </div>
20
22
  </template>
@@ -1,6 +1,7 @@
1
1
  <script setup>
2
+ import { px } from '../px.js'
2
3
  defineProps({ width: { type: [String, Number], default: '100%' }, height: { type: [String, Number], default: 12 }, radius: { type: String, default: 'var(--radius-sm)' }, style: Object })
3
4
  </script>
4
5
  <template>
5
- <span :style="{ display: 'block', width, height, borderRadius: radius, background: 'var(--skeleton)', animation: 'kb-pulse 1.4s var(--ease-in-out) infinite', ...style }" />
6
+ <span :style="{ display: 'block', width, height, borderRadius: px(radius), background: 'var(--skeleton)', animation: 'kb-pulse 1.4s var(--ease-in-out) infinite', ...style }" />
6
7
  </template>
@@ -1,6 +1,7 @@
1
1
  <script setup>
2
+ import { px } from '../px.js'
2
3
  defineProps({ size: { type: Number, default: 16 }, color: { type: String, default: 'var(--action-primary)' }, style: Object })
3
4
  </script>
4
5
  <template>
5
- <span :style="{ display: 'inline-block', width: size, height: size, borderRadius: 'var(--radius-full)', border: '2px solid var(--track)', borderTopColor: color, animation: 'kb-spin .7s linear infinite', ...style }" />
6
+ <span :style="{ display: 'inline-block', width: px(size), height: px(size), borderRadius: 'var(--radius-full)', border: '2px solid var(--track)', borderTopColor: color, animation: 'kb-spin .7s linear infinite', ...style }" />
6
7
  </template>
@@ -8,7 +8,7 @@ defineProps({ steps: { type: Array, default: () => [] }, current: { type: Number
8
8
  <template v-for="(s, i) in steps" :key="s.label">
9
9
  <div v-if="i > 0" style="flex:1;height:2px;background:var(--track);margin-top:15.25px;margin-left:12px;margin-right:12px;min-width:24px" />
10
10
  <div style="display:flex;flex-direction:column;align-items:center;gap:8px;flex-shrink:0">
11
- <span :style="{ width: 32, height: 32, borderRadius: 'var(--radius-full)', boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: i < current ? 'var(--success)' : i === current ? 'var(--action-primary)' : 'var(--bg-surface)', border: (i < current || i === current) ? 'none' : '2px solid var(--track)', color: (i < current || i === current) ? 'var(--on-accent)' : 'var(--text-muted)' }">
11
+ <span :style="{ width: '32px', height: '32px', borderRadius: 'var(--radius-full)', boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: i < current ? 'var(--success)' : i === current ? 'var(--action-primary)' : 'var(--bg-surface)', border: (i < current || i === current) ? 'none' : '2px solid var(--track)', color: (i < current || i === current) ? 'var(--on-accent)' : 'var(--text-muted)' }">
12
12
  <Icon :name="i < current ? 'check' : (s.icon || 'file')" :size="16" />
13
13
  </span>
14
14
  <span :style="{ fontFamily: 'var(--font-sans)', fontSize: 'var(--text-xs)', lineHeight: 'var(--leading-xs)', letterSpacing: 'var(--tracking-xs)', fontWeight: i === current ? 'var(--weight-medium)' : 'var(--weight-regular)', color: i === current ? 'var(--text-heading)' : 'var(--text-secondary)', whiteSpace: 'nowrap' }">{{ s.label }}</span>
@@ -18,7 +18,7 @@ export function Timeline({ items = [], style }) {
18
18
  <div style={{ display: "flex", flexDirection: "column", gap: 2, paddingBottom: i < items.length - 1 ? 18 : 0, minWidth: 0, paddingTop: 3 }}>
19
19
  <span style={{ fontFamily: "var(--font-sans)", fontWeight: "var(--weight-medium)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", letterSpacing: "var(--tracking-base)", color: "var(--text-heading)" }}>{it.title}</span>
20
20
  {it.meta && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", lineHeight: "var(--leading-xs)", letterSpacing: "var(--tracking-xs)", color: "var(--text-muted)" }}>{it.meta}</span>}
21
- {it.body && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", lineHeight: "var(--leading-sm)", color: "var(--text-body)", paddingTop: 4 }}>{it.body}</span>}
21
+ {it.body && <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", lineHeight: "var(--leading-base)", color: "var(--text-body)", paddingTop: 4 }}>{it.body}</span>}
22
22
  </div>
23
23
  </div>
24
24
  ))}
@@ -12,9 +12,9 @@ defineProps({ items: { type: Array, default: () => [] }, style: Object })
12
12
  <span v-if="i < items.length - 1" style="width:1.5px;flex:1;min-height:14px;background:var(--border-control)" />
13
13
  </div>
14
14
  <div :style="{ display: 'flex', flexDirection: 'column', gap: '2px', paddingBottom: i < items.length - 1 ? '18px' : 0, minWidth: 0, paddingTop: '3px' }">
15
- <span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);color:var(--text-heading)">{{ it.title }}</span>
16
- <span v-if="it.meta" style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-muted)">{{ it.meta }}</span>
17
- <span v-if="it.body" style="font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-body);padding-top:4px">{{ it.body }}</span>
15
+ <span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ it.title }}</span>
16
+ <span v-if="it.meta" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);letter-spacing:var(--tracking-xs);color:var(--text-muted)">{{ it.meta }}</span>
17
+ <span v-if="it.body" style="font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--text-body);padding-top:4px">{{ it.body }}</span>
18
18
  </div>
19
19
  </div>
20
20
  </div>
@@ -8,12 +8,12 @@ const emit = defineEmits(['close'])
8
8
  const t = computed(() => toastTones[props.tone])
9
9
  </script>
10
10
  <template>
11
- <div :style="{ display: 'flex', alignItems: 'flex-start', gap: 10, minWidth: 280, maxWidth: 380, background: 'var(--bg-raised)', border: '1px solid var(--border-card)', borderRadius: 'var(--radius-lg)', boxShadow: 'var(--shadow-menu)', padding: '12px 12px 12px 14px', animation: 'kb-toast-in var(--dur) var(--ease-out)', ...style }">
12
- <span :style="{ color: t.color, display: 'inline-flex', marginTop: 1 }"><Icon :name="t.icon" :size="16" /></span>
11
+ <div :style="{ display: 'flex', alignItems: 'flex-start', gap: '10px', minWidth: '280px', maxWidth: '380px', background: 'var(--bg-raised)', border: '1px solid var(--border-card)', borderRadius: 'var(--radius-lg)', boxShadow: 'var(--shadow-menu)', padding: '12px 12px 12px 14px', animation: 'kb-toast-in var(--dur) var(--ease-out)', ...style }">
12
+ <span :style="{ color: t.color, display: 'inline-flex', alignItems: 'center', flexShrink: 0, height: 'var(--leading-base)' }"><Icon :name="t.icon" :size="16" /></span>
13
13
  <div style="display:flex;flex-direction:column;flex:1;min-width:0">
14
14
  <span style="font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)">{{ title }}</span>
15
- <span v-if="meta" style="font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-secondary)">{{ meta }}</span>
15
+ <span v-if="meta" style="font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);color:var(--text-secondary)">{{ meta }}</span>
16
16
  </div>
17
- <IconButton v-if="dismissible" icon="x" size="xs" muted label="Dismiss" @click="emit('close')" />
17
+ <IconButton v-if="dismissible" icon="x" size="sm" muted label="Dismiss" :style="{ margin: '-2px 0' }" @click="emit('close')" />
18
18
  </div>
19
19
  </template>
@@ -0,0 +1,11 @@
1
+ <script setup>
2
+ import Toast from './Toast.vue'
3
+ // Fixed bottom-right stack of toasts. toasts: [{ id, tone, title, meta }]. Emits dismiss(id).
4
+ defineProps({ toasts: { type: Array, default: () => [] }, style: Object })
5
+ const emit = defineEmits(['dismiss'])
6
+ </script>
7
+ <template>
8
+ <div :style="{ position: 'fixed', right: '24px', bottom: '24px', zIndex: 'var(--z-toast)', display: 'flex', flexDirection: 'column', gap: '8px', ...style }">
9
+ <Toast v-for="t in toasts" :key="t.id" :tone="t.tone" :title="t.title" :meta="t.meta" dismissible @close="emit('dismiss', t.id)" />
10
+ </div>
11
+ </template>
@@ -0,0 +1,19 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ // Hover tooltip around any inline content. placement: top | bottom | left | right. Dark pill, 11px, fades in.
4
+ const props = defineProps({ label: String, placement: { type: String, default: 'top' }, style: Object })
5
+ const show = ref(false)
6
+ const POS = {
7
+ top: { bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: '6px' },
8
+ bottom: { top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: '6px' },
9
+ left: { right: '100%', top: '50%', transform: 'translateY(-50%)', marginRight: '6px' },
10
+ right: { left: '100%', top: '50%', transform: 'translateY(-50%)', marginLeft: '6px' }
11
+ }
12
+ const pos = computed(() => POS[props.placement] || POS.top)
13
+ </script>
14
+ <template>
15
+ <span :style="{ position: 'relative', display: 'inline-flex', ...style }" @mouseenter="show = true" @mouseleave="show = false">
16
+ <slot />
17
+ <span v-if="show" role="tooltip" :style="{ position: 'absolute', zIndex: 'var(--z-tooltip)', whiteSpace: 'nowrap', background: 'var(--tooltip-bg)', color: 'var(--tooltip-fg)', borderRadius: 'var(--radius-md)', padding: '5px 8px', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-xs)', lineHeight: 'var(--leading-xs)', letterSpacing: 'var(--tracking-xs)', boxShadow: 'var(--shadow-menu)', pointerEvents: 'none', animation: 'kb-fade-in var(--dur-fast) var(--ease-out)', ...pos }">{{ label }}</span>
18
+ </span>
19
+ </template>
@@ -12,17 +12,17 @@ Additions (not in the source file): `Tooltip`, `Modal`, `Toast`/`ToastStack`, `A
12
12
  ```jsx
13
13
  <Tooltip label="Run a test execution"><Button variant="ghost" icon="play">Run Test</Button></Tooltip>
14
14
  <Modal open={o} title="Delete node?" subtitle="This removes its 2 edges." onClose={close}
15
- footer={<><Button variant="outline" size="sm" onClick={close}>Cancel</Button><Button size="sm" style={{background:"var(--danger)"}}>Delete Node</Button></>}/>
15
+ footer={<><Button variant="outline" onClick={close}>Cancel</Button><Button variant="danger">Delete Node</Button></>}/> // Modal is an lg region: 36px buttons
16
16
  <ToastStack toasts={toasts} onDismiss={dismiss}/>
17
17
  <Alert tone="warning" title="4 configurations differ">Review before continuing.</Alert>
18
- <EmptyState icon="file-text" title="No documents yet" hint="..." action={<Button icon="plus" size="sm">New Document</Button>}/>
19
- <Skeleton width={140} height={13}/> <Spinner/>
18
+ <EmptyState icon="file-text" title="No documents yet" hint="..." action={<Button icon="plus">New Document</Button>}/>
19
+ <Skeleton width={140} height={14}/> <Spinner/>
20
20
  ```
21
21
 
22
22
  `Menu` (addition) — trigger-anchored dropdown (vs. `ContextMenu`, which opens at a click point):
23
23
 
24
24
  ```jsx
25
- <Menu trigger={<Button variant="dark" icon="chevron-down" size="sm">Show</Button>}
25
+ <Menu trigger={<Button variant="dark" icon="chevron-down">Show</Button>}
26
26
  items={[{label:"Preview",icon:"eye"},{label:"Export",icon:"download"},{label:"Delete",icon:"trash-2",danger:true}]}/>
27
27
  ```
28
28
 
@@ -1,12 +1,13 @@
1
1
  import * as React from "react";
2
- /** Checkbox with optional 12px/500 label ("Debug Mode"). md 16px · sm 13px (tables). */
2
+ /** Checkbox with optional label ("Debug Mode"). Box sm 14 · md/lg 16; the label uses the tier's control text and is the click target. */
3
3
  export interface CheckboxProps {
4
4
  checked?: boolean;
5
5
  /** Dash glyph, same fill as checked — for "some of N selected" parent rows */
6
6
  indeterminate?: boolean;
7
7
  onChange?: (checked: boolean) => void;
8
8
  label?: string;
9
- size?: "md" | "sm";
9
+ /** Size tier. Omit to inherit the enclosing region's tier (the default). */
10
+ size?: "sm" | "md" | "lg";
10
11
  disabled?: boolean;
11
12
  style?: React.CSSProperties;
12
13
  }