banhaten 0.1.3 → 0.3.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 (275) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/CONTRIBUTING.md +44 -0
  3. package/README.md +65 -13
  4. package/SECURITY.md +15 -0
  5. package/banhaten.config.example.json +0 -2
  6. package/docs/design-system/README.md +9 -0
  7. package/docs/design-system/adoption-quality-remediation.md +87 -0
  8. package/docs/design-system/agent-usage.md +13 -0
  9. package/docs/design-system/appearance-presets.md +6 -1
  10. package/docs/design-system/component-docs-pattern.md +81 -0
  11. package/docs/design-system/component-implementation-ownership-audit.md +244 -0
  12. package/docs/design-system/component-quality-audit-2026-08-20.md +223 -0
  13. package/docs/design-system/component-showcase-consistency-report.md +2 -0
  14. package/docs/design-system/component-token-consistency-audit.md +13 -3
  15. package/docs/design-system/components/README.md +4 -2
  16. package/docs/design-system/components/accordion.md +16 -0
  17. package/docs/design-system/components/activity-feed.md +17 -1
  18. package/docs/design-system/components/activity-heatmap.md +73 -0
  19. package/docs/design-system/components/alert-dialog.md +17 -0
  20. package/docs/design-system/components/alert.md +46 -14
  21. package/docs/design-system/components/aspect-ratio.md +15 -0
  22. package/docs/design-system/components/attribute.md +16 -0
  23. package/docs/design-system/components/autocomplete.md +19 -0
  24. package/docs/design-system/components/avatar.md +18 -0
  25. package/docs/design-system/components/badge.md +19 -0
  26. package/docs/design-system/components/banner.md +19 -1
  27. package/docs/design-system/components/breadcrumbs.md +17 -0
  28. package/docs/design-system/components/button-group.md +35 -0
  29. package/docs/design-system/components/button.md +18 -0
  30. package/docs/design-system/components/card.md +26 -1
  31. package/docs/design-system/components/carousel.md +20 -3
  32. package/docs/design-system/components/catalog-components.md +18 -1
  33. package/docs/design-system/components/chart.md +15 -0
  34. package/docs/design-system/components/checkbox.md +16 -0
  35. package/docs/design-system/components/collapsible.md +15 -0
  36. package/docs/design-system/components/command.md +15 -0
  37. package/docs/design-system/components/context-menu.md +15 -0
  38. package/docs/design-system/components/date-picker.md +21 -1
  39. package/docs/design-system/components/divider.md +16 -0
  40. package/docs/design-system/components/empty-state.md +17 -0
  41. package/docs/design-system/components/field.md +17 -0
  42. package/docs/design-system/components/file-upload.md +18 -0
  43. package/docs/design-system/components/hover-card.md +15 -0
  44. package/docs/design-system/components/input-group.md +17 -0
  45. package/docs/design-system/components/input-otp.md +18 -0
  46. package/docs/design-system/components/input.md +43 -7
  47. package/docs/design-system/components/item.md +72 -0
  48. package/docs/design-system/components/kbd.md +18 -0
  49. package/docs/design-system/components/label.md +16 -0
  50. package/docs/design-system/components/menu.md +17 -0
  51. package/docs/design-system/components/menubar.md +15 -0
  52. package/docs/design-system/components/modal.md +25 -4
  53. package/docs/design-system/components/native-select.md +18 -0
  54. package/docs/design-system/components/navigation-menu.md +15 -0
  55. package/docs/design-system/components/onboarding-step-list-item.md +16 -1
  56. package/docs/design-system/components/page-header.md +17 -1
  57. package/docs/design-system/components/pagination.md +31 -8
  58. package/docs/design-system/components/popover.md +19 -0
  59. package/docs/design-system/components/progress-slider.md +18 -0
  60. package/docs/design-system/components/progress.md +17 -0
  61. package/docs/design-system/components/radio-card.md +16 -0
  62. package/docs/design-system/components/radio-group.md +17 -0
  63. package/docs/design-system/components/resizable.md +15 -0
  64. package/docs/design-system/components/scroll-area.md +20 -1
  65. package/docs/design-system/components/segmented-control.md +71 -0
  66. package/docs/design-system/components/select.md +44 -7
  67. package/docs/design-system/components/sheet.md +17 -0
  68. package/docs/design-system/components/sidebar.md +17 -0
  69. package/docs/design-system/components/skeleton.md +18 -0
  70. package/docs/design-system/components/slideout.md +17 -0
  71. package/docs/design-system/components/slider.md +20 -0
  72. package/docs/design-system/components/social-button.md +21 -3
  73. package/docs/design-system/components/spinner.md +16 -0
  74. package/docs/design-system/components/steps.md +19 -0
  75. package/docs/design-system/components/table.md +36 -4
  76. package/docs/design-system/components/tabs.md +17 -0
  77. package/docs/design-system/components/tag.md +17 -0
  78. package/docs/design-system/components/textarea.md +16 -0
  79. package/docs/design-system/components/timeline.md +17 -0
  80. package/docs/design-system/components/toast.md +17 -0
  81. package/docs/design-system/components/toggle.md +17 -0
  82. package/docs/design-system/components/toolbar.md +21 -1
  83. package/docs/design-system/components/tooltip.md +18 -1
  84. package/docs/design-system/density-and-elevation.md +61 -0
  85. package/docs/design-system/direction-and-localization.md +23 -0
  86. package/docs/design-system/form-recipes.md +73 -0
  87. package/docs/design-system/gap-promotion-policy.md +21 -0
  88. package/docs/design-system/gap-promotion-report.md +100 -0
  89. package/docs/design-system/installation.md +68 -0
  90. package/docs/design-system/launch-comparison-shadcn.md +98 -0
  91. package/docs/design-system/registry-and-cli.md +30 -7
  92. package/docs/design-system/release-policy.md +30 -0
  93. package/docs/design-system/roadmap.md +14 -0
  94. package/docs/design-system/support-matrix.md +23 -0
  95. package/docs/design-system/tabs.md +1 -1
  96. package/docs/design-system/token-governance.md +23 -1
  97. package/package.json +30 -15
  98. package/registry/component-contract.json +40 -0
  99. package/registry/components/alert-dialog.tsx +2 -2
  100. package/registry/components/alert.tsx +101 -76
  101. package/registry/components/autocomplete.tsx +23 -9
  102. package/registry/components/avatar.tsx +14 -10
  103. package/registry/components/badge.tsx +18 -18
  104. package/registry/components/button-group.tsx +324 -5
  105. package/registry/components/button.tsx +38 -17
  106. package/registry/components/card.tsx +87 -62
  107. package/registry/components/carousel.tsx +67 -5
  108. package/registry/components/chart.tsx +36 -3
  109. package/registry/components/checkbox.tsx +3 -3
  110. package/registry/components/collapsible.tsx +1 -1
  111. package/registry/components/command.tsx +38 -4
  112. package/registry/components/context-menu.tsx +1 -1
  113. package/registry/components/date-picker.tsx +312 -135
  114. package/registry/components/expanded/ActivityFeed.tsx +9 -22
  115. package/registry/components/expanded/ActivityHeatmap.tsx +64 -0
  116. package/registry/components/expanded/Banner.tsx +3 -4
  117. package/registry/components/expanded/Breadcrumbs.tsx +8 -5
  118. package/registry/components/expanded/CatalogComponentsShowcase.tsx +16 -17
  119. package/registry/components/expanded/CatalogDivider.tsx +4 -3
  120. package/registry/components/expanded/EmptyState.tsx +1 -1
  121. package/registry/components/expanded/FileUpload.tsx +16 -7
  122. package/registry/components/expanded/OnboardingStepListItem.tsx +2 -6
  123. package/registry/components/expanded/PageHeader.tsx +47 -56
  124. package/registry/components/expanded/Slideout.tsx +29 -424
  125. package/registry/components/expanded/Steps.tsx +151 -13
  126. package/registry/components/expanded/Table.tsx +161 -82
  127. package/registry/components/expanded/Timeline.tsx +14 -27
  128. package/registry/components/expanded/activityFeed.css +9 -9
  129. package/registry/components/expanded/activityHeatmap.css +66 -0
  130. package/registry/components/expanded/banner.css +21 -11
  131. package/registry/components/expanded/breadcrumbs.css +2 -2
  132. package/registry/components/expanded/divider.css +1 -1
  133. package/registry/components/expanded/emptyState.css +1 -1
  134. package/registry/components/expanded/fileUpload.css +7 -7
  135. package/registry/components/expanded/onboardingStepListItem.css +2 -2
  136. package/registry/components/expanded/pageHeader.css +14 -11
  137. package/registry/components/expanded/slideout.css +10 -690
  138. package/registry/components/expanded/steps.css +78 -15
  139. package/registry/components/expanded/table.css +60 -69
  140. package/registry/components/expanded/timeline.css +19 -18
  141. package/registry/components/field.tsx +61 -8
  142. package/registry/components/input-content.ts +1 -0
  143. package/registry/components/input-otp.tsx +13 -3
  144. package/registry/components/input.tsx +68 -43
  145. package/registry/components/item.tsx +189 -0
  146. package/registry/components/kbd.tsx +111 -4
  147. package/registry/components/menu.tsx +87 -251
  148. package/registry/components/menubar.tsx +1 -1
  149. package/registry/components/modal.tsx +143 -103
  150. package/registry/components/native-select.tsx +2 -2
  151. package/registry/components/navigation-menu.tsx +1 -1
  152. package/registry/components/pagination.css +36 -0
  153. package/registry/components/pagination.tsx +88 -64
  154. package/registry/components/popover.tsx +162 -664
  155. package/registry/components/progress-slider.tsx +1 -1
  156. package/registry/components/radio-card.tsx +116 -87
  157. package/registry/components/resizable.tsx +36 -2
  158. package/registry/components/scroll-area.tsx +30 -6
  159. package/registry/components/segmented-control.tsx +87 -0
  160. package/registry/components/select-content.tsx +2 -2
  161. package/registry/components/select.tsx +86 -27
  162. package/registry/components/sheet.tsx +1 -1
  163. package/registry/components/sidebar.tsx +12 -6
  164. package/registry/components/skeleton.css +0 -3
  165. package/registry/components/social-button.tsx +59 -30
  166. package/registry/components/spinner.tsx +18 -55
  167. package/registry/components/table-elements.tsx +128 -0
  168. package/registry/components/tabs.css +1 -1
  169. package/registry/components/tabs.tsx +13 -14
  170. package/registry/components/tag.tsx +3 -3
  171. package/registry/components/textarea.tsx +3 -3
  172. package/registry/components/toast.tsx +16 -11
  173. package/registry/components/toggle.tsx +4 -4
  174. package/registry/components/toolbar.tsx +94 -22
  175. package/registry/components/tooltip.tsx +2 -2
  176. package/registry/examples/alert-demo.tsx +25 -5
  177. package/registry/examples/attribute-demo.tsx +1 -1
  178. package/registry/examples/autocomplete-demo.tsx +3 -3
  179. package/registry/examples/avatar-demo.tsx +14 -11
  180. package/registry/examples/badge-demo.tsx +1 -1
  181. package/registry/examples/button-demo.tsx +6 -6
  182. package/registry/examples/button-group-demo.tsx +23 -2
  183. package/registry/examples/card-demo.tsx +46 -8
  184. package/registry/examples/carousel-demo.tsx +20 -2
  185. package/registry/examples/chart-demo.tsx +1 -1
  186. package/registry/examples/checkbox-demo.tsx +5 -5
  187. package/registry/examples/collection-filter-bar-demo.tsx +135 -0
  188. package/registry/examples/command-demo.tsx +3 -3
  189. package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
  190. package/registry/examples/context-menu-demo.tsx +1 -1
  191. package/registry/examples/control-density-contract-demo.tsx +61 -0
  192. package/registry/examples/date-picker-demo.tsx +12 -12
  193. package/registry/examples/divider-demo.tsx +3 -3
  194. package/registry/examples/expanded/activity-heatmap-demo.tsx +29 -0
  195. package/registry/examples/expanded/banner-demo.tsx +1 -1
  196. package/registry/examples/expanded/breadcrumbs-demo.tsx +4 -4
  197. package/registry/examples/expanded/empty-state-demo.tsx +1 -1
  198. package/registry/examples/expanded/file-upload-demo.tsx +1 -1
  199. package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
  200. package/registry/examples/expanded/slideout-demo.tsx +2 -2
  201. package/registry/examples/expanded/steps-demo.tsx +1 -1
  202. package/registry/examples/expanded/tabs-demo.tsx +1 -1
  203. package/registry/examples/expanded/timeline-demo.tsx +1 -1
  204. package/registry/examples/hover-card-demo.tsx +2 -2
  205. package/registry/examples/input-demo.tsx +6 -6
  206. package/registry/examples/input-otp-demo.tsx +4 -4
  207. package/registry/examples/item-demo.tsx +46 -0
  208. package/registry/examples/kbd-demo.tsx +1 -1
  209. package/registry/examples/label-demo.tsx +1 -1
  210. package/registry/examples/menu-demo.tsx +1 -1
  211. package/registry/examples/modal-demo.tsx +67 -9
  212. package/registry/examples/navigation-menu-demo.tsx +1 -1
  213. package/registry/examples/pagination-demo.tsx +10 -7
  214. package/registry/examples/popover-demo.tsx +19 -19
  215. package/registry/examples/progress-demo.tsx +1 -1
  216. package/registry/examples/progress-slider-demo.tsx +1 -1
  217. package/registry/examples/radio-card-demo.tsx +4 -4
  218. package/registry/examples/radio-group-demo.tsx +2 -2
  219. package/registry/examples/resizable-demo.tsx +1 -1
  220. package/registry/examples/results-toolbar-demo.tsx +44 -0
  221. package/registry/examples/scroll-area-demo.tsx +3 -3
  222. package/registry/examples/segmented-control-demo.tsx +39 -0
  223. package/registry/examples/select-demo.tsx +1 -1
  224. package/registry/examples/sidebar-demo.tsx +5 -3
  225. package/registry/examples/slider-demo.tsx +3 -3
  226. package/registry/examples/social-button-demo.tsx +18 -8
  227. package/registry/examples/spinner-demo.tsx +13 -13
  228. package/registry/examples/tabs-demo.tsx +1 -1
  229. package/registry/examples/tag-demo.tsx +1 -1
  230. package/registry/examples/textarea-demo.tsx +4 -4
  231. package/registry/examples/toast-demo.tsx +1 -1
  232. package/registry/examples/toggle-demo.tsx +6 -6
  233. package/registry/examples/tooltip-demo.tsx +4 -4
  234. package/registry/guidance/USAGE.md +27 -0
  235. package/registry/hooks/use-direction.ts +193 -13
  236. package/registry/index.json +5668 -375
  237. package/registry/styles/globals.css +2404 -3999
  238. package/registry.json +156 -149
  239. package/schema/config.schema.json +11 -8
  240. package/schema/registry.schema.json +150 -45
  241. package/schema/tokens.schema.json +134 -34
  242. package/src/cli/command-line.js +194 -0
  243. package/src/cli/config-editors.js +9 -0
  244. package/src/cli/doctor.js +399 -0
  245. package/src/cli/file-transaction.js +295 -0
  246. package/src/cli/global-css.js +153 -0
  247. package/src/cli/index.js +567 -1613
  248. package/src/cli/install-plan.js +592 -0
  249. package/src/cli/install-receipt.js +171 -0
  250. package/src/cli/jsonc.js +203 -0
  251. package/src/cli/package-install-state.js +254 -0
  252. package/src/cli/package-manager.js +238 -0
  253. package/src/cli/package-plan.js +185 -0
  254. package/src/cli/package-version.js +97 -0
  255. package/src/cli/registry-query.js +207 -0
  256. package/src/cli/reporting.js +55 -0
  257. package/src/cli/schema-validation.js +19 -0
  258. package/src/cli/source-scanner.js +686 -0
  259. package/src/cli/vite-config.js +478 -0
  260. package/tokens/banhaten.tokens.json +4444 -331
  261. package/docs/design-system/components/command-bar.md +0 -57
  262. package/registry/components/expanded/BannerBoard.tsx +0 -225
  263. package/registry/components/expanded/CatalogTag.tsx +0 -85
  264. package/registry/components/expanded/CommandBar.tsx +0 -386
  265. package/registry/components/expanded/IconExplorer.tsx +0 -612
  266. package/registry/components/expanded/TimelineShowcase.tsx +0 -158
  267. package/registry/components/expanded/commandBar.css +0 -470
  268. package/registry/components/expanded/iconExplorer.css +0 -764
  269. package/registry/components/expanded/iconPacks.ts +0 -866
  270. package/registry/components/expanded/tag.css +0 -148
  271. package/registry/examples/expanded/icons-demo.tsx +0 -5
  272. package/tokens/Color mode.zip +0 -0
  273. package/tokens/Numbers.zip +0 -0
  274. package/tokens/Radius.zip +0 -0
  275. package/tokens/Theme.zip +0 -0
@@ -1,16 +1,22 @@
1
- import { useEffect, useMemo, useRef, useState, type CSSProperties, type ReactNode } from "react";
1
+ import { useMemo, useState, type CSSProperties, type ReactNode } from "react";
2
2
  import {
3
3
  ArrowUpDownIcon,
4
- CheckIcon as LucideCheckIcon,
5
4
  EditIcon as LucideEditIcon,
6
5
  EllipsisIcon,
7
6
  EyeIcon,
8
7
  FileIcon as LucideFileIcon,
9
- MinusIcon,
8
+ StarIcon,
10
9
  Trash2Icon,
11
10
  } from "lucide-react";
12
11
  import { Pagination, type PaginationPage, type PaginationProps } from "@/components/ui/pagination";
13
12
  import { Spinner } from "@/components/ui/spinner";
13
+ import { Avatar as AvatarPrimitive, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
14
+ import { Badge } from "@/components/ui/badge";
15
+ import { Button } from "@/components/ui/button";
16
+ import { Checkbox } from "@/components/ui/checkbox";
17
+ import { Progress as ProgressPrimitive } from "@/components/ui/progress";
18
+ import { Tag } from "@/components/ui/tag";
19
+ import { Toggle } from "@/components/ui/toggle";
14
20
  import {
15
21
  Toolbar,
16
22
  ToolbarBadge,
@@ -65,21 +71,22 @@ export type AvatarItem = {
65
71
  };
66
72
 
67
73
  export type TableAction = {
74
+ disabled?: boolean;
68
75
  icon: "view" | "edit" | "delete" | "more";
69
76
  label: string;
70
77
  onAction?: () => void;
71
78
  };
72
79
 
73
80
  export type TableItem =
74
- | { type: "checkbox"; checked: boolean; indeterminate?: boolean }
81
+ | { type: "checkbox"; ariaLabel?: string; checked: boolean; disabled?: boolean; indeterminate?: boolean; onCheckedChange?: (checked: boolean) => void }
75
82
  | { type: "text"; tone?: "default" | "subtle"; value: string; weight?: "regular" | "medium" }
76
83
  | { type: "avatarText"; caption?: string; name: string; src?: string; status?: "top" | "bottom" }
77
84
  | { type: "rating"; max?: 5; value: number }
78
85
  | { type: "progress"; label?: string; tone?: "success" | "brand"; value: number }
79
- | { type: "toggle"; checked: boolean; disabled?: boolean }
86
+ | { type: "toggle"; ariaLabel?: string; checked: boolean; disabled?: boolean; onCheckedChange?: (checked: boolean) => void }
80
87
  | { type: "badges"; items: BadgeItem[]; maxVisible?: number }
81
88
  | { type: "onlyAvatar"; alt: string; src?: string }
82
- | { type: "avatarStack"; avatars: AvatarItem[]; canAdd?: boolean; count?: number }
89
+ | { type: "avatarStack"; addLabel?: string; avatars: AvatarItem[]; canAdd?: boolean; count?: number; onAdd?: () => void }
83
90
  | { type: "tags"; items: string[]; maxVisible?: number }
84
91
  | { type: "file"; label?: string }
85
92
  | { type: "brandLogoText"; label: string; logoSrc?: string }
@@ -129,6 +136,7 @@ export type DataTableSearch<Row extends TableRowBase> =
129
136
  };
130
137
 
131
138
  export type DataTablePagination = {
139
+ ariaLabel?: string;
132
140
  defaultPage?: number;
133
141
  manual?: boolean;
134
142
  onPageChange?: (page: number) => void;
@@ -185,7 +193,7 @@ function cx(...classes: Array<string | false | undefined>) {
185
193
 
186
194
  export function Table<Row extends TableRowBase>({
187
195
  columns,
188
- dir = "ltr",
196
+ dir,
189
197
  onSelectedRowIdsChange,
190
198
  onSortChange,
191
199
  rows,
@@ -210,7 +218,12 @@ export function Table<Row extends TableRowBase>({
210
218
  }
211
219
 
212
220
  return (
213
- <div className={`ds-table-wrap ds-table-wrap--${size}`} dir={dir}>
221
+ <div
222
+ aria-label="Scrollable table"
223
+ className={`ds-table-wrap ds-table-wrap--${size}`}
224
+ dir={dir}
225
+ tabIndex={0}
226
+ >
214
227
  <table className="ds-table">
215
228
  <thead>
216
229
  <tr>
@@ -232,18 +245,20 @@ export function Table<Row extends TableRowBase>({
232
245
  ) : column.header ? (
233
246
  column.sortable ? (
234
247
  onSortChange ? (
235
- <button
248
+ <Button
236
249
  aria-label={`Sort by ${column.header}`}
237
250
  className={cx(
238
251
  "ds-table__sort",
239
252
  sort?.columnId === column.id && `ds-table__sort--${sort.direction}`,
240
253
  )}
241
254
  onClick={() => onSortChange(getNextSort(sort, column.id))}
255
+ size="xs"
242
256
  type="button"
257
+ variant="ghost"
243
258
  >
244
259
  <span>{column.header}</span>
245
260
  <SortIcon direction={sort?.columnId === column.id ? sort.direction : undefined} />
246
- </button>
261
+ </Button>
247
262
  ) : (
248
263
  <span className="ds-table__sort ds-table__sort--static">
249
264
  <span>{column.header}</span>
@@ -253,7 +268,11 @@ export function Table<Row extends TableRowBase>({
253
268
  ) : (
254
269
  column.header
255
270
  )
256
- ) : null}
271
+ ) : (
272
+ <span className="sr-only">
273
+ {column.kind === "actions" ? "Actions" : column.id}
274
+ </span>
275
+ )}
257
276
  </th>
258
277
  ))}
259
278
  </tr>
@@ -277,7 +296,7 @@ export function Table<Row extends TableRowBase>({
277
296
  ) : column.renderCell ? (
278
297
  column.renderCell(row)
279
298
  ) : column.item ? (
280
- renderTableItem(column.item(row))
299
+ renderTableItem(column.item(row), size)
281
300
  ) : null}
282
301
  </td>
283
302
  ))}
@@ -311,7 +330,7 @@ export function DataTable<Row extends TableRowBase>({
311
330
  columns,
312
331
  defaultSort = null,
313
332
  description,
314
- dir = "ltr",
333
+ dir,
315
334
  emptyState,
316
335
  error = false,
317
336
  filters = [],
@@ -486,6 +505,10 @@ export function DataTable<Row extends TableRowBase>({
486
505
  <footer className="ds-data-table__footer">
487
506
  <ToolbarText>{labels.resultsSummary({ from: visibleFrom, to: visibleTo, total: totalRows })}</ToolbarText>
488
507
  <Pagination
508
+ aria-label={
509
+ paginationConfig.ariaLabel ??
510
+ (typeof title === "string" ? `${title} pagination` : "Table pagination")
511
+ }
489
512
  dir={dir}
490
513
  nextDisabled={currentPage >= pageCount}
491
514
  onPageChange={setDataTablePage}
@@ -749,16 +772,24 @@ function clampNumber(value: number, min: number, max: number) {
749
772
  return Math.min(max, Math.max(min, Math.floor(value)));
750
773
  }
751
774
 
752
- function renderTableItem(item: TableItem) {
775
+ function renderTableItem(item: TableItem, tableSize: TableSize) {
753
776
  switch (item.type) {
754
777
  case "checkbox":
755
- return <SelectionCheckbox checked={item.checked} indeterminate={item.indeterminate} label="Select row" onChange={() => undefined} />;
778
+ return (
779
+ <SelectionCheckbox
780
+ checked={item.checked}
781
+ disabled={item.disabled ?? !item.onCheckedChange}
782
+ indeterminate={item.indeterminate}
783
+ label={item.ariaLabel ?? "Select row"}
784
+ onChange={item.onCheckedChange}
785
+ />
786
+ );
756
787
  case "text":
757
788
  return <span className={`ds-table-item-text ds-table-item-text--${item.tone ?? "default"} ds-table-item-text--${item.weight ?? "regular"}`}>{item.value}</span>;
758
789
  case "avatarText":
759
790
  return (
760
791
  <span className="ds-table-avatar-text">
761
- <Avatar name={item.name} src={item.src} />
792
+ <Avatar name={item.name} size={tableSize === "lg" ? "md" : "sm"} src={item.src} />
762
793
  <span>
763
794
  <strong>{item.name}</strong>
764
795
  {item.caption && <em>{item.caption}</em>}
@@ -770,13 +801,30 @@ function renderTableItem(item: TableItem) {
770
801
  case "progress":
771
802
  return <Progress value={item.value} label={item.label} tone={item.tone ?? "success"} />;
772
803
  case "toggle":
773
- return <button aria-pressed={item.checked} className="ds-table-toggle" disabled={item.disabled} type="button"><span /></button>;
804
+ return (
805
+ <Toggle
806
+ aria-label={item.ariaLabel ?? "Toggle row value"}
807
+ checked={item.checked}
808
+ disabled={item.disabled ?? !item.onCheckedChange}
809
+ onCheckedChange={item.onCheckedChange}
810
+ size="sm"
811
+ />
812
+ );
774
813
  case "badges":
775
814
  return <BadgeList items={item.items} maxVisible={item.maxVisible} />;
776
815
  case "onlyAvatar":
777
- return <Avatar name={item.alt} src={item.src} />;
816
+ return <Avatar name={item.alt} size={tableSize === "lg" ? "md" : "xs"} src={item.src} />;
778
817
  case "avatarStack":
779
- return <AvatarStack avatars={item.avatars} count={item.count} canAdd={item.canAdd} />;
818
+ return (
819
+ <AvatarStack
820
+ addLabel={item.addLabel}
821
+ avatars={item.avatars}
822
+ canAdd={item.canAdd}
823
+ count={item.count}
824
+ onAdd={item.onAdd}
825
+ size={tableSize}
826
+ />
827
+ );
780
828
  case "tags":
781
829
  return <TagList items={item.items} maxVisible={item.maxVisible} />;
782
830
  case "file":
@@ -793,7 +841,7 @@ function renderTableItem(item: TableItem) {
793
841
  return (
794
842
  <span className="ds-table-action-group">
795
843
  {item.actions.map((action) => (
796
- <IconButton key={action.label} icon={action.icon} label={action.label} onClick={action.onAction} />
844
+ <IconButton disabled={action.disabled} key={action.label} icon={action.icon} label={action.label} onClick={action.onAction} />
797
845
  ))}
798
846
  </span>
799
847
  );
@@ -813,35 +861,21 @@ function SelectionCheckbox({
813
861
  disabled?: boolean;
814
862
  indeterminate?: boolean;
815
863
  label: string;
816
- onChange: (checked: boolean) => void;
864
+ onChange?: (checked: boolean) => void;
817
865
  }) {
818
- const inputRef = useRef<HTMLInputElement | null>(null);
819
-
820
- useEffect(() => {
821
- if (inputRef.current) {
822
- inputRef.current.indeterminate = indeterminate;
823
- }
824
- }, [indeterminate]);
825
-
826
866
  return (
827
- <label className="ds-table-checkbox">
828
- <input
829
- aria-checked={indeterminate ? "mixed" : checked}
830
- aria-label={label}
831
- checked={checked}
832
- disabled={disabled}
833
- onChange={(event) => onChange(event.currentTarget.checked)}
834
- ref={inputRef}
835
- type="checkbox"
836
- />
837
- <span aria-hidden="true">
838
- {indeterminate ? <IndeterminateIcon /> : <CheckIcon />}
839
- </span>
840
- </label>
867
+ <Checkbox
868
+ aria-label={label}
869
+ checked={indeterminate ? "indeterminate" : checked}
870
+ disabled={disabled}
871
+ onCheckedChange={(nextChecked) => onChange?.(nextChecked === true)}
872
+ />
841
873
  );
842
874
  }
843
875
 
844
- function Avatar({ name, src }: { name: string; src?: string }) {
876
+ type TableAvatarSize = "xs" | "sm" | "md";
877
+
878
+ function Avatar({ name, size, src }: { name: string; size: TableAvatarSize; src?: string }) {
845
879
  const initials = name
846
880
  .split(" ")
847
881
  .map((part) => part[0])
@@ -849,17 +883,40 @@ function Avatar({ name, src }: { name: string; src?: string }) {
849
883
  .join("")
850
884
  .toUpperCase();
851
885
 
852
- return src ? <img alt="" className="ds-table-avatar" src={src} /> : <span className="ds-table-avatar">{initials}</span>;
886
+ return (
887
+ <AvatarPrimitive className="ds-table-avatar" size={size}>
888
+ {src ? <AvatarImage alt="" src={src} /> : null}
889
+ <AvatarFallback size={size}>{initials}</AvatarFallback>
890
+ </AvatarPrimitive>
891
+ );
853
892
  }
854
893
 
855
- function AvatarStack({ avatars, canAdd, count }: { avatars: AvatarItem[]; canAdd?: boolean; count?: number }) {
894
+ function AvatarStack({
895
+ addLabel = "Add person",
896
+ avatars,
897
+ canAdd,
898
+ count,
899
+ onAdd,
900
+ size,
901
+ }: {
902
+ addLabel?: string;
903
+ avatars: AvatarItem[];
904
+ canAdd?: boolean;
905
+ count?: number;
906
+ onAdd?: () => void;
907
+ size: TableSize;
908
+ }) {
856
909
  return (
857
- <span className="ds-table-avatar-stack">
910
+ <span className={`ds-table-avatar-stack ds-table-avatar-stack--${size}`}>
858
911
  {avatars.slice(0, 3).map((avatar) => (
859
- <Avatar key={avatar.alt} name={avatar.alt} src={avatar.src} />
912
+ <Avatar key={avatar.alt} name={avatar.alt} size={size === "lg" ? "md" : "xs"} src={avatar.src} />
860
913
  ))}
861
914
  {count ? <span className="ds-table-avatar ds-table-avatar--count">+{count}</span> : null}
862
- {canAdd ? <button aria-label="Add person" type="button">+</button> : null}
915
+ {canAdd ? (
916
+ <Button aria-label={addLabel} disabled={!onAdd} onClick={onAdd} size="icon-xs" type="button" variant="ghost">
917
+ +
918
+ </Button>
919
+ ) : null}
863
920
  </span>
864
921
  );
865
922
  }
@@ -871,12 +928,17 @@ function BadgeList({ items, maxVisible = items.length }: { items: BadgeItem[]; m
871
928
  return (
872
929
  <span className="ds-table-badges">
873
930
  {visible.map((item, index) => (
874
- <span className={`ds-table-badge ds-table-badge--${item.tone ?? "neutral"}`} key={`${item.label}-${index}`}>
875
- {item.dot && <i />}
931
+ <Badge
932
+ badgeStyle="light"
933
+ color={badgeColor(item.tone)}
934
+ key={`${item.label}-${index}`}
935
+ size="xs"
936
+ type={item.dot ? "dot" : "default"}
937
+ >
876
938
  {item.label}
877
- </span>
939
+ </Badge>
878
940
  ))}
879
- {overflow > 0 && <span className="ds-table-badge">+{overflow}</span>}
941
+ {overflow > 0 && <Badge size="xs">+{overflow}</Badge>}
880
942
  </span>
881
943
  );
882
944
  }
@@ -888,31 +950,40 @@ function TagList({ items, maxVisible = items.length }: { items: string[]; maxVis
888
950
  return (
889
951
  <span className="ds-table-tags">
890
952
  {visible.map((item, index) => (
891
- <span key={`${item}-${index}`}>{item}</span>
953
+ <Tag key={`${item}-${index}`} size="xs">{item}</Tag>
892
954
  ))}
893
- {overflow > 0 && <span>+{overflow}</span>}
955
+ {overflow > 0 && <Tag size="xs">+{overflow}</Tag>}
894
956
  </span>
895
957
  );
896
958
  }
897
959
 
898
960
  function Progress({ label, tone, value }: { label?: string; tone: "success" | "brand"; value: number }) {
899
- const safeValue = Math.max(0, Math.min(100, value));
900
-
901
961
  return (
902
- <span className={`ds-table-progress ds-table-progress--${tone}`}>
903
- <span aria-hidden="true">
904
- <i style={{ width: `${safeValue}%` }} />
905
- </span>
906
- <em>{label ?? `${safeValue}%`}</em>
907
- </span>
962
+ <ProgressPrimitive
963
+ aria-label={label ?? "Progress"}
964
+ className={`ds-table-progress ds-table-progress--${tone}`}
965
+ indicator={label}
966
+ labelPosition="inline"
967
+ size="lg"
968
+ value={value}
969
+ />
908
970
  );
909
971
  }
910
972
 
911
973
  function Rating({ max, value }: { max: number; value: number }) {
912
974
  return (
913
- <span className="ds-table-rating" aria-label={`${value} out of ${max}`}>
975
+ <span
976
+ aria-label={`${value} out of ${max}`}
977
+ className="ds-table-rating"
978
+ role="img"
979
+ >
914
980
  {Array.from({ length: max }, (_, index) => (
915
- <span className={index < value ? "is-filled" : undefined} key={index}>*</span>
981
+ <StarIcon
982
+ aria-hidden="true"
983
+ className={index < value ? "is-filled" : undefined}
984
+ key={index}
985
+ strokeWidth="var(--bh-icon-stroke-190)"
986
+ />
916
987
  ))}
917
988
  </span>
918
989
  );
@@ -927,14 +998,30 @@ function IconLabel({ icon, label }: { icon: ReactNode; label: string }) {
927
998
  );
928
999
  }
929
1000
 
930
- function IconButton({ icon, label, onClick }: { icon: TableAction["icon"]; label: string; onClick?: () => void }) {
1001
+ function IconButton({
1002
+ disabled,
1003
+ icon,
1004
+ label,
1005
+ onClick,
1006
+ }: {
1007
+ disabled?: boolean;
1008
+ icon: TableAction["icon"];
1009
+ label: string;
1010
+ onClick?: () => void;
1011
+ }) {
931
1012
  return (
932
- <button aria-label={label} className="ds-table-icon-button" onClick={onClick} type="button">
1013
+ <Button aria-label={label} className="ds-table-icon-button" disabled={disabled ?? !onClick} onClick={onClick} size="icon-xs" type="button" variant="ghost">
933
1014
  {renderActionIcon(icon)}
934
- </button>
1015
+ </Button>
935
1016
  );
936
1017
  }
937
1018
 
1019
+ function badgeColor(tone: BadgeItem["tone"]) {
1020
+ if (tone === "success") return "green" as const;
1021
+ if (tone === "warning") return "amber" as const;
1022
+ return tone ?? "neutral";
1023
+ }
1024
+
938
1025
  function renderActionIcon(icon: TableAction["icon"]) {
939
1026
  switch (icon) {
940
1027
  case "view":
@@ -949,38 +1036,30 @@ function renderActionIcon(icon: TableAction["icon"]) {
949
1036
  }
950
1037
  }
951
1038
 
952
- function CheckIcon() {
953
- return <LucideCheckIcon strokeWidth={2.6} />;
954
- }
955
-
956
- function IndeterminateIcon() {
957
- return <MinusIcon strokeWidth={2.6} />;
958
- }
959
-
960
1039
  function SortIcon({ direction }: { direction?: TableSortDirection }) {
961
1040
  return (
962
- <ArrowUpDownIcon aria-hidden="true" data-direction={direction ?? "none"} strokeWidth={1.9} />
1041
+ <ArrowUpDownIcon aria-hidden="true" data-direction={direction ?? "none"} strokeWidth="var(--bh-icon-stroke-190)" />
963
1042
  );
964
1043
  }
965
1044
 
966
1045
  function MoreIcon() {
967
- return <EllipsisIcon aria-hidden="true" strokeWidth={2.25} />;
1046
+ return <EllipsisIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-225)" />;
968
1047
  }
969
1048
 
970
1049
  function ViewIcon() {
971
- return <EyeIcon aria-hidden="true" strokeWidth={1.9} />;
1050
+ return <EyeIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-190)" />;
972
1051
  }
973
1052
 
974
1053
  function EditIcon() {
975
- return <LucideEditIcon aria-hidden="true" strokeWidth={1.9} />;
1054
+ return <LucideEditIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-190)" />;
976
1055
  }
977
1056
 
978
1057
  function DeleteIcon() {
979
- return <Trash2Icon aria-hidden="true" strokeWidth={1.9} />;
1058
+ return <Trash2Icon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-190)" />;
980
1059
  }
981
1060
 
982
1061
  function FileIcon() {
983
- return <LucideFileIcon strokeWidth={1.9} />;
1062
+ return <LucideFileIcon strokeWidth="var(--bh-icon-stroke-190)" />;
984
1063
  }
985
1064
 
986
1065
  function BrandIcon() {
@@ -8,11 +8,12 @@ export type TimelineItemState = "finished" | "active" | "inactive";
8
8
 
9
9
  export type TimelineStep = {
10
10
  bottomDate?: string;
11
- caption?: string;
11
+ caption?: ReactNode;
12
+ className?: string;
12
13
  date?: string;
13
14
  icon?: ReactNode;
14
15
  id?: string;
15
- label?: string;
16
+ label?: ReactNode;
16
17
  showBottomDate?: boolean;
17
18
  showCaption?: boolean;
18
19
  showLeadingDate?: boolean;
@@ -27,6 +28,7 @@ export type TimelineItemProps = TimelineStep & {
27
28
  className?: string;
28
29
  dir?: "ltr" | "rtl";
29
30
  orientation?: TimelineOrientation;
31
+ role?: "listitem" | "none" | "presentation";
30
32
  variant?: TimelineVariant;
31
33
  };
32
34
 
@@ -46,14 +48,12 @@ function classes(...names: Array<string | false | undefined>) {
46
48
  function Indicator({
47
49
  icon,
48
50
  orientation,
49
- rtl,
50
51
  showLine,
51
52
  state,
52
53
  variant,
53
54
  }: {
54
55
  icon?: ReactNode;
55
56
  orientation: TimelineOrientation;
56
- rtl: boolean;
57
57
  showLine: boolean;
58
58
  state: TimelineItemState;
59
59
  variant: TimelineVariant;
@@ -79,21 +79,11 @@ function Indicator({
79
79
  className={classes(
80
80
  "ds-timeline__indicator",
81
81
  `ds-timeline__indicator--${orientation}`,
82
- rtl && "ds-timeline__indicator--rtl",
83
82
  )}
84
83
  aria-hidden="true"
85
84
  >
86
- {orientation === "horizontal" && rtl ? (
87
- <>
88
- {line}
89
- {marker}
90
- </>
91
- ) : (
92
- <>
93
- {marker}
94
- {line}
95
- </>
96
- )}
85
+ {marker}
86
+ {line}
97
87
  </span>
98
88
  );
99
89
  }
@@ -103,10 +93,11 @@ export function TimelineItem({
103
93
  caption,
104
94
  className,
105
95
  date,
106
- dir = "ltr",
96
+ dir,
107
97
  icon,
108
98
  label,
109
99
  orientation = "vertical",
100
+ role,
110
101
  showBottomDate = false,
111
102
  showCaption = true,
112
103
  showLeadingDate = true,
@@ -117,7 +108,6 @@ export function TimelineItem({
117
108
  topDate,
118
109
  variant = "default",
119
110
  }: TimelineItemProps) {
120
- const rtl = dir === "rtl";
121
111
  const displayDate = date;
122
112
  const displayLabel = label;
123
113
  const displayCaption = caption;
@@ -141,7 +131,6 @@ export function TimelineItem({
141
131
  <Indicator
142
132
  icon={icon}
143
133
  orientation={orientation}
144
- rtl={rtl}
145
134
  showLine={showLine}
146
135
  state={state}
147
136
  variant={variant}
@@ -159,11 +148,10 @@ export function TimelineItem({
159
148
  `ds-timeline-item--${orientation}`,
160
149
  `ds-timeline-item--${variant}`,
161
150
  `ds-timeline-item--${state}`,
162
- rtl ? "ds-timeline-item--rtl" : "ds-timeline-item--ltr",
163
151
  className,
164
152
  )}
165
153
  dir={dir}
166
- role="listitem"
154
+ role={role}
167
155
  >
168
156
  {leadingDate}
169
157
  {indicator}
@@ -173,15 +161,13 @@ export function TimelineItem({
173
161
  }
174
162
 
175
163
  export function Timeline({
176
- ariaLabel,
164
+ ariaLabel = "Timeline",
177
165
  className,
178
- dir = "ltr",
166
+ dir,
179
167
  orientation = "vertical",
180
168
  steps = [],
181
169
  variant = "default",
182
170
  }: TimelineProps) {
183
- const rtl = dir === "rtl";
184
-
185
171
  return (
186
172
  <div
187
173
  aria-label={ariaLabel}
@@ -189,11 +175,11 @@ export function Timeline({
189
175
  "ds-timeline",
190
176
  `ds-timeline--${orientation}`,
191
177
  `ds-timeline--${variant}`,
192
- rtl ? "ds-timeline--rtl" : "ds-timeline--ltr",
193
178
  className,
194
179
  )}
195
180
  dir={dir}
196
181
  role="list"
182
+ tabIndex={orientation === "horizontal" ? 0 : undefined}
197
183
  >
198
184
  {steps.map((step, index) => (
199
185
  <TimelineItem
@@ -201,6 +187,7 @@ export function Timeline({
201
187
  key={step.id ?? index}
202
188
  dir={dir}
203
189
  orientation={orientation}
190
+ role="listitem"
204
191
  showTopDate={step.showTopDate ?? orientation === "horizontal"}
205
192
  showBottomDate={step.showBottomDate ?? false}
206
193
  showPaddingBottom={step.showPaddingBottom ?? orientation === "vertical"}
@@ -212,5 +199,5 @@ export function Timeline({
212
199
  }
213
200
 
214
201
  function CheckIcon() {
215
- return <LucideCheckIcon aria-hidden="true" className="ds-timeline__check" strokeWidth={3} />;
202
+ return <LucideCheckIcon aria-hidden="true" className="ds-timeline__check" strokeWidth="var(--bh-icon-stroke-300)" />;
216
203
  }
@@ -30,7 +30,7 @@
30
30
  align-items: flex-start;
31
31
  color: var(--activity-feed-content-default);
32
32
  font-family: var(--bh-font-family);
33
- letter-spacing: 0;
33
+ letter-spacing: var(--bh-text-base-letter-spacing);
34
34
  }
35
35
 
36
36
  .ds-activity-feed *,
@@ -43,7 +43,7 @@
43
43
  text-decoration: none;
44
44
  }
45
45
 
46
- .ds-activity-feed--rtl {
46
+ .ds-activity-feed:dir(rtl) {
47
47
  font-family: var(--bh-font-family);
48
48
  margin-inline-end: auto;
49
49
  }
@@ -102,7 +102,7 @@
102
102
  gap: var(--activity-feed-space-lg);
103
103
  }
104
104
 
105
- .ds-activity-feed-item--rtl .ds-activity-feed-item__content {
105
+ .ds-activity-feed-item:dir(rtl) .ds-activity-feed-item__content {
106
106
  align-items: flex-start;
107
107
  }
108
108
 
@@ -121,7 +121,7 @@
121
121
  gap: var(--bh-space-sm-6);
122
122
  }
123
123
 
124
- .ds-activity-feed-item--rtl .ds-activity-feed-item__topline {
124
+ .ds-activity-feed-item:dir(rtl) .ds-activity-feed-item__topline {
125
125
  justify-content: flex-start;
126
126
  }
127
127
 
@@ -139,7 +139,7 @@
139
139
  word-break: break-word;
140
140
  }
141
141
 
142
- .ds-activity-feed-item--rtl .ds-activity-feed-item__text {
142
+ .ds-activity-feed-item:dir(rtl) .ds-activity-feed-item__text {
143
143
  justify-content: flex-start;
144
144
  text-align: right;
145
145
  }
@@ -149,7 +149,7 @@
149
149
  font-weight: var(--bh-font-weight-medium);
150
150
  }
151
151
 
152
- .ds-activity-feed-item--rtl .ds-activity-feed-item__text strong {
152
+ .ds-activity-feed-item:dir(rtl) .ds-activity-feed-item__text strong {
153
153
  font-weight: var(--bh-font-weight-semibold);
154
154
  }
155
155
 
@@ -199,7 +199,7 @@
199
199
  padding: var(--activity-feed-space-xl);
200
200
  }
201
201
 
202
- .ds-activity-feed-item--rtl .ds-activity-feed-item__caption {
202
+ .ds-activity-feed-item:dir(rtl) .ds-activity-feed-item__caption {
203
203
  justify-content: flex-start;
204
204
  text-align: right;
205
205
  }
@@ -216,7 +216,7 @@
216
216
  line-height: var(--bh-text-body-sm-regular-line-height);
217
217
  }
218
218
 
219
- .ds-activity-feed-item--rtl .ds-activity-feed-item__slot {
219
+ .ds-activity-feed-item:dir(rtl) .ds-activity-feed-item__slot {
220
220
  place-items: center start;
221
221
  text-align: right;
222
222
  }
@@ -230,7 +230,7 @@
230
230
  line-height: var(--bh-text-body-sm-regular-line-height);
231
231
  }
232
232
 
233
- .ds-activity-feed-item--rtl .ds-activity-feed-item__timestamp {
233
+ .ds-activity-feed-item:dir(rtl) .ds-activity-feed-item__timestamp {
234
234
  text-align: right;
235
235
  }
236
236