banhaten 0.4.0 → 0.6.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 (214) hide show
  1. package/CHANGELOG.md +78 -1
  2. package/CONTRIBUTING.md +6 -4
  3. package/README.md +4 -2
  4. package/docs/design-spacing.md +13 -0
  5. package/docs/design-system/README.md +2 -0
  6. package/docs/design-system/appearances/default.md +1 -1
  7. package/docs/design-system/appearances/rounded.md +1 -1
  8. package/docs/design-system/appearances/sharp.md +1 -1
  9. package/docs/design-system/component-showcase-consistency-report.md +5 -5
  10. package/docs/design-system/components/README.md +15 -0
  11. package/docs/design-system/components/accordion.md +1 -1
  12. package/docs/design-system/components/activity-feed.md +1 -1
  13. package/docs/design-system/components/activity-heatmap.md +1 -1
  14. package/docs/design-system/components/agent-input.md +85 -0
  15. package/docs/design-system/components/alert-dialog.md +1 -1
  16. package/docs/design-system/components/alert.md +10 -4
  17. package/docs/design-system/components/app-header.md +97 -0
  18. package/docs/design-system/components/approval-card.md +134 -0
  19. package/docs/design-system/components/attribute.md +14 -2
  20. package/docs/design-system/components/avatar.md +1 -1
  21. package/docs/design-system/components/badge.md +1 -1
  22. package/docs/design-system/components/banner.md +1 -1
  23. package/docs/design-system/components/breadcrumbs.md +8 -3
  24. package/docs/design-system/components/button-group.md +1 -1
  25. package/docs/design-system/components/button.md +8 -5
  26. package/docs/design-system/components/card.md +15 -1
  27. package/docs/design-system/components/catalog-components.md +1 -1
  28. package/docs/design-system/components/chart.md +95 -24
  29. package/docs/design-system/components/checkbox.md +1 -1
  30. package/docs/design-system/components/collapsible.md +20 -6
  31. package/docs/design-system/components/command.md +1 -1
  32. package/docs/design-system/components/context-menu.md +1 -1
  33. package/docs/design-system/components/date-picker.md +1 -1
  34. package/docs/design-system/components/file-upload.md +1 -1
  35. package/docs/design-system/components/hover-card.md +1 -1
  36. package/docs/design-system/components/inline-citations.md +93 -0
  37. package/docs/design-system/components/input-group.md +1 -1
  38. package/docs/design-system/components/input.md +59 -3
  39. package/docs/design-system/components/item.md +1 -1
  40. package/docs/design-system/components/kbd.md +6 -4
  41. package/docs/design-system/components/menu.md +35 -4
  42. package/docs/design-system/components/menubar.md +1 -1
  43. package/docs/design-system/components/modal.md +2 -2
  44. package/docs/design-system/components/native-select.md +1 -1
  45. package/docs/design-system/components/navigation-menu.md +1 -1
  46. package/docs/design-system/components/onboarding-step-list-item.md +1 -1
  47. package/docs/design-system/components/page-header.md +58 -17
  48. package/docs/design-system/components/pagination.md +2 -2
  49. package/docs/design-system/components/popover.md +1 -1
  50. package/docs/design-system/components/progress-slider.md +1 -1
  51. package/docs/design-system/components/radio-card.md +1 -1
  52. package/docs/design-system/components/radio-group.md +1 -1
  53. package/docs/design-system/components/resizable.md +1 -1
  54. package/docs/design-system/components/rolling-count.md +61 -0
  55. package/docs/design-system/components/scroll-area.md +1 -1
  56. package/docs/design-system/components/select.md +12 -2
  57. package/docs/design-system/components/sheet.md +1 -1
  58. package/docs/design-system/components/shimmer-text.md +67 -0
  59. package/docs/design-system/components/sidebar.md +75 -8
  60. package/docs/design-system/components/skeleton.md +2 -2
  61. package/docs/design-system/components/slideout.md +1 -1
  62. package/docs/design-system/components/slider.md +2 -2
  63. package/docs/design-system/components/social-button.md +2 -2
  64. package/docs/design-system/components/streaming-text.md +82 -0
  65. package/docs/design-system/components/table.md +103 -21
  66. package/docs/design-system/components/tabs.md +25 -6
  67. package/docs/design-system/components/tag.md +1 -1
  68. package/docs/design-system/components/task-list.md +95 -0
  69. package/docs/design-system/components/textarea.md +1 -1
  70. package/docs/design-system/components/thinking-reasoning.md +72 -0
  71. package/docs/design-system/components/thinking-state.md +67 -0
  72. package/docs/design-system/components/timeline.md +1 -1
  73. package/docs/design-system/components/toast.md +1 -1
  74. package/docs/design-system/components/toggle.md +2 -2
  75. package/docs/design-system/components/toolbar.md +11 -6
  76. package/docs/design-system/components/tooltip.md +6 -4
  77. package/docs/design-system/density-and-elevation.md +16 -16
  78. package/docs/design-system/design-system-test-missing-items.md +8 -8
  79. package/docs/design-system/installation.md +2 -0
  80. package/docs/design-system/tabs.md +2 -1
  81. package/docs/design-system/template-gap-audit.md +32 -0
  82. package/docs/design-system/templates.md +59 -0
  83. package/docs/design-system/token-governance.md +15 -2
  84. package/package.json +2 -2
  85. package/registry/components/accordion.tsx +2 -2
  86. package/registry/components/agent-input-parts.tsx +552 -0
  87. package/registry/components/agent-input-state.ts +156 -0
  88. package/registry/components/agent-input.css +307 -0
  89. package/registry/components/agent-input.tsx +382 -0
  90. package/registry/components/alert-dialog.tsx +2 -2
  91. package/registry/components/alert.tsx +23 -7
  92. package/registry/components/app-header.tsx +151 -0
  93. package/registry/components/approval-card.css +409 -0
  94. package/registry/components/approval-card.tsx +401 -0
  95. package/registry/components/approval-frame.tsx +108 -0
  96. package/registry/components/approval-questions.tsx +559 -0
  97. package/registry/components/attribute.tsx +48 -13
  98. package/registry/components/autocomplete.tsx +15 -68
  99. package/registry/components/avatar.tsx +2 -2
  100. package/registry/components/badge.tsx +8 -7
  101. package/registry/components/button-group.tsx +2 -2
  102. package/registry/components/button.tsx +19 -14
  103. package/registry/components/card.tsx +18 -5
  104. package/registry/components/chart-block-data.ts +177 -0
  105. package/registry/components/chart-block.css +171 -0
  106. package/registry/components/chart-block.tsx +260 -0
  107. package/registry/components/chart-interactions.ts +123 -0
  108. package/registry/components/chart-options.cartesian.ts +162 -0
  109. package/registry/components/chart-options.paint.ts +99 -0
  110. package/registry/components/chart-options.parts.ts +72 -0
  111. package/registry/components/chart-options.radial.ts +109 -0
  112. package/registry/components/chart-options.ts +44 -0
  113. package/registry/components/chart-options.types.ts +67 -0
  114. package/registry/components/chart.tsx +1 -1
  115. package/registry/components/checkbox.tsx +6 -6
  116. package/registry/components/collapsible.tsx +43 -3
  117. package/registry/components/command.tsx +1 -1
  118. package/registry/components/context-menu.tsx +1 -1
  119. package/registry/components/data-chart.tsx +40 -0
  120. package/registry/components/date-picker.tsx +9 -9
  121. package/registry/components/echart-theme.ts +246 -0
  122. package/registry/components/echart.css +49 -0
  123. package/registry/components/echart.tsx +178 -0
  124. package/registry/components/expanded/Breadcrumbs.tsx +5 -1
  125. package/registry/components/expanded/PageHeader.tsx +67 -24
  126. package/registry/components/expanded/Table.tsx +60 -22
  127. package/registry/components/expanded/activityFeed.css +2 -2
  128. package/registry/components/expanded/activityHeatmap.css +1 -1
  129. package/registry/components/expanded/banner.css +2 -2
  130. package/registry/components/expanded/breadcrumbs.css +19 -2
  131. package/registry/components/expanded/catalogComponentsShowcase.css +1 -1
  132. package/registry/components/expanded/fileUpload.css +7 -7
  133. package/registry/components/expanded/onboardingStepListItem.css +2 -2
  134. package/registry/components/expanded/pageHeader.css +38 -44
  135. package/registry/components/expanded/slideout.css +4 -4
  136. package/registry/components/expanded/table.css +58 -9
  137. package/registry/components/expanded/timeline.css +1 -1
  138. package/registry/components/hover-card.tsx +1 -1
  139. package/registry/components/inline-citations.css +126 -0
  140. package/registry/components/inline-citations.tsx +292 -0
  141. package/registry/components/input-group.tsx +4 -4
  142. package/registry/components/input.tsx +150 -137
  143. package/registry/components/item.tsx +1 -1
  144. package/registry/components/kbd.tsx +3 -2
  145. package/registry/components/menu.tsx +117 -12
  146. package/registry/components/menubar.tsx +2 -2
  147. package/registry/components/modal.tsx +3 -3
  148. package/registry/components/native-select.tsx +4 -4
  149. package/registry/components/navigation-menu.tsx +2 -2
  150. package/registry/components/pagination.tsx +4 -4
  151. package/registry/components/popover.tsx +3 -3
  152. package/registry/components/progress-slider.tsx +1 -1
  153. package/registry/components/radio-card.tsx +7 -7
  154. package/registry/components/radio-group.tsx +2 -2
  155. package/registry/components/resizable.tsx +2 -2
  156. package/registry/components/rolling-count.css +37 -0
  157. package/registry/components/rolling-count.tsx +110 -0
  158. package/registry/components/scroll-area.tsx +1 -1
  159. package/registry/components/select.tsx +15 -10
  160. package/registry/components/sheet.tsx +2 -2
  161. package/registry/components/shimmer-text.css +69 -0
  162. package/registry/components/shimmer-text.tsx +43 -0
  163. package/registry/components/sidebar.tsx +528 -223
  164. package/registry/components/skeleton.tsx +1 -1
  165. package/registry/components/slider.tsx +2 -2
  166. package/registry/components/social-button.tsx +1 -1
  167. package/registry/components/streaming-text.css +45 -0
  168. package/registry/components/streaming-text.tsx +165 -0
  169. package/registry/components/table-elements.tsx +164 -5
  170. package/registry/components/tabs.css +32 -4
  171. package/registry/components/tabs.tsx +5 -1
  172. package/registry/components/tag.tsx +1 -1
  173. package/registry/components/task-list.css +328 -0
  174. package/registry/components/task-list.tsx +429 -0
  175. package/registry/components/textarea.tsx +4 -4
  176. package/registry/components/thinking-reasoning.css +178 -0
  177. package/registry/components/thinking-reasoning.tsx +253 -0
  178. package/registry/components/thinking-state.css +8 -0
  179. package/registry/components/thinking-state.tsx +46 -0
  180. package/registry/components/toast.tsx +1 -1
  181. package/registry/components/toggle.tsx +2 -2
  182. package/registry/components/toolbar.tsx +6 -6
  183. package/registry/components/tooltip.tsx +6 -4
  184. package/registry/examples/agent-input-demo.tsx +90 -0
  185. package/registry/examples/app-header-demo.tsx +40 -0
  186. package/registry/examples/approval-card-demo.tsx +83 -0
  187. package/registry/examples/button-demo.tsx +3 -0
  188. package/registry/examples/chart-block-demo.tsx +5 -0
  189. package/registry/examples/chart-demo.tsx +6 -21
  190. package/registry/examples/collection-filter-bar-demo.tsx +1 -1
  191. package/registry/examples/control-density-contract-demo.tsx +5 -1
  192. package/registry/examples/inline-citations-demo.tsx +31 -0
  193. package/registry/examples/input-actions-demo.tsx +36 -0
  194. package/registry/examples/menu-demo.tsx +1 -1
  195. package/registry/examples/popover-demo.tsx +1 -0
  196. package/registry/examples/rolling-count-demo.tsx +39 -0
  197. package/registry/examples/shimmer-text-demo.tsx +20 -0
  198. package/registry/examples/streaming-text-demo.tsx +28 -0
  199. package/registry/examples/task-list-demo.tsx +74 -0
  200. package/registry/examples/thinking-reasoning-demo.tsx +74 -0
  201. package/registry/examples/thinking-state-demo.tsx +13 -0
  202. package/registry/guidance/charts.LICENSE.txt +24 -0
  203. package/registry/hooks/use-controllable-state.ts +33 -0
  204. package/registry/hooks/use-listbox-navigation.ts +73 -0
  205. package/registry/hooks/use-motion-end.ts +55 -0
  206. package/registry/hooks/use-reduced-motion.ts +28 -0
  207. package/registry/index.json +1808 -190
  208. package/registry/styles/globals.css +992 -1088
  209. package/registry.json +385 -10
  210. package/schema/registry.schema.json +5 -1
  211. package/schema/tokens.schema.json +8 -4
  212. package/src/cli/file-transaction.js +15 -8
  213. package/src/cli/index.js +22 -29
  214. package/tokens/banhaten.tokens.json +490 -192
