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
@@ -54,8 +54,8 @@ function classes(...names: Array<string | false | undefined>) {
54
54
 
55
55
  function StatusDot() {
56
56
  return (
57
- <span className="ds-activity-feed-item__status" aria-hidden="true" data-node-id="188:64134">
58
- <span data-node-id="188:64135" />
57
+ <span className="ds-activity-feed-item__status" aria-hidden="true">
58
+ <span />
59
59
  </span>
60
60
  );
61
61
  }
@@ -63,21 +63,15 @@ function StatusDot() {
63
63
  function Indicator({
64
64
  avatarAlt,
65
65
  avatarSrc,
66
- rtl,
67
66
  showLine,
68
- type,
69
67
  }: {
70
68
  avatarAlt: string;
71
69
  avatarSrc: string;
72
- rtl: boolean;
73
70
  showLine: boolean;
74
- type: ActivityFeedItemType;
75
71
  }) {
76
- const avatarNodeId = rtl ? (type === "caption" ? "587:18722" : "587:18703") : type === "caption" ? "584:18593" : "584:10955";
77
-
78
72
  return (
79
73
  <span className="ds-activity-feed-item__indicator" aria-hidden={avatarAlt ? undefined : "true"}>
80
- <span className="ds-activity-feed-item__avatar" data-node-id={avatarNodeId}>
74
+ <span className="ds-activity-feed-item__avatar">
81
75
  <img alt={avatarAlt} src={avatarSrc} />
82
76
  </span>
83
77
  {showLine && <span className="ds-activity-feed-item__line" />}
@@ -146,7 +140,7 @@ export function ActivityFeedItem({
146
140
  hasTwoActionButtons = true,
147
141
  label,
148
142
  link,
149
- dir = "ltr",
143
+ dir,
150
144
  showLine = true,
151
145
  showPaddingBottom = true,
152
146
  slotContent,
@@ -155,8 +149,6 @@ export function ActivityFeedItem({
155
149
  type = "caption",
156
150
  }: ActivityFeedItemProps) {
157
151
  const actionItems = actions === false || !hasActionButtons ? [] : (actions ?? []).slice(0, hasTwoActionButtons ? 2 : 1);
158
- const rtl = dir === "rtl";
159
- const nodeId = rtl ? (type === "caption" ? "587:18720" : "587:18701") : type === "caption" ? "584:18591" : "584:10411";
160
152
 
161
153
  const textParts = [
162
154
  label && <strong key="label">{label}</strong>,
@@ -169,13 +161,11 @@ export function ActivityFeedItem({
169
161
  className={classes(
170
162
  "ds-activity-feed-item",
171
163
  `ds-activity-feed-item--${type}`,
172
- rtl ? "ds-activity-feed-item--rtl" : "ds-activity-feed-item--ltr",
173
164
  className,
174
165
  )}
175
- data-node-id={nodeId}
176
- dir={rtl ? "rtl" : "ltr"}
166
+ dir={dir}
177
167
  >
178
- <Indicator avatarAlt={avatarAlt} avatarSrc={avatarSrc} rtl={rtl} showLine={showLine} type={type} />
168
+ <Indicator avatarAlt={avatarAlt} avatarSrc={avatarSrc} showLine={showLine} />
179
169
 
180
170
  <div className="ds-activity-feed-item__content">
181
171
  <div className="ds-activity-feed-item__container">
@@ -215,17 +205,14 @@ export function ActivityFeed({
215
205
  ariaLabel,
216
206
  avatarSrc = defaultAvatarSrc,
217
207
  className,
218
- dir = "ltr",
208
+ dir,
219
209
  items = [],
220
210
  }: ActivityFeedProps) {
221
- const rtl = dir === "rtl";
222
-
223
211
  return (
224
212
  <section
225
213
  aria-label={ariaLabel}
226
- className={classes("ds-activity-feed", rtl ? "ds-activity-feed--rtl" : "ds-activity-feed--ltr", className)}
227
- data-node-id={rtl ? "587:19711" : "587:19381"}
228
- dir={rtl ? "rtl" : "ltr"}
214
+ className={classes("ds-activity-feed", className)}
215
+ dir={dir}
229
216
  >
230
217
  {items.map((item, index) => (
231
218
  <ActivityFeedItem
@@ -0,0 +1,64 @@
1
+ import type { ComponentPropsWithoutRef } from "react";
2
+ import "./activityHeatmap.css";
3
+
4
+ export type ActivityHeatmapLevel = 0 | 1 | 2 | 3 | 4;
5
+
6
+ export type ActivityHeatmapItem = {
7
+ id?: string;
8
+ label?: string;
9
+ level: ActivityHeatmapLevel;
10
+ };
11
+
12
+ export type ActivityHeatmapProps = Omit<
13
+ ComponentPropsWithoutRef<"div">,
14
+ "children" | "dir"
15
+ > & {
16
+ ariaLabel?: string;
17
+ dir?: "ltr" | "rtl";
18
+ items?: readonly ActivityHeatmapItem[];
19
+ };
20
+
21
+ function classes(...names: Array<string | false | undefined>) {
22
+ return names.filter(Boolean).join(" ");
23
+ }
24
+
25
+ function normalizeLevel(level: ActivityHeatmapLevel): ActivityHeatmapLevel {
26
+ return Math.max(0, Math.min(4, Math.round(level))) as ActivityHeatmapLevel;
27
+ }
28
+
29
+ export function ActivityHeatmap({
30
+ ariaLabel = "Activity heatmap",
31
+ className,
32
+ dir,
33
+ items = [],
34
+ tabIndex = 0,
35
+ ...props
36
+ }: ActivityHeatmapProps) {
37
+ return (
38
+ <div
39
+ {...props}
40
+ aria-label={ariaLabel}
41
+ className={classes("ds-activity-heatmap", className)}
42
+ data-slot="activity-heatmap"
43
+ dir={dir}
44
+ role="img"
45
+ tabIndex={tabIndex}
46
+ >
47
+ <span
48
+ aria-hidden="true"
49
+ className="ds-activity-heatmap__grid"
50
+ data-slot="activity-heatmap-grid"
51
+ >
52
+ {items.map((item, index) => (
53
+ <span
54
+ className="ds-activity-heatmap__cell"
55
+ data-level={normalizeLevel(item.level)}
56
+ data-slot="activity-heatmap-cell"
57
+ key={item.id ?? index}
58
+ title={item.label}
59
+ />
60
+ ))}
61
+ </span>
62
+ </div>
63
+ );
64
+ }
@@ -35,11 +35,11 @@ export type BannerProps = {
35
35
  };
36
36
 
37
37
  function MegaphoneIcon() {
38
- return <LucideMegaphoneIcon aria-hidden="true" className="ds-banner__icon" strokeWidth={2.1} />;
38
+ return <LucideMegaphoneIcon aria-hidden="true" className="ds-banner__icon" strokeWidth="var(--bh-icon-stroke-210)" />;
39
39
  }
40
40
 
41
41
  function CloseIcon() {
42
- return <XIcon aria-hidden="true" className="ds-banner__close-icon" strokeWidth={2.25} />;
42
+ return <XIcon aria-hidden="true" className="ds-banner__close-icon" strokeWidth="var(--bh-icon-stroke-225)" />;
43
43
  }
44
44
 
45
45
  function getBannerActionVariant(color: BannerColor): ButtonProps["variant"] {
@@ -60,7 +60,7 @@ export function Banner({
60
60
  color = "brand",
61
61
  content = "title",
62
62
  description,
63
- dir = "ltr",
63
+ dir,
64
64
  icon,
65
65
  inputAriaLabel,
66
66
  inputPlaceholder,
@@ -101,7 +101,6 @@ export function Banner({
101
101
  `ds-banner--${size}`,
102
102
  `ds-banner--${color}`,
103
103
  `ds-banner--${content}`,
104
- dir === "rtl" && "ds-banner--rtl",
105
104
  className,
106
105
  )}
107
106
  dir={dir}
@@ -19,6 +19,7 @@ export type BreadcrumbItem = {
19
19
 
20
20
  export type BreadcrumbsProps = {
21
21
  "aria-label"?: string;
22
+ className?: string;
22
23
  dir?: "ltr" | "rtl" | "auto";
23
24
  iconOnly?: boolean;
24
25
  items: BreadcrumbItem[];
@@ -49,7 +50,8 @@ function visibleItems(
49
50
 
50
51
  export function Breadcrumbs({
51
52
  "aria-label": ariaLabel,
52
- dir = "ltr",
53
+ className,
54
+ dir,
53
55
  iconOnly = false,
54
56
  items,
55
57
  maxItems,
@@ -67,6 +69,7 @@ export function Breadcrumbs({
67
69
  `ds-breadcrumbs--${surfaceStyle}`,
68
70
  `ds-breadcrumbs--${separator}`,
69
71
  iconOnly && "ds-breadcrumbs--icon-only",
72
+ className,
70
73
  )}
71
74
  dir={dir}
72
75
  >
@@ -112,17 +115,17 @@ function Separator({ separator }: { separator: BreadcrumbSeparator }) {
112
115
  }
113
116
 
114
117
  export function BreadcrumbHomeIcon() {
115
- return <HomeIcon aria-hidden="true" strokeWidth={1.9} />;
118
+ return <HomeIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-190)" />;
116
119
  }
117
120
 
118
121
  export function BreadcrumbFolderIcon() {
119
- return <FolderIcon aria-hidden="true" strokeWidth={1.9} />;
122
+ return <FolderIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-190)" />;
120
123
  }
121
124
 
122
125
  function ChevronIcon() {
123
- return <ChevronRightIcon aria-hidden="true" strokeWidth={2} />;
126
+ return <ChevronRightIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-200)" />;
124
127
  }
125
128
 
126
129
  function OverflowIcon() {
127
- return <EllipsisIcon aria-hidden="true" strokeWidth={2.25} />;
130
+ return <EllipsisIcon aria-hidden="true" strokeWidth="var(--bh-icon-stroke-225)" />;
128
131
  }
@@ -8,7 +8,7 @@ import {
8
8
  import { CatalogContentDivider, CatalogDivider } from "./CatalogDivider";
9
9
  import { FileUpload, type FileUploadFile } from "./FileUpload";
10
10
  import { Table, type TableColumn, type TableRowBase } from "./Table";
11
- import { CatalogTag, type CatalogTagState, type CatalogTagType } from "./CatalogTag";
11
+ import { Tag, type TagState, type TagType } from "@/components/ui/tag";
12
12
  import "./catalogComponentsShowcase.css";
13
13
 
14
14
  type ProjectRow = TableRowBase & {
@@ -32,7 +32,6 @@ const rtlBreadcrumbItems: BreadcrumbItem[] = [
32
32
  { label: "\u0627\u0644\u0645\u0643\u0648\u0646\u0627\u062a", current: true },
33
33
  ];
34
34
 
35
- const breadcrumbAriaLabel = "Catalog breadcrumbs";
36
35
  const rtlBreadcrumbAriaLabel = "\u0627\u0644\u0645\u0633\u0627\u0631";
37
36
 
38
37
  const uploadFiles: FileUploadFile[] = [
@@ -99,8 +98,8 @@ const columns: TableColumn<ProjectRow>[] = [
99
98
  { id: "actions", width: 44, kind: "actions", item: () => ({ type: "action", icon: "more", label: "Open row actions" }) },
100
99
  ];
101
100
 
102
- const tagTypes: CatalogTagType[] = ["simple", "dot", "flag", "avatar", "icon"];
103
- const tagStates: CatalogTagState[] = ["default", "active", "disabled"];
101
+ const tagTypes: TagType[] = ["simple", "dot", "flag", "avatar", "icon"];
102
+ const tagStates: TagState[] = ["default", "active", "disabled"];
104
103
 
105
104
  export function CatalogComponentsShowcase() {
106
105
  const [selectedRows, setSelectedRows] = useState(["1", "2"]);
@@ -109,9 +108,9 @@ export function CatalogComponentsShowcase() {
109
108
  <div className="catalog-showcase">
110
109
  <ShowcaseCard title="Breadcrumbs" description="Default, raised, icon-only, separator, and RTL variants.">
111
110
  <div className="catalog-showcase__stack">
112
- <Breadcrumbs aria-label={breadcrumbAriaLabel} items={breadcrumbItems} overflowLabel="More" />
113
- <Breadcrumbs aria-label={breadcrumbAriaLabel} items={breadcrumbItems} overflowLabel="More" separator="chevron" style="raised" />
114
- <Breadcrumbs aria-label={breadcrumbAriaLabel} iconOnly items={breadcrumbItems} overflowLabel="More" separator="chevron" style="raised" />
111
+ <Breadcrumbs aria-label="Default catalog breadcrumbs" items={breadcrumbItems} overflowLabel="More" />
112
+ <Breadcrumbs aria-label="Raised catalog breadcrumbs" items={breadcrumbItems} overflowLabel="More" separator="chevron" style="raised" />
113
+ <Breadcrumbs aria-label="Icon-only catalog breadcrumbs" iconOnly items={breadcrumbItems} overflowLabel="More" separator="chevron" style="raised" />
115
114
  <Breadcrumbs
116
115
  aria-label={rtlBreadcrumbAriaLabel}
117
116
  dir="rtl"
@@ -136,7 +135,7 @@ export function CatalogComponentsShowcase() {
136
135
  <ShowcaseCard title="Tags" description="Type, state, size, close button, and RTL coverage.">
137
136
  <div className="catalog-showcase__tag-grid">
138
137
  {tagTypes.map((type) => (
139
- <CatalogTag
138
+ <Tag
140
139
  closeLabel="Remove tag"
141
140
  key={type}
142
141
  showCloseButton={type === "simple"}
@@ -144,19 +143,19 @@ export function CatalogComponentsShowcase() {
144
143
  avatar={type === "avatar" ? "A" : undefined}
145
144
  >
146
145
  {type}
147
- </CatalogTag>
146
+ </Tag>
148
147
  ))}
149
148
  {tagStates.map((state) => (
150
- <CatalogTag key={state} state={state} type="dot">
149
+ <Tag key={state} state={state} type="dot">
151
150
  {state}
152
- </CatalogTag>
151
+ </Tag>
153
152
  ))}
154
- <CatalogTag size="xs">xs</CatalogTag>
155
- <CatalogTag size="md">md</CatalogTag>
156
- <CatalogTag closeLabel="Remove tag" size="lg" showCloseButton>
153
+ <Tag size="xs">xs</Tag>
154
+ <Tag size="md">md</Tag>
155
+ <Tag closeLabel="Remove tag" size="lg" showCloseButton>
157
156
  large
158
- </CatalogTag>
159
- <CatalogTag
157
+ </Tag>
158
+ <Tag
160
159
  avatar={"\u0645"}
161
160
  closeLabel={"\u0625\u0632\u0627\u0644\u0629 \u0648\u0633\u0645"}
162
161
  dir="rtl"
@@ -164,7 +163,7 @@ export function CatalogComponentsShowcase() {
164
163
  showCloseButton
165
164
  >
166
165
  {"\u0645\u0644\u0635\u0642"}
167
- </CatalogTag>
166
+ </Tag>
168
167
  </div>
169
168
  </ShowcaseCard>
170
169
 
@@ -1,3 +1,4 @@
1
+ import { Button } from "@/components/ui/button";
1
2
  import "./divider.css";
2
3
 
3
4
  export type DividerBorderStyle = "solid" | "dotted";
@@ -27,7 +28,7 @@ export function CatalogDivider({ borderStyle = "solid" }: CatalogDividerProps) {
27
28
  export function CatalogContentDivider({
28
29
  actionLabel,
29
30
  borderStyle = "solid",
30
- dir = "ltr",
31
+ dir,
31
32
  disabled = false,
32
33
  label,
33
34
  onAction,
@@ -36,9 +37,9 @@ export function CatalogContentDivider({
36
37
  <div className={`ds-content-divider ds-content-divider--${borderStyle}`} dir={dir}>
37
38
  <span aria-hidden="true" className="ds-content-divider__rule" />
38
39
  {actionLabel && onAction ? (
39
- <button className="ds-content-divider__button" disabled={disabled} onClick={onAction} type="button">
40
+ <Button className="ds-content-divider__button" disabled={disabled} onClick={onAction} size="sm" type="button" variant="soft">
40
41
  {actionLabel}
41
- </button>
42
+ </Button>
42
43
  ) : label ? (
43
44
  <span className="ds-content-divider__label">{label}</span>
44
45
  ) : null}
@@ -38,7 +38,7 @@ export function EmptyState({
38
38
  align = "center",
39
39
  className,
40
40
  description,
41
- dir = "ltr",
41
+ dir,
42
42
  icon,
43
43
  iconLabel,
44
44
  size = "default",
@@ -52,7 +52,7 @@ export function FileUpload({
52
52
  accept,
53
53
  browseLabel,
54
54
  cancelLabel = "Cancel upload",
55
- dir = "ltr",
55
+ dir,
56
56
  disabled = false,
57
57
  dropzoneState = "default",
58
58
  files = [],
@@ -154,9 +154,9 @@ export function FileUpload({
154
154
  <p className="ds-file-upload__title">
155
155
  {resolvedPrompt}{" "}
156
156
  {resolvedBrowseLabel && (
157
- <button className="ds-file-upload__browse" disabled={disabled} onClick={browse} type="button">
157
+ <Button className="ds-file-upload__browse" disabled={disabled} onClick={browse} size="xs" type="button" variant="link">
158
158
  {resolvedBrowseLabel}
159
- </button>
159
+ </Button>
160
160
  )}
161
161
  </p>
162
162
  )}
@@ -251,7 +251,14 @@ function FileUploadRow({
251
251
  )}
252
252
 
253
253
  {isProgress && (
254
- <div className="ds-file-upload-row__progress" aria-label={`Upload progress ${progressLabel}`}>
254
+ <div
255
+ aria-label={`Upload progress ${progressLabel}`}
256
+ aria-valuemax={100}
257
+ aria-valuemin={0}
258
+ aria-valuenow={progressValue}
259
+ className="ds-file-upload-row__progress"
260
+ role="progressbar"
261
+ >
255
262
  <span className="ds-file-upload-row__progress-track">
256
263
  <span className="ds-file-upload-row__progress-fill" style={{ width: `${progressValue}%` }} />
257
264
  </span>
@@ -260,9 +267,9 @@ function FileUploadRow({
260
267
  )}
261
268
 
262
269
  {!isSmall && file.status === "error" && (
263
- <button className="ds-file-upload-row__retry" onClick={() => onRetry?.(file.id)} type="button">
270
+ <Button className="ds-file-upload-row__retry" onClick={() => onRetry?.(file.id)} size="xs" type="button" variant="link">
264
271
  {retryLabel}
265
- </button>
272
+ </Button>
266
273
  )}
267
274
  </div>
268
275
  </div>
@@ -397,12 +404,14 @@ function StatusIcon({
397
404
  }) {
398
405
  return (
399
406
  <span
407
+ aria-hidden={standalone ? undefined : "true"}
408
+ aria-label={standalone ? getStatusLabel(status) : undefined}
400
409
  className={cx(
401
410
  "ds-file-upload-row__status",
402
411
  standalone && "ds-file-upload-row__status--standalone",
403
412
  `ds-file-upload-row__status--${status}`
404
413
  )}
405
- aria-label={getStatusLabel(status)}
414
+ role={standalone ? "img" : undefined}
406
415
  >
407
416
  {status === "uploaded" && <CheckboxCircleFillIcon />}
408
417
  {status === "uploading" && <Loader2LineIcon />}
@@ -15,13 +15,12 @@ export type OnboardingStepListItemProps = {
15
15
 
16
16
  export function OnboardingStepListItem({
17
17
  className = "",
18
- dir = "ltr",
18
+ dir,
19
19
  label,
20
20
  showLeadingLine = true,
21
21
  showTrailingLine = true,
22
22
  state = "finished",
23
23
  }: OnboardingStepListItemProps) {
24
- const rtl = dir === "rtl";
25
24
  const isFinished = state === "finished";
26
25
  const isActive = state === "active";
27
26
  const isInactive = state === "inactive";
@@ -31,7 +30,6 @@ export function OnboardingStepListItem({
31
30
  className={[
32
31
  "ds-onboarding-step",
33
32
  `ds-onboarding-step--${state}`,
34
- rtl ? "ds-onboarding-step--rtl" : "ds-onboarding-step--ltr",
35
33
  className,
36
34
  ]
37
35
  .filter(Boolean)
@@ -55,9 +53,7 @@ function CheckCircleIcon() {
55
53
  <CircleCheckIcon
56
54
  aria-hidden="true"
57
55
  className="ds-onboarding-step__check"
58
- height="20"
59
- strokeWidth={2.25}
60
- width="20"
56
+ strokeWidth="var(--bh-icon-stroke-225)"
61
57
  />
62
58
  );
63
59
  }
@@ -1,5 +1,7 @@
1
- import type { ReactNode } from "react";
1
+ import type { ElementType, ReactNode } from "react";
2
2
  import { HomeIcon as LucideHomeIcon } from "lucide-react";
3
+ import { Button } from "@/components/ui/button";
4
+ import { Breadcrumbs as OwnedBreadcrumbs } from "./Breadcrumbs";
3
5
  import { Tabs, type TabsProps } from "../tabs";
4
6
  import "./pageHeader.css";
5
7
 
@@ -31,6 +33,7 @@ export type PageHeaderProps = {
31
33
  description?: string;
32
34
  dir?: "ltr" | "rtl";
33
35
  icon?: ReactNode | false;
36
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
34
37
  metaInfo?: string;
35
38
  tabs?: PageHeaderTabs | false;
36
39
  title?: string;
@@ -41,43 +44,7 @@ function classes(...names: Array<string | false | undefined>) {
41
44
  }
42
45
 
43
46
  function HomeIcon() {
44
- return <LucideHomeIcon aria-hidden="true" className="ds-page-header__home-icon" strokeWidth={1.9} />;
45
- }
46
-
47
- function Breadcrumbs({ items, label }: { items: PageHeaderBreadcrumb[]; label?: string }) {
48
- return (
49
- <nav aria-label={label} className="ds-page-header__breadcrumbs" data-node-id="1134:39921">
50
- <ol>
51
- {items.map((item, index) => {
52
- const isLast = index === items.length - 1;
53
- const isCurrent = item.current === true;
54
- const content = (
55
- <>
56
- {item.icon && <span className="ds-page-header__breadcrumb-icon">{item.icon}</span>}
57
- <span className="ds-page-header__breadcrumb-label">{item.label}</span>
58
- </>
59
- );
60
-
61
- return (
62
- <li key={`${item.label}-${index}`}>
63
- {item.href && !isCurrent ? (
64
- <a href={item.href}>{content}</a>
65
- ) : (
66
- <span aria-current={isCurrent ? "page" : undefined} className={isCurrent ? "is-current" : undefined}>
67
- {content}
68
- </span>
69
- )}
70
- {!isLast && (
71
- <span aria-hidden="true" className="ds-page-header__breadcrumb-separator">
72
- /
73
- </span>
74
- )}
75
- </li>
76
- );
77
- })}
78
- </ol>
79
- </nav>
80
- );
47
+ return <LucideHomeIcon aria-hidden="true" className="ds-page-header__home-icon" strokeWidth="var(--bh-icon-stroke-190)" />;
81
48
  }
82
49
 
83
50
  function Action({ action }: { action: PageHeaderAction }) {
@@ -86,22 +53,37 @@ function Action({ action }: { action: PageHeaderAction }) {
86
53
 
87
54
  if (action.href) {
88
55
  return (
89
- <a aria-label={action.ariaLabel} className={className} href={action.href}>
90
- {label}
91
- </a>
56
+ <Button asChild className={className} variant="secondary">
57
+ <a
58
+ aria-disabled={action.disabled || undefined}
59
+ aria-label={action.ariaLabel}
60
+ href={action.disabled ? undefined : action.href}
61
+ onClick={(event) => {
62
+ if (action.disabled) {
63
+ event.preventDefault();
64
+ return;
65
+ }
66
+ action.onClick?.();
67
+ }}
68
+ tabIndex={action.disabled ? -1 : undefined}
69
+ >
70
+ {label}
71
+ </a>
72
+ </Button>
92
73
  );
93
74
  }
94
75
 
95
76
  return (
96
- <button
77
+ <Button
97
78
  aria-label={action.ariaLabel}
98
79
  className={className}
99
80
  disabled={action.disabled}
100
81
  onClick={action.onClick}
101
82
  type="button"
83
+ variant="secondary"
102
84
  >
103
85
  {label}
104
- </button>
86
+ </Button>
105
87
  );
106
88
  }
107
89
 
@@ -111,7 +93,8 @@ export function PageHeader({
111
93
  breadcrumbs = [],
112
94
  className,
113
95
  description,
114
- dir = "ltr",
96
+ dir,
97
+ headingLevel = 1,
115
98
  icon,
116
99
  metaInfo,
117
100
  tabs = {},
@@ -120,34 +103,42 @@ export function PageHeader({
120
103
  const breadcrumbItems = breadcrumbs === false ? [] : breadcrumbs;
121
104
  const actionItems = actions === false ? [] : actions;
122
105
  const showIcon = icon !== false;
106
+ const Heading = `h${headingLevel}` as ElementType;
123
107
 
124
108
  return (
125
- <header className={classes("ds-page-header", className)} data-node-id="1154:2816" dir={dir}>
126
- {breadcrumbItems.length > 0 && <Breadcrumbs items={breadcrumbItems} label={breadcrumbLabel} />}
127
-
128
- <div className="ds-page-header__section" data-node-id="1134:40171">
129
- <div className="ds-page-header__topline" data-node-id="1134:40048">
130
- <div className="ds-page-header__copy" data-node-id="1134:40046">
131
- <div className="ds-page-header__heading-row" data-node-id="1136:40843">
132
- <div className="ds-page-header__heading-main" data-node-id="1157:13000">
109
+ <header className={classes("ds-page-header", className)} dir={dir}>
110
+ {breadcrumbItems.length > 0 && (
111
+ <OwnedBreadcrumbs
112
+ aria-label={breadcrumbLabel ?? "Breadcrumb"}
113
+ className="ds-page-header__breadcrumbs"
114
+ dir={dir}
115
+ items={breadcrumbItems}
116
+ />
117
+ )}
118
+
119
+ <div className="ds-page-header__section">
120
+ <div className="ds-page-header__topline">
121
+ <div className="ds-page-header__copy">
122
+ <div className="ds-page-header__heading-row">
123
+ <div className="ds-page-header__heading-main">
133
124
  {showIcon && <span className="ds-page-header__icon">{icon ?? <HomeIcon />}</span>}
134
- {title && <h1 data-node-id="1134:39780">{title}</h1>}
125
+ {title && <Heading className="ds-page-header__title">{title}</Heading>}
135
126
  </div>
136
127
  {metaInfo && (
137
- <span className="ds-page-header__meta" data-node-id="1134:40045">
128
+ <span className="ds-page-header__meta">
138
129
  {metaInfo}
139
130
  </span>
140
131
  )}
141
132
  </div>
142
133
  {description && (
143
- <p className="ds-page-header__description" data-node-id="1136:40842">
134
+ <p className="ds-page-header__description">
144
135
  {description}
145
136
  </p>
146
137
  )}
147
138
  </div>
148
139
 
149
140
  {actionItems.length > 0 && (
150
- <div className="ds-page-header__actions" data-node-id="1156:2489">
141
+ <div className="ds-page-header__actions">
151
142
  {actionItems.map((action, index) => (
152
143
  <Action action={action} key={`${action.label}-${index}`} />
153
144
  ))}