@@ -9,6 +9,7 @@ import "./breadcrumbs.css";
9
9
 
10
10
  export type BreadcrumbSeparator = "slash" | "chevron";
11
11
  export type BreadcrumbStyle = "default" | "raised";
12
+ export type BreadcrumbSize = "md" | "sm";
12
13
 
13
14
  export type BreadcrumbItem = {
14
15
  label: string;
@@ -26,6 +27,7 @@ export type BreadcrumbsProps = {
26
27
  maxItems?: number;
27
28
  overflowLabel?: string;
28
29
  separator?: BreadcrumbSeparator;
30
+ size?: BreadcrumbSize;
29
31
  style?: BreadcrumbStyle;
30
32
  };
31
33
 
@@ -57,6 +59,7 @@ export function Breadcrumbs({
57
59
  maxItems,
58
60
  overflowLabel,
59
61
  separator = "slash",
62
+ size = "md",
60
63
  style: surfaceStyle = "default",
61
64
  }: BreadcrumbsProps) {
62
65
  const renderedItems = visibleItems(items, maxItems, overflowLabel);
@@ -68,6 +71,7 @@ export function Breadcrumbs({
68
71
  "ds-breadcrumbs",
69
72
  `ds-breadcrumbs--${surfaceStyle}`,
70
73
  `ds-breadcrumbs--${separator}`,
74
+ size === "sm" && "ds-breadcrumbs--sm",
71
75
  iconOnly && "ds-breadcrumbs--icon-only",
72
76
  className,
73
77
  )}
@@ -82,7 +86,7 @@ export function Breadcrumbs({
82
86
  <span className="ds-breadcrumbs__icon" aria-hidden={item.icon ? "true" : undefined}>
83
87
  {item.overflow ? <OverflowIcon /> : item.icon}
84
88
  </span>
85
- <span className={cx("ds-breadcrumbs__label", iconOnly && "ds-breadcrumbs__label--hidden")}>
89
+ <span className={cx("ds-breadcrumbs__label", iconOnly && "ds-breadcrumbs__label--hidden")} dir="auto">
86
90
  {item.label}
87
91
  </span>
88
92
  </>
@@ -1,4 +1,4 @@
1
- import type { ElementType, ReactNode } from "react";
1
+ import { isValidElement, type ElementType, type ReactNode } from "react";
2
2
  import { HomeIcon as LucideHomeIcon } from "lucide-react";
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { Breadcrumbs as OwnedBreadcrumbs } from "./Breadcrumbs";
@@ -22,38 +22,68 @@ export type PageHeaderAction = {
22
22
 
23
23
  export type PageHeaderTabs = Pick<
24
24
  TabsProps,
25
- "activeIndex" | "ariaLabel" | "defaultActiveIndex" | "items" | "onActiveIndexChange"
25
+ | "activeIndex"
26
+ | "ariaLabel"
27
+ | "defaultActiveIndex"
28
+ | "fullWidth"
29
+ | "items"
30
+ | "onActiveIndexChange"
26
31
  >;
27
32
 
33
+ export type PageHeaderSize = "default" | "comfortable";
34
+
28
35
  export type PageHeaderProps = {
29
- actions?: PageHeaderAction[] | false;
36
+ /** Action data rendered as secondary Buttons, or your own action nodes. */
37
+ actions?: PageHeaderAction[] | ReactNode | false;
30
38
  breadcrumbLabel?: string;
31
39
  breadcrumbs?: PageHeaderBreadcrumb[] | false;
40
+ /** Supporting content under the description, such as a status badge. */
41
+ children?: ReactNode;
32
42
  className?: string;
33
- description?: string;
43
+ description?: ReactNode;
34
44
  dir?: "ltr" | "rtl";
35
45
  icon?: ReactNode | false;
36
46
  headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
37
47
  metaInfo?: string;
48
+ /**
49
+ * `comfortable` is the page heading inside an app shell whose header already
50
+ * carries the breadcrumbs: no block padding, 8px between title and
51
+ * description, 12px between actions, 40px action Buttons, and no icon or tabs
52
+ * unless you pass them.
53
+ */
54
+ size?: PageHeaderSize;
38
55
  tabs?: PageHeaderTabs | false;
39
- title?: string;
56
+ /** Heading text; inline content such as a record name and its type is allowed. */
57
+ title?: ReactNode;
40
58
  };
41
59
 
42
60
  function classes(...names: Array<string | false | undefined>) {
43
61
  return names.filter(Boolean).join(" ");
44
62
  }
45
63
 
64
+ /** Action data is an array of plain `{ label }` objects; anything else is rendered as nodes. */
65
+ function isActionData(actions: PageHeaderProps["actions"]): actions is PageHeaderAction[] {
66
+ return Array.isArray(actions) && actions.every((action) => action !== null && typeof action === "object" && !isValidElement(action) && "label" in action);
67
+ }
68
+
46
69
  function HomeIcon() {
47
70
  return <LucideHomeIcon aria-hidden="true" className="ds-page-header__home-icon" strokeWidth="var(--bh-icon-stroke-190)" />;
48
71
  }
49
72
 
50
- function Action({ action }: { action: PageHeaderAction }) {
51
- const className = "ds-page-header__action";
52
- const label = <span>{action.label}</span>;
73
+ function Action({ action, density }: { action: PageHeaderAction; density: "default" | "comfortable" }) {
74
+ const label = (
75
+ <span
76
+ className="min-w-0 px-[var(--bh-button-label-padding-x)]"
77
+ data-slot="button-label"
78
+ dir="auto"
79
+ >
80
+ {action.label}
81
+ </span>
82
+ );
53
83
 
54
84
  if (action.href) {
55
85
  return (
56
- <Button asChild className={className} variant="secondary">
86
+ <Button asChild density={density} variant="secondary">
57
87
  <a
58
88
  aria-disabled={action.disabled || undefined}
59
89
  aria-label={action.ariaLabel}
@@ -76,9 +106,9 @@ function Action({ action }: { action: PageHeaderAction }) {
76
106
  return (
77
107
  <Button
78
108
  aria-label={action.ariaLabel}
79
- className={className}
80
109
  disabled={action.disabled}
81
110
  onClick={action.onClick}
111
+ density={density}
82
112
  type="button"
83
113
  variant="secondary"
84
114
  >
@@ -91,22 +121,32 @@ export function PageHeader({
91
121
  actions = [],
92
122
  breadcrumbLabel,
93
123
  breadcrumbs = [],
124
+ children,
94
125
  className,
95
126
  description,
96
127
  dir,
97
128
  headingLevel = 1,
98
129
  icon,
99
130
  metaInfo,
100
- tabs = {},
131
+ size = "default",
132
+ tabs,
101
133
  title,
102
134
  }: PageHeaderProps) {
135
+ const comfortable = size === "comfortable";
103
136
  const breadcrumbItems = breadcrumbs === false ? [] : breadcrumbs;
104
- const actionItems = actions === false ? [] : actions;
105
- const showIcon = icon !== false;
137
+ const actionItems = isActionData(actions) ? actions : [];
138
+ const actionNodes = actions !== false && !isActionData(actions) ? actions : null;
139
+ // The comfortable heading has no icon or tabs unless they are passed.
140
+ const showIcon = icon !== false && !(comfortable && icon === undefined);
141
+ const tabsConfig = tabs ?? (comfortable ? false : {});
106
142
  const Heading = `h${headingLevel}` as ElementType;
107
143
 
108
144
  return (
109
- <header className={classes("ds-page-header", className)} dir={dir}>
145
+ <header
146
+ className={classes("ds-page-header", comfortable && "ds-page-header--comfortable", className)}
147
+ data-size={size}
148
+ dir={dir}
149
+ >
110
150
  {breadcrumbItems.length > 0 && (
111
151
  <OwnedBreadcrumbs
112
152
  aria-label={breadcrumbLabel ?? "Breadcrumb"}
@@ -122,7 +162,7 @@ export function PageHeader({
122
162
  <div className="ds-page-header__heading-row">
123
163
  <div className="ds-page-header__heading-main">
124
164
  {showIcon && <span className="ds-page-header__icon">{icon ?? <HomeIcon />}</span>}
125
- {title && <Heading className="ds-page-header__title">{title}</Heading>}
165
+ {title && <Heading className="ds-page-header__title" dir="auto">{title}</Heading>}
126
166
  </div>
127
167
  {metaInfo && (
128
168
  <span className="ds-page-header__meta">
@@ -131,30 +171,33 @@ export function PageHeader({
131
171
  )}
132
172
  </div>
133
173
  {description && (
134
- <p className="ds-page-header__description">
174
+ <p className="ds-page-header__description" dir="auto">
135
175
  {description}
136
176
  </p>
137
177
  )}
178
+ {children && <div className="ds-page-header__supporting">{children}</div>}
138
179
  </div>
139
180
 
140
181
  {actionItems.length > 0 && (
141
182
  <div className="ds-page-header__actions">
142
183
  {actionItems.map((action, index) => (
143
- <Action action={action} key={`${action.label}-${index}`} />
184
+ <Action action={action} density={comfortable ? "comfortable" : "default"} key={`${action.label}-${index}`} />
144
185
  ))}
145
186
  </div>
146
187
  )}
188
+ {actionNodes && <div className="ds-page-header__actions">{actionNodes}</div>}
147
189
  </div>
148
190
 
149
- {tabs !== false && (
191
+ {tabsConfig !== false && (
150
192
  <Tabs
151
- ariaLabel={tabs.ariaLabel}
193
+ ariaLabel={tabsConfig.ariaLabel}
152
194
  className="ds-page-header__tabs"
153
- fullWidth
154
- items={tabs.items}
155
- activeIndex={tabs.activeIndex}
156
- defaultActiveIndex={tabs.defaultActiveIndex}
157
- onActiveIndexChange={tabs.onActiveIndexChange}
195
+ fullWidth={tabsConfig.fullWidth ?? true}
196
+ items={tabsConfig.items}
197
+ activeIndex={tabsConfig.activeIndex}
198
+ defaultActiveIndex={tabsConfig.defaultActiveIndex}
199
+ onActiveIndexChange={tabsConfig.onActiveIndexChange}
200
+ rootClassName="ds-page-header__tabs-root"
158
201
  size="md"
159
202
  variant="underline"
160
203
  dir={dir}
@@ -29,7 +29,7 @@ import {
29
29
  import "./table.css";
30
30
 
31
31
  export type TableDirection = "ltr" | "rtl";
32
- export type TableSize = "sm" | "lg";
32
+ export type TableSize = "sm" | "lg" | "comfortable";
33
33
  export type TableRowState = "default" | "selected" | "disabled";
34
34
  export type TableSortDirection = "asc" | "desc";
35
35
  export type TableSortValue = string | number | boolean | Date | null | undefined;
@@ -79,7 +79,7 @@ export type TableAction = {
79
79
 
80
80
  export type TableItem =
81
81
  | { type: "checkbox"; ariaLabel?: string; checked: boolean; disabled?: boolean; indeterminate?: boolean; onCheckedChange?: (checked: boolean) => void }
82
- | { type: "text"; tone?: "default" | "subtle"; value: string; weight?: "regular" | "medium" }
82
+ | { type: "text"; caption?: string; tone?: "default" | "subtle"; value: string; weight?: "regular" | "medium" }
83
83
  | { type: "avatarText"; caption?: string; name: string; src?: string; status?: "top" | "bottom" }
84
84
  | { type: "rating"; max?: 5; value: number }
85
85
  | { type: "progress"; label?: string; tone?: "success" | "brand"; value: number }
@@ -89,9 +89,9 @@ export type TableItem =
89
89
  | { type: "avatarStack"; addLabel?: string; avatars: AvatarItem[]; canAdd?: boolean; count?: number; onAdd?: () => void }
90
90
  | { type: "tags"; items: string[]; maxVisible?: number }
91
91
  | { type: "file"; label?: string }
92
- | { type: "brandLogoText"; label: string; logoSrc?: string }
93
- | { type: "payment"; brand: string; label?: string }
94
- | { type: "image"; label: string; src: string }
92
+ | { type: "brandLogoText"; caption?: string; label: string; logoSrc?: string }
93
+ | { type: "payment"; brand: string; caption?: string; label?: string }
94
+ | { type: "image"; caption?: string; label: string; src: string }
95
95
  | { type: "action"; icon: TableAction["icon"]; label: string; onAction?: () => void }
96
96
  | { type: "actionGroup"; actions: TableAction[] };
97
97
 
@@ -139,6 +139,7 @@ export type DataTablePagination = {
139
139
  ariaLabel?: string;
140
140
  defaultPage?: number;
141
141
  manual?: boolean;
142
+ messages?: PaginationProps["messages"];
142
143
  onPageChange?: (page: number) => void;
143
144
  page?: number;
144
145
  pageSize?: number;
@@ -293,11 +294,9 @@ export function Table<Row extends TableRowBase>({
293
294
  label={`Select row ${row.id}`}
294
295
  onChange={(checked) => toggleRow(row, checked)}
295
296
  />
296
- ) : column.renderCell ? (
297
- column.renderCell(row)
298
- ) : column.item ? (
299
- renderTableItem(column.item(row), size)
300
- ) : null}
297
+ ) : (
298
+ renderTableCell(column, row, size === "comfortable" ? "lg" : size)
299
+ )}
301
300
  </td>
302
301
  ))}
303
302
  </tr>
@@ -309,6 +308,22 @@ export function Table<Row extends TableRowBase>({
309
308
  );
310
309
  }
311
310
 
311
+ function renderTableCell<Row extends TableRowBase>(
312
+ column: TableColumn<Row>,
313
+ row: Row,
314
+ size: TableSize,
315
+ ) {
316
+ if (column.renderCell) return column.renderCell(row);
317
+ if (column.item) return renderTableItem(column.item(row), size);
318
+ if ((column.kind ?? "data") !== "data") return null;
319
+ if (!Object.prototype.hasOwnProperty.call(row, column.id)) return null;
320
+
321
+ const value: unknown = Reflect.get(row, column.id);
322
+ if (typeof value !== "string" && typeof value !== "number") return null;
323
+
324
+ return renderTableItem({ type: "text", value: String(value) }, size);
325
+ }
326
+
312
327
  const defaultDataTablePagination: DataTablePagination = {};
313
328
 
314
329
  const defaultDataTableLabels: DataTableLabels = {
@@ -389,6 +404,9 @@ export function DataTable<Row extends TableRowBase>({
389
404
  const displayedRows = paginationConfig?.manual ? processedRows : paginationConfig ? processedRows.slice(startIndex, startIndex + pageSize) : processedRows;
390
405
  const visibleFrom = totalRows === 0 ? 0 : startIndex + 1;
391
406
  const visibleTo = paginationConfig?.manual ? Math.min(currentPage * pageSize, totalRows) : Math.min(startIndex + displayedRows.length, totalRows);
407
+ const resultsSummary = paginationConfig
408
+ ? labels.resultsSummary({ from: visibleFrom, to: visibleTo, total: totalRows })
409
+ : null;
392
410
  const hasHeader = Boolean(title || description || actions.length > 0);
393
411
  const hasToolbar = Boolean(searchConfig || filters.length > 0 || selectedRowIds.length > 0 || bulkActions.length > 0);
394
412
  const activeBulkActions = selectedRowIds.length > 0 ? bulkActions : [];
@@ -503,20 +521,23 @@ export function DataTable<Row extends TableRowBase>({
503
521
 
504
522
  {paginationConfig && (
505
523
  <footer className="ds-data-table__footer">
506
- <ToolbarText>{labels.resultsSummary({ from: visibleFrom, to: visibleTo, total: totalRows })}</ToolbarText>
524
+ <ToolbarText>{resultsSummary}</ToolbarText>
507
525
  <Pagination
508
526
  aria-label={
509
527
  paginationConfig.ariaLabel ??
528
+ paginationConfig.messages?.navigation ??
510
529
  (typeof title === "string" ? `${title} pagination` : "Table pagination")
511
530
  }
512
531
  dir={dir}
532
+ messages={paginationConfig.messages}
513
533
  nextDisabled={currentPage >= pageCount}
514
534
  onPageChange={setDataTablePage}
515
535
  page={currentPage}
516
536
  pages={paginationConfig.pages ?? getDataTablePaginationPages(currentPage, pageCount)}
517
537
  previousDisabled={currentPage <= 1}
538
+ caption={resultsSummary}
518
539
  showCaption={paginationConfig.showCaption ?? false}
519
- summary={labels.resultsSummary({ from: visibleFrom, to: visibleTo, total: totalRows })}
540
+ summary={resultsSummary}
520
541
  type={paginationConfig.type ?? "numeric"}
521
542
  variant={paginationConfig.variant ?? "soft"}
522
543
  />
@@ -729,7 +750,9 @@ function getTableItemSortValue(item: TableItem): TableSortValue {
729
750
 
730
751
  function getTableItemText(item: TableItem) {
731
752
  const value = getTableItemSortValue(item);
732
- return value === null || value === undefined ? "" : String(value);
753
+ const text = value === null || value === undefined ? "" : String(value);
754
+ const caption = "caption" in item ? item.caption : undefined;
755
+ return caption ? `${text} ${caption}` : text;
733
756
  }
734
757
 
735
758
  function getDataTablePaginationPages(currentPage: number, pageCount: number): PaginationPage[] {
@@ -784,15 +807,23 @@ function renderTableItem(item: TableItem, tableSize: TableSize) {
784
807
  onChange={item.onCheckedChange}
785
808
  />
786
809
  );
787
- case "text":
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>;
810
+ case "text": {
811
+ const className = `ds-table-item-text ds-table-item-text--${item.tone ?? "default"} ds-table-item-text--${item.weight ?? "regular"}`;
812
+ if (!item.caption) return <span className={className}>{item.value}</span>;
813
+ return (
814
+ <span className={`${className} ds-table-text-stack`}>
815
+ <span dir="auto">{item.value}</span>
816
+ <span className="ds-table-caption" dir="auto">{item.caption}</span>
817
+ </span>
818
+ );
819
+ }
789
820
  case "avatarText":
790
821
  return (
791
822
  <span className="ds-table-avatar-text">
792
823
  <Avatar name={item.name} size={tableSize === "lg" ? "md" : "sm"} src={item.src} />
793
824
  <span>
794
- <strong>{item.name}</strong>
795
- {item.caption && <em>{item.caption}</em>}
825
+ <strong dir="auto">{item.name}</strong>
826
+ {item.caption && <em className="ds-table-caption" dir="auto">{item.caption}</em>}
796
827
  </span>
797
828
  </span>
798
829
  );
@@ -830,11 +861,11 @@ function renderTableItem(item: TableItem, tableSize: TableSize) {
830
861
  case "file":
831
862
  return item.label ? <IconLabel icon={<FileIcon />} label={item.label} /> : null;
832
863
  case "brandLogoText":
833
- return <IconLabel icon={item.logoSrc ? <img alt="" src={item.logoSrc} /> : <BrandIcon />} label={item.label} />;
864
+ return <IconLabel caption={item.caption} icon={item.logoSrc ? <img alt="" src={item.logoSrc} /> : <BrandIcon />} label={item.label} />;
834
865
  case "payment":
835
- return <IconLabel icon={<PaymentIcon />} label={item.label ?? item.brand} />;
866
+ return <IconLabel caption={item.caption} icon={<PaymentIcon />} label={item.label ?? item.brand} />;
836
867
  case "image":
837
- return <IconLabel icon={<img alt="" src={item.src} />} label={item.label} />;
868
+ return <IconLabel caption={item.caption} icon={<img alt="" src={item.src} />} label={item.label} />;
838
869
  case "action":
839
870
  return <IconButton icon={item.icon} label={item.label} onClick={item.onAction} />;
840
871
  case "actionGroup":
@@ -989,11 +1020,18 @@ function Rating({ max, value }: { max: number; value: number }) {
989
1020
  );
990
1021
  }
991
1022
 
992
- function IconLabel({ icon, label }: { icon: ReactNode; label: string }) {
1023
+ function IconLabel({ caption, icon, label }: { caption?: string; icon: ReactNode; label: string }) {
993
1024
  return (
994
1025
  <span className="ds-table-icon-label">
995
1026
  <span aria-hidden="true">{icon}</span>
996
- <span>{label}</span>
1027
+ {caption ? (
1028
+ <span className="ds-table-text-stack">
1029
+ <span dir="auto">{label}</span>
1030
+ <span className="ds-table-caption" dir="auto">{caption}</span>
1031
+ </span>
1032
+ ) : (
1033
+ <span>{label}</span>
1034
+ )}
997
1035
  </span>
998
1036
  );
999
1037
  }
@@ -19,8 +19,8 @@
19
19
  --activity-feed-space-md: var(--bh-space-md-8);
20
20
  --activity-feed-space-lg: var(--bh-space-lg-10);
21
21
  --activity-feed-space-xl: var(--bh-space-xl-12);
22
- --activity-feed-shadow-component: var(--shadow-component-default);
23
- --activity-feed-shadow-color-button: var(--shadow-button-raised);
22
+ --activity-feed-elevation-component: var(--elevation-component-default);
23
+ --activity-feed-elevation-color-button: var(--elevation-button-raised);
24
24
 
25
25
  box-sizing: border-box;
26
26
  position: relative;
@@ -20,7 +20,7 @@
20
20
  }
21
21
 
22
22
  .ds-activity-heatmap:focus-visible {
23
- box-shadow: var(--shadow-component-focus-ring);
23
+ box-shadow: var(--elevation-component-focus-ring);
24
24
  }
25
25
 
26
26
  .ds-activity-heatmap *,
@@ -17,7 +17,7 @@
17
17
  --banner-height-default: calc(var(--bh-space-8xl-48) + var(--bh-space-xl-12));
18
18
  --banner-height-title: calc(var(--bh-space-8xl-48) + var(--bh-space-xs-4));
19
19
  --banner-input-width: var(--bh-input-width);
20
- --banner-shadow-floating: var(--shadow-xl);
20
+ --banner-elevation-floating: var(--elevation-05);
21
21
 
22
22
  box-sizing: border-box;
23
23
  position: relative;
@@ -92,7 +92,7 @@
92
92
  .ds-banner--floating.ds-banner--brand.ds-banner--single-action-inline,
93
93
  .ds-banner--floating.ds-banner--brand.ds-banner--input,
94
94
  .ds-banner--floating.ds-banner--brand.ds-banner--title-description {
95
- box-shadow: var(--banner-shadow-floating);
95
+ box-shadow: var(--banner-elevation-floating);
96
96
  }
97
97
 
98
98
  .ds-banner__main,
@@ -1,6 +1,7 @@
1
1
  .ds-breadcrumbs {
2
2
  --breadcrumbs-color: var(--bh-content-subtle);
3
- --breadcrumbs-current: var(--bh-content-default);
3
+ --breadcrumbs-current: var(--bh-breadcrumbs-current-content);
4
+ --breadcrumbs-hover: var(--bh-content-default);
4
5
  --breadcrumbs-border: var(--bh-border-subtle);
5
6
  --breadcrumbs-surface: var(--bh-bg-raised);
6
7
  --breadcrumbs-radius: var(--bh-radius-lg-8);
@@ -19,6 +20,13 @@
19
20
  line-height: var(--bh-text-body-md-line-height);
20
21
  }
21
22
 
23
+ .ds-breadcrumbs--sm {
24
+ font-size: var(--bh-text-body-sm-medium-font-size);
25
+ font-weight: var(--bh-text-body-sm-medium-font-weight);
26
+ letter-spacing: var(--bh-text-body-sm-medium-letter-spacing);
27
+ line-height: var(--bh-text-body-sm-medium-line-height);
28
+ }
29
+
22
30
  .ds-breadcrumbs *,
23
31
  .ds-breadcrumbs *::before,
24
32
  .ds-breadcrumbs *::after {
@@ -59,7 +67,7 @@
59
67
  }
60
68
 
61
69
  .ds-breadcrumbs a:hover {
62
- color: var(--breadcrumbs-current);
70
+ color: var(--breadcrumbs-hover);
63
71
  }
64
72
 
65
73
  .ds-breadcrumbs a:focus-visible {
@@ -123,6 +131,15 @@
123
131
  display: block;
124
132
  }
125
133
 
134
+ .ds-breadcrumbs--sm .ds-breadcrumbs__separator,
135
+ .ds-breadcrumbs--sm .ds-breadcrumbs__separator svg {
136
+ width: var(--bh-space-xl-12);
137
+ }
138
+
139
+ .ds-breadcrumbs--sm .ds-breadcrumbs__separator svg {
140
+ height: var(--bh-space-xl-12);
141
+ }
142
+
126
143
  .ds-breadcrumbs:dir(rtl) .ds-breadcrumbs__separator svg {
127
144
  transform: scaleX(-1);
128
145
  }
@@ -15,7 +15,7 @@
15
15
  border-radius: var(--bh-radius-lg-8);
16
16
  background: var(--bh-bg-raised);
17
17
  padding: var(--bh-space-4xl-20);
18
- box-shadow: var(--shadow-component-default);
18
+ box-shadow: var(--elevation-component-default);
19
19
  }
20
20
 
21
21
  .catalog-showcase__card:nth-child(4),
@@ -76,7 +76,7 @@
76
76
  color: var(--bh-content-default);
77
77
  box-shadow:
78
78
  inset 0px 0px 0px var(--bh-border-width-default) var(--bh-border-subtle),
79
- var(--shadow-component-default);
79
+ var(--elevation-component-default);
80
80
  }
81
81
 
82
82
  .ds-file-upload__dropzone:hover .ds-file-upload__icon,
@@ -84,7 +84,7 @@
84
84
  .ds-file-upload__dropzone[aria-disabled="true"] .ds-file-upload__icon {
85
85
  box-shadow:
86
86
  inset 0px 0px 0px var(--bh-border-width-default) var(--bh-border-subtle),
87
- var(--shadow-component-default);
87
+ var(--elevation-component-default);
88
88
  }
89
89
 
90
90
  .ds-file-upload__icon svg {
@@ -160,9 +160,9 @@
160
160
 
161
161
  .ds-file-upload-row {
162
162
  --ds-file-upload-row-border: var(--bh-border-subtle);
163
- --ds-file-upload-row-shadow:
163
+ --ds-file-upload-row-elevation:
164
164
  inset 0px 0px 0px var(--bh-border-width-default) var(--ds-file-upload-row-border),
165
- var(--shadow-component-default);
165
+ var(--elevation-component-default);
166
166
 
167
167
  min-height: calc(var(--bh-space-9xl-64) + var(--bh-space-xl-12));
168
168
  display: flex;
@@ -172,7 +172,7 @@
172
172
  border: 0;
173
173
  border-radius: var(--bh-radius-2xl-12);
174
174
  background: var(--bh-bg-raised);
175
- box-shadow: var(--ds-file-upload-row-shadow);
175
+ box-shadow: var(--ds-file-upload-row-elevation);
176
176
  overflow: clip;
177
177
  }
178
178
 
@@ -193,9 +193,9 @@
193
193
 
194
194
  .ds-file-upload-row--error {
195
195
  --ds-file-upload-row-border: var(--bh-border-danger-strong);
196
- --ds-file-upload-row-shadow:
196
+ --ds-file-upload-row-elevation:
197
197
  inset 0px 0px 0px var(--bh-border-width-default) var(--ds-file-upload-row-border),
198
- var(--shadow-sm);
198
+ var(--elevation-02);
199
199
  }
200
200
 
201
201
  .ds-file-upload-row__content {
@@ -18,7 +18,7 @@
18
18
  --onboarding-step-line-width: var(--bh-space-xxs-2);
19
19
  --onboarding-step-line-offset: calc(-1 * var(--bh-space-md-8));
20
20
  --onboarding-step-line-segment-height: calc(var(--bh-space-2xl-14) - var(--bh-space-xxxs-1));
21
- --onboarding-step-shadow: var(--shadow-component-default);
21
+ --onboarding-step-elevation: var(--elevation-component-default);
22
22
 
23
23
  box-sizing: border-box;
24
24
  position: relative;
@@ -50,7 +50,7 @@
50
50
  border-color: var(--onboarding-step-border-default);
51
51
  color: var(--onboarding-step-content-brand);
52
52
  background: var(--onboarding-step-bg-raised);
53
- box-shadow: var(--onboarding-step-shadow);
53
+ box-shadow: var(--onboarding-step-elevation);
54
54
  }
55
55
 
56
56
  .ds-onboarding-step--inactive {