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.
- package/CHANGELOG.md +78 -1
- package/CONTRIBUTING.md +6 -4
- package/README.md +4 -2
- package/docs/design-spacing.md +13 -0
- package/docs/design-system/README.md +2 -0
- package/docs/design-system/appearances/default.md +1 -1
- package/docs/design-system/appearances/rounded.md +1 -1
- package/docs/design-system/appearances/sharp.md +1 -1
- package/docs/design-system/component-showcase-consistency-report.md +5 -5
- package/docs/design-system/components/README.md +15 -0
- package/docs/design-system/components/accordion.md +1 -1
- package/docs/design-system/components/activity-feed.md +1 -1
- package/docs/design-system/components/activity-heatmap.md +1 -1
- package/docs/design-system/components/agent-input.md +85 -0
- package/docs/design-system/components/alert-dialog.md +1 -1
- package/docs/design-system/components/alert.md +10 -4
- package/docs/design-system/components/app-header.md +97 -0
- package/docs/design-system/components/approval-card.md +134 -0
- package/docs/design-system/components/attribute.md +14 -2
- package/docs/design-system/components/avatar.md +1 -1
- package/docs/design-system/components/badge.md +1 -1
- package/docs/design-system/components/banner.md +1 -1
- package/docs/design-system/components/breadcrumbs.md +8 -3
- package/docs/design-system/components/button-group.md +1 -1
- package/docs/design-system/components/button.md +8 -5
- package/docs/design-system/components/card.md +15 -1
- package/docs/design-system/components/catalog-components.md +1 -1
- package/docs/design-system/components/chart.md +95 -24
- package/docs/design-system/components/checkbox.md +1 -1
- package/docs/design-system/components/collapsible.md +20 -6
- package/docs/design-system/components/command.md +1 -1
- package/docs/design-system/components/context-menu.md +1 -1
- package/docs/design-system/components/date-picker.md +1 -1
- package/docs/design-system/components/file-upload.md +1 -1
- package/docs/design-system/components/hover-card.md +1 -1
- package/docs/design-system/components/inline-citations.md +93 -0
- package/docs/design-system/components/input-group.md +1 -1
- package/docs/design-system/components/input.md +59 -3
- package/docs/design-system/components/item.md +1 -1
- package/docs/design-system/components/kbd.md +6 -4
- package/docs/design-system/components/menu.md +35 -4
- package/docs/design-system/components/menubar.md +1 -1
- package/docs/design-system/components/modal.md +2 -2
- package/docs/design-system/components/native-select.md +1 -1
- package/docs/design-system/components/navigation-menu.md +1 -1
- package/docs/design-system/components/onboarding-step-list-item.md +1 -1
- package/docs/design-system/components/page-header.md +58 -17
- package/docs/design-system/components/pagination.md +2 -2
- package/docs/design-system/components/popover.md +1 -1
- package/docs/design-system/components/progress-slider.md +1 -1
- package/docs/design-system/components/radio-card.md +1 -1
- package/docs/design-system/components/radio-group.md +1 -1
- package/docs/design-system/components/resizable.md +1 -1
- package/docs/design-system/components/rolling-count.md +61 -0
- package/docs/design-system/components/scroll-area.md +1 -1
- package/docs/design-system/components/select.md +12 -2
- package/docs/design-system/components/sheet.md +1 -1
- package/docs/design-system/components/shimmer-text.md +67 -0
- package/docs/design-system/components/sidebar.md +75 -8
- package/docs/design-system/components/skeleton.md +2 -2
- package/docs/design-system/components/slideout.md +1 -1
- package/docs/design-system/components/slider.md +2 -2
- package/docs/design-system/components/social-button.md +2 -2
- package/docs/design-system/components/streaming-text.md +82 -0
- package/docs/design-system/components/table.md +103 -21
- package/docs/design-system/components/tabs.md +25 -6
- package/docs/design-system/components/tag.md +1 -1
- package/docs/design-system/components/task-list.md +95 -0
- package/docs/design-system/components/textarea.md +1 -1
- package/docs/design-system/components/thinking-reasoning.md +72 -0
- package/docs/design-system/components/thinking-state.md +67 -0
- package/docs/design-system/components/timeline.md +1 -1
- package/docs/design-system/components/toast.md +1 -1
- package/docs/design-system/components/toggle.md +2 -2
- package/docs/design-system/components/toolbar.md +11 -6
- package/docs/design-system/components/tooltip.md +6 -4
- package/docs/design-system/density-and-elevation.md +16 -16
- package/docs/design-system/design-system-test-missing-items.md +8 -8
- package/docs/design-system/installation.md +2 -0
- package/docs/design-system/tabs.md +2 -1
- package/docs/design-system/template-gap-audit.md +32 -0
- package/docs/design-system/templates.md +59 -0
- package/docs/design-system/token-governance.md +15 -2
- package/package.json +2 -2
- package/registry/components/accordion.tsx +2 -2
- package/registry/components/agent-input-parts.tsx +552 -0
- package/registry/components/agent-input-state.ts +156 -0
- package/registry/components/agent-input.css +307 -0
- package/registry/components/agent-input.tsx +382 -0
- package/registry/components/alert-dialog.tsx +2 -2
- package/registry/components/alert.tsx +23 -7
- package/registry/components/app-header.tsx +151 -0
- package/registry/components/approval-card.css +409 -0
- package/registry/components/approval-card.tsx +401 -0
- package/registry/components/approval-frame.tsx +108 -0
- package/registry/components/approval-questions.tsx +559 -0
- package/registry/components/attribute.tsx +48 -13
- package/registry/components/autocomplete.tsx +15 -68
- package/registry/components/avatar.tsx +2 -2
- package/registry/components/badge.tsx +8 -7
- package/registry/components/button-group.tsx +2 -2
- package/registry/components/button.tsx +19 -14
- package/registry/components/card.tsx +18 -5
- package/registry/components/chart-block-data.ts +177 -0
- package/registry/components/chart-block.css +171 -0
- package/registry/components/chart-block.tsx +260 -0
- package/registry/components/chart-interactions.ts +123 -0
- package/registry/components/chart-options.cartesian.ts +162 -0
- package/registry/components/chart-options.paint.ts +99 -0
- package/registry/components/chart-options.parts.ts +72 -0
- package/registry/components/chart-options.radial.ts +109 -0
- package/registry/components/chart-options.ts +44 -0
- package/registry/components/chart-options.types.ts +67 -0
- package/registry/components/chart.tsx +1 -1
- package/registry/components/checkbox.tsx +6 -6
- package/registry/components/collapsible.tsx +43 -3
- package/registry/components/command.tsx +1 -1
- package/registry/components/context-menu.tsx +1 -1
- package/registry/components/data-chart.tsx +40 -0
- package/registry/components/date-picker.tsx +9 -9
- package/registry/components/echart-theme.ts +246 -0
- package/registry/components/echart.css +49 -0
- package/registry/components/echart.tsx +178 -0
- package/registry/components/expanded/Breadcrumbs.tsx +5 -1
- package/registry/components/expanded/PageHeader.tsx +67 -24
- package/registry/components/expanded/Table.tsx +60 -22
- package/registry/components/expanded/activityFeed.css +2 -2
- package/registry/components/expanded/activityHeatmap.css +1 -1
- package/registry/components/expanded/banner.css +2 -2
- package/registry/components/expanded/breadcrumbs.css +19 -2
- package/registry/components/expanded/catalogComponentsShowcase.css +1 -1
- package/registry/components/expanded/fileUpload.css +7 -7
- package/registry/components/expanded/onboardingStepListItem.css +2 -2
- package/registry/components/expanded/pageHeader.css +38 -44
- package/registry/components/expanded/slideout.css +4 -4
- package/registry/components/expanded/table.css +58 -9
- package/registry/components/expanded/timeline.css +1 -1
- package/registry/components/hover-card.tsx +1 -1
- package/registry/components/inline-citations.css +126 -0
- package/registry/components/inline-citations.tsx +292 -0
- package/registry/components/input-group.tsx +4 -4
- package/registry/components/input.tsx +150 -137
- package/registry/components/item.tsx +1 -1
- package/registry/components/kbd.tsx +3 -2
- package/registry/components/menu.tsx +117 -12
- package/registry/components/menubar.tsx +2 -2
- package/registry/components/modal.tsx +3 -3
- package/registry/components/native-select.tsx +4 -4
- package/registry/components/navigation-menu.tsx +2 -2
- package/registry/components/pagination.tsx +4 -4
- package/registry/components/popover.tsx +3 -3
- package/registry/components/progress-slider.tsx +1 -1
- package/registry/components/radio-card.tsx +7 -7
- package/registry/components/radio-group.tsx +2 -2
- package/registry/components/resizable.tsx +2 -2
- package/registry/components/rolling-count.css +37 -0
- package/registry/components/rolling-count.tsx +110 -0
- package/registry/components/scroll-area.tsx +1 -1
- package/registry/components/select.tsx +15 -10
- package/registry/components/sheet.tsx +2 -2
- package/registry/components/shimmer-text.css +69 -0
- package/registry/components/shimmer-text.tsx +43 -0
- package/registry/components/sidebar.tsx +528 -223
- package/registry/components/skeleton.tsx +1 -1
- package/registry/components/slider.tsx +2 -2
- package/registry/components/social-button.tsx +1 -1
- package/registry/components/streaming-text.css +45 -0
- package/registry/components/streaming-text.tsx +165 -0
- package/registry/components/table-elements.tsx +164 -5
- package/registry/components/tabs.css +32 -4
- package/registry/components/tabs.tsx +5 -1
- package/registry/components/tag.tsx +1 -1
- package/registry/components/task-list.css +328 -0
- package/registry/components/task-list.tsx +429 -0
- package/registry/components/textarea.tsx +4 -4
- package/registry/components/thinking-reasoning.css +178 -0
- package/registry/components/thinking-reasoning.tsx +253 -0
- package/registry/components/thinking-state.css +8 -0
- package/registry/components/thinking-state.tsx +46 -0
- package/registry/components/toast.tsx +1 -1
- package/registry/components/toggle.tsx +2 -2
- package/registry/components/toolbar.tsx +6 -6
- package/registry/components/tooltip.tsx +6 -4
- package/registry/examples/agent-input-demo.tsx +90 -0
- package/registry/examples/app-header-demo.tsx +40 -0
- package/registry/examples/approval-card-demo.tsx +83 -0
- package/registry/examples/button-demo.tsx +3 -0
- package/registry/examples/chart-block-demo.tsx +5 -0
- package/registry/examples/chart-demo.tsx +6 -21
- package/registry/examples/collection-filter-bar-demo.tsx +1 -1
- package/registry/examples/control-density-contract-demo.tsx +5 -1
- package/registry/examples/inline-citations-demo.tsx +31 -0
- package/registry/examples/input-actions-demo.tsx +36 -0
- package/registry/examples/menu-demo.tsx +1 -1
- package/registry/examples/popover-demo.tsx +1 -0
- package/registry/examples/rolling-count-demo.tsx +39 -0
- package/registry/examples/shimmer-text-demo.tsx +20 -0
- package/registry/examples/streaming-text-demo.tsx +28 -0
- package/registry/examples/task-list-demo.tsx +74 -0
- package/registry/examples/thinking-reasoning-demo.tsx +74 -0
- package/registry/examples/thinking-state-demo.tsx +13 -0
- package/registry/guidance/charts.LICENSE.txt +24 -0
- package/registry/hooks/use-controllable-state.ts +33 -0
- package/registry/hooks/use-listbox-navigation.ts +73 -0
- package/registry/hooks/use-motion-end.ts +55 -0
- package/registry/hooks/use-reduced-motion.ts +28 -0
- package/registry/index.json +1808 -190
- package/registry/styles/globals.css +992 -1088
- package/registry.json +385 -10
- package/schema/registry.schema.json +5 -1
- package/schema/tokens.schema.json +8 -4
- package/src/cli/file-transaction.js +15 -8
- package/src/cli/index.js +22 -29
- package/tokens/banhaten.tokens.json +490 -192
|
@@ -36,7 +36,7 @@ export function LoadingPanel() {
|
|
|
36
36
|
|
|
37
37
|
## Token Contract
|
|
38
38
|
|
|
39
|
-
Skeleton consumes Banhaten background, border, radius, spacing, button, card, opacity, and
|
|
39
|
+
Skeleton consumes Banhaten background, border, radius, spacing, button, card, opacity, and elevation tokens. Custom layout sizing should be passed through CSS variables or token classes rather than raw dimensions.
|
|
40
40
|
|
|
41
41
|
- Fill: `--bh-bg-muted`, `--bh-bg-subtle`, `--bh-bg-raised-subtle`, `--bh-bg-brand-subtle`
|
|
42
42
|
- Highlight: `--bh-bg-raised-subtle`, `--bh-bg-default`, `--bh-bg-brand-soft`
|
|
@@ -86,6 +86,6 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
86
86
|
| `features` | `shimmer animation`, `reduced motion`, `text lines`, `avatar placeholders`, `button placeholders`, `input placeholders`, `card placeholders`, `list placeholders`, `table placeholders`, `form placeholders` |
|
|
87
87
|
| `shapes` | `block`, `text`, `circle` |
|
|
88
88
|
| `sizes` | `xs`, `sm`, `md`, `lg`, `xl` |
|
|
89
|
-
| `tokens` | `--bh-alpha-alpha-transparent`, `--bh-bg-brand-soft`, `--bh-bg-brand-subtle`, `--bh-bg-default`, `--bh-bg-default-hover`, `--bh-bg-muted`, `--bh-bg-raised`, `--bh-bg-raised-subtle`, `--bh-bg-subtle`, `--bh-border-default`, `--bh-border-subtle`, `--bh-button-lg-height`, `--bh-button-md-height`, `--bh-button-sm-height`, `--bh-button-xl-height`, `--bh-card-desktop-content-padding`, `--bh-control-default`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-md-6`, `--bh-radius-xl-10`, `--bh-skeleton-bg`, `--bh-skeleton-duration`, `--bh-skeleton-height`, `--bh-skeleton-highlight`, `--bh-skeleton-radius`, `--bh-skeleton-shimmer-width`, `--bh-skeleton-table-columns`, `--bh-skeleton-width`, `--bh-space-11xl-96`, `--bh-space-12xl-128`, `--bh-space-13xl-160`, `--bh-space-14xl-192`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-8xl-48`, `--bh-space-9xl-64`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-text-body-md-regular-line-height`, `--
|
|
89
|
+
| `tokens` | `--bh-alpha-alpha-transparent`, `--bh-bg-brand-soft`, `--bh-bg-brand-subtle`, `--bh-bg-default`, `--bh-bg-default-hover`, `--bh-bg-muted`, `--bh-bg-raised`, `--bh-bg-raised-subtle`, `--bh-bg-subtle`, `--bh-border-default`, `--bh-border-subtle`, `--bh-button-lg-height`, `--bh-button-md-height`, `--bh-button-sm-height`, `--bh-button-xl-height`, `--bh-card-desktop-content-padding`, `--bh-control-default`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-md-6`, `--bh-radius-xl-10`, `--bh-skeleton-bg`, `--bh-skeleton-duration`, `--bh-skeleton-height`, `--bh-skeleton-highlight`, `--bh-skeleton-radius`, `--bh-skeleton-shimmer-width`, `--bh-skeleton-table-columns`, `--bh-skeleton-width`, `--bh-space-11xl-96`, `--bh-space-12xl-128`, `--bh-space-13xl-160`, `--bh-space-14xl-192`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-8xl-48`, `--bh-space-9xl-64`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-text-body-md-regular-line-height`, `--elevation-component-default` |
|
|
90
90
|
| `tones` | `default`, `subtle`, `surface`, `brand` |
|
|
91
91
|
<!-- registry-contract:end -->
|
|
@@ -76,5 +76,5 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
76
76
|
| `modes` | `desktop`, `mobile` |
|
|
77
77
|
| `sizes` | `small`, `medium`, `large` |
|
|
78
78
|
| `states` | `header-action`, `footer-actions`, `without-footer`, `primary-only` |
|
|
79
|
-
| `tokens` | `--bh-bg-default`, `--bh-bg-dim`, `--bh-bg-muted`, `--bh-bg-neutral-medium`, `--bh-bg-neutral-subtle`, `--bh-bg-raised`, `--bh-border-default`, `--bh-border-input`, `--bh-border-subtle`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-link`, `--bh-content-on-brand`, `--bh-content-on-color`, `--bh-content-subtle`, `--bh-content-success-default`, `--bh-control-default`, `--bh-font-family`, `--bh-font-weight-medium`, `--bh-icon-stroke-220`, `--bh-interactive-brand-default`, `--bh-interactive-ghost-hover`, `--bh-radius-2xl-12`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-space-11xl-96`, `--bh-space-12xl-128`, `--bh-space-18xl-320`, `--bh-space-19xl-384`, `--bh-space-2xl-14`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-9xl-64`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-space-xxs-2`, `--bh-space-xxxs-1`, `--bh-text-body-md-medium-line-height`, `--bh-text-body-md-regular-font-size`, `--bh-text-body-md-regular-letter-spacing`, `--bh-text-body-md-regular-line-height`, `--bh-text-heading-sm-medium-font-size`, `--bh-text-heading-sm-medium-font-weight`, `--bh-text-heading-sm-medium-line-height`, `--bh-text-heading-xs-medium-font-size`, `--bh-text-heading-xs-medium-line-height`, `--
|
|
79
|
+
| `tokens` | `--bh-bg-default`, `--bh-bg-dim`, `--bh-bg-muted`, `--bh-bg-neutral-medium`, `--bh-bg-neutral-subtle`, `--bh-bg-raised`, `--bh-border-default`, `--bh-border-input`, `--bh-border-subtle`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-link`, `--bh-content-on-brand`, `--bh-content-on-color`, `--bh-content-subtle`, `--bh-content-success-default`, `--bh-control-default`, `--bh-font-family`, `--bh-font-weight-medium`, `--bh-icon-stroke-220`, `--bh-interactive-brand-default`, `--bh-interactive-ghost-hover`, `--bh-radius-2xl-12`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-space-11xl-96`, `--bh-space-12xl-128`, `--bh-space-18xl-320`, `--bh-space-19xl-384`, `--bh-space-2xl-14`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-9xl-64`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-space-xxs-2`, `--bh-space-xxxs-1`, `--bh-text-body-md-medium-line-height`, `--bh-text-body-md-regular-font-size`, `--bh-text-body-md-regular-letter-spacing`, `--bh-text-body-md-regular-line-height`, `--bh-text-heading-sm-medium-font-size`, `--bh-text-heading-sm-medium-font-weight`, `--bh-text-heading-sm-medium-line-height`, `--bh-text-heading-xs-medium-font-size`, `--bh-text-heading-xs-medium-line-height`, `--elevation-button-raised`, `--elevation-button-soft`, `--elevation-component-default` |
|
|
80
80
|
<!-- registry-contract:end -->
|
|
@@ -30,7 +30,7 @@ Use `showTicks` and `tickCount` for dotted track markers. Use `indicator` for Fi
|
|
|
30
30
|
|
|
31
31
|
## Tokens
|
|
32
32
|
|
|
33
|
-
Slider consumes component aliases for track size, thumb size, pill height, tick geometry, thumb
|
|
33
|
+
Slider consumes component aliases for track size, thumb size, pill height, tick geometry, thumb elevation, and semantic color roles. Tones map to semantic brand/status/accent tokens so color theme changes are inherited automatically.
|
|
34
34
|
|
|
35
35
|
## Install
|
|
36
36
|
|
|
@@ -75,7 +75,7 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
75
75
|
| `indicators` | `none`, `lower-text`, `upper-text`, `lower-tooltip`, `upper-tooltip` |
|
|
76
76
|
| `sizes` | `sm`, `default`, `lg` |
|
|
77
77
|
| `states` | `default`, `disabled` |
|
|
78
|
-
| `tokens` | `--bh-bg-always-white`, `--bh-bg-brand-soft`, `--bh-bg-brand-subtle`, `--bh-bg-danger-default`, `--bh-bg-danger-soft`, `--bh-bg-danger-subtle`, `--bh-bg-default`, `--bh-bg-disabled`, `--bh-bg-neutral-bold`, `--bh-bg-neutral-soft`, `--bh-bg-neutral-subtle`, `--bh-bg-raised`, `--bh-bg-success-default`, `--bh-bg-success-soft`, `--bh-bg-success-subtle`, `--bh-bg-warning-default`, `--bh-bg-warning-soft`, `--bh-bg-warning-subtle`, `--bh-border-brand-strong`, `--bh-border-brand-subtle`, `--bh-border-danger-strong`, `--bh-border-danger-subtle`, `--bh-border-disabled`, `--bh-border-strong`, `--bh-border-subtle`, `--bh-border-success-strong`, `--bh-border-success-subtle`, `--bh-border-warning-strong`, `--bh-border-warning-subtle`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-subtle`, `--bh-interactive-indicator-default`, `--bh-opacity-60`, `--bh-radius-full`, `--bh-radius-md-6`, `--bh-slider-indicator-gap`, `--bh-slider-pill-height`, `--bh-slider-pill-radius`, `--bh-slider-pill-thumb-border-width`, `--bh-slider-pill-thumb-width`, `--bh-slider-pill-track-bg`, `--bh-slider-range-bg`, `--bh-slider-thumb-bg`, `--bh-slider-thumb-grip-bg`, `--bh-slider-thumb-grip-width`, `--bh-slider-thumb-pill-bg`, `--bh-slider-thumb-pill-border`, `--bh-slider-thumb-radius`, `--bh-slider-thumb-ring-bg`, `--bh-slider-thumb-ring-width-current`, `--bh-slider-thumb-ring-width-lg`, `--bh-slider-thumb-ring-width-md`, `--bh-slider-thumb-ring-width-sm`, `--bh-slider-thumb-size-current`, `--bh-slider-thumb-size-lg`, `--bh-slider-thumb-size-md`, `--bh-slider-thumb-size-sm`, `--bh-slider-tick-bg`, `--bh-slider-tick-gap`, `--bh-slider-tick-size`, `--bh-slider-track-bg`, `--bh-slider-track-radius`, `--bh-slider-track-size-current`, `--bh-slider-track-size-lg`, `--bh-slider-track-size-md`, `--bh-slider-track-size-sm`, `--bh-slider-vertical-height`, `--bh-slider-width`, `--bh-space-6xl-32`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-text-body-2xs-medium-font-size`, `--bh-text-body-2xs-medium-font-weight`, `--bh-text-body-2xs-medium-letter-spacing`, `--bh-text-body-2xs-medium-line-height`, `--bh-text-body-2xs-regular-font-size`, `--bh-text-body-2xs-regular-font-weight`, `--bh-text-body-2xs-regular-letter-spacing`, `--bh-text-body-2xs-regular-line-height`, `--bh-text-body-sm-medium-font-size`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-medium-letter-spacing`, `--bh-text-body-sm-medium-line-height`, `--bh-z-base`, `--bh-z-overlay`, `--bh-z-popover`, `--bh-z-raised`, `--
|
|
78
|
+
| `tokens` | `--bh-bg-always-white`, `--bh-bg-brand-soft`, `--bh-bg-brand-subtle`, `--bh-bg-danger-default`, `--bh-bg-danger-soft`, `--bh-bg-danger-subtle`, `--bh-bg-default`, `--bh-bg-disabled`, `--bh-bg-neutral-bold`, `--bh-bg-neutral-soft`, `--bh-bg-neutral-subtle`, `--bh-bg-raised`, `--bh-bg-success-default`, `--bh-bg-success-soft`, `--bh-bg-success-subtle`, `--bh-bg-warning-default`, `--bh-bg-warning-soft`, `--bh-bg-warning-subtle`, `--bh-border-brand-strong`, `--bh-border-brand-subtle`, `--bh-border-danger-strong`, `--bh-border-danger-subtle`, `--bh-border-disabled`, `--bh-border-strong`, `--bh-border-subtle`, `--bh-border-success-strong`, `--bh-border-success-subtle`, `--bh-border-warning-strong`, `--bh-border-warning-subtle`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-subtle`, `--bh-interactive-indicator-default`, `--bh-opacity-60`, `--bh-radius-full`, `--bh-radius-md-6`, `--bh-slider-indicator-gap`, `--bh-slider-pill-height`, `--bh-slider-pill-radius`, `--bh-slider-pill-thumb-border-width`, `--bh-slider-pill-thumb-width`, `--bh-slider-pill-track-bg`, `--bh-slider-range-bg`, `--bh-slider-thumb-bg`, `--bh-slider-thumb-grip-bg`, `--bh-slider-thumb-grip-width`, `--bh-slider-thumb-pill-bg`, `--bh-slider-thumb-pill-border`, `--bh-slider-thumb-radius`, `--bh-slider-thumb-ring-bg`, `--bh-slider-thumb-ring-width-current`, `--bh-slider-thumb-ring-width-lg`, `--bh-slider-thumb-ring-width-md`, `--bh-slider-thumb-ring-width-sm`, `--bh-slider-thumb-size-current`, `--bh-slider-thumb-size-lg`, `--bh-slider-thumb-size-md`, `--bh-slider-thumb-size-sm`, `--bh-slider-tick-bg`, `--bh-slider-tick-gap`, `--bh-slider-tick-size`, `--bh-slider-track-bg`, `--bh-slider-track-radius`, `--bh-slider-track-size-current`, `--bh-slider-track-size-lg`, `--bh-slider-track-size-md`, `--bh-slider-track-size-sm`, `--bh-slider-vertical-height`, `--bh-slider-width`, `--bh-space-6xl-32`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-text-body-2xs-medium-font-size`, `--bh-text-body-2xs-medium-font-weight`, `--bh-text-body-2xs-medium-letter-spacing`, `--bh-text-body-2xs-medium-line-height`, `--bh-text-body-2xs-regular-font-size`, `--bh-text-body-2xs-regular-font-weight`, `--bh-text-body-2xs-regular-letter-spacing`, `--bh-text-body-2xs-regular-line-height`, `--bh-text-body-sm-medium-font-size`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-medium-letter-spacing`, `--bh-text-body-sm-medium-line-height`, `--bh-z-base`, `--bh-z-overlay`, `--bh-z-popover`, `--bh-z-raised`, `--elevation-slider-thumb`, `--elevation-slider-thumb-focus`, `--elevation-slider-tooltip` |
|
|
79
79
|
| `tones` | `brand`, `neutral`, `success`, `warning`, `danger` |
|
|
80
80
|
| `valueModes` | `single`, `range`, `multi-thumb` |
|
|
81
81
|
<!-- registry-contract:end -->
|
|
@@ -19,7 +19,7 @@ Social Button is the sign-in action primitive for supported identity providers.
|
|
|
19
19
|
|
|
20
20
|
## Token Contract
|
|
21
21
|
|
|
22
|
-
Social Button uses Banhaten control, typography, radius, focus, spacing, and
|
|
22
|
+
Social Button uses Banhaten control, typography, radius, focus, spacing, and elevation tokens. Provider colors are the limited brand-color exception and should stay scoped to this component.
|
|
23
23
|
|
|
24
24
|
## RTL
|
|
25
25
|
|
|
@@ -60,6 +60,6 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
60
60
|
| `registryDependencies` | None |
|
|
61
61
|
| `platforms` | `apple`, `facebook`, `google`, `linkedin`, `twitter`, `whatsapp` |
|
|
62
62
|
| `sizes` | `default`, `icon` |
|
|
63
|
-
| `tokens` | `--bh-bg-raised`, `--bh-bg-raised-hover`, `--bh-border-default`, `--bh-border-focus`, `--bh-border-width-default`, `--bh-content-default`, `--bh-icon-stroke-200`, `--bh-icon-stroke-225`, `--bh-opacity-50`, `--bh-social-apple-bg`, `--bh-social-apple-border`, `--bh-social-apple-fg`, `--bh-social-apple-hover`, `--bh-social-apple-icon`, `--bh-social-apple-icon-border`, `--bh-social-apple-outline-icon`, `--bh-social-apple-width`, `--bh-social-apple-width-rtl`, `--bh-social-button-bg`, `--bh-social-button-bg-hover`, `--bh-social-button-border`, `--bh-social-button-current-border`, `--bh-social-button-fg`, `--bh-social-button-focus-border`, `--bh-social-button-gap`, `--bh-social-button-height`, `--bh-social-button-icon`, `--bh-social-button-icon-size`, `--bh-social-button-label-padding-inline-end`, `--bh-social-button-padding-x`, `--bh-social-button-padding-y`, `--bh-social-button-radius`, `--bh-social-button-width`, `--bh-social-button-width-rtl`, `--bh-social-facebook-bg`, `--bh-social-facebook-fg`, `--bh-social-facebook-hover`, `--bh-social-facebook-icon`, `--bh-social-facebook-outline-icon`, `--bh-social-facebook-width`, `--bh-social-facebook-width-rtl`, `--bh-social-google-bg`, `--bh-social-google-border`, `--bh-social-google-fg`, `--bh-social-google-hover`, `--bh-social-google-width`, `--bh-social-google-width-rtl`, `--bh-social-linkedin-bg`, `--bh-social-linkedin-fg`, `--bh-social-linkedin-hover`, `--bh-social-linkedin-icon`, `--bh-social-linkedin-outline-icon`, `--bh-social-linkedin-width`, `--bh-social-linkedin-width-rtl`, `--bh-social-twitter-bg`, `--bh-social-twitter-border`, `--bh-social-twitter-fg`, `--bh-social-twitter-hover`, `--bh-social-twitter-icon`, `--bh-social-twitter-icon-border`, `--bh-social-twitter-outline-icon`, `--bh-social-twitter-width`, `--bh-social-twitter-width-rtl`, `--bh-social-whatsapp-bg`, `--bh-social-whatsapp-fg`, `--bh-social-whatsapp-hover`, `--bh-social-whatsapp-icon`, `--bh-social-whatsapp-outline-icon`, `--bh-social-whatsapp-width`, `--bh-social-whatsapp-width-rtl`, `--bh-text-button-font-size`, `--bh-text-button-font-weight`, `--bh-text-button-letter-spacing`, `--bh-text-button-line-height`, `--
|
|
63
|
+
| `tokens` | `--bh-bg-raised`, `--bh-bg-raised-hover`, `--bh-border-default`, `--bh-border-focus`, `--bh-border-width-default`, `--bh-content-default`, `--bh-icon-stroke-200`, `--bh-icon-stroke-225`, `--bh-opacity-50`, `--bh-social-apple-bg`, `--bh-social-apple-border`, `--bh-social-apple-fg`, `--bh-social-apple-hover`, `--bh-social-apple-icon`, `--bh-social-apple-icon-border`, `--bh-social-apple-outline-icon`, `--bh-social-apple-width`, `--bh-social-apple-width-rtl`, `--bh-social-button-bg`, `--bh-social-button-bg-hover`, `--bh-social-button-border`, `--bh-social-button-current-border`, `--bh-social-button-fg`, `--bh-social-button-focus-border`, `--bh-social-button-gap`, `--bh-social-button-height`, `--bh-social-button-icon`, `--bh-social-button-icon-size`, `--bh-social-button-label-padding-inline-end`, `--bh-social-button-padding-x`, `--bh-social-button-padding-y`, `--bh-social-button-radius`, `--bh-social-button-width`, `--bh-social-button-width-rtl`, `--bh-social-facebook-bg`, `--bh-social-facebook-fg`, `--bh-social-facebook-hover`, `--bh-social-facebook-icon`, `--bh-social-facebook-outline-icon`, `--bh-social-facebook-width`, `--bh-social-facebook-width-rtl`, `--bh-social-google-bg`, `--bh-social-google-border`, `--bh-social-google-fg`, `--bh-social-google-hover`, `--bh-social-google-width`, `--bh-social-google-width-rtl`, `--bh-social-linkedin-bg`, `--bh-social-linkedin-fg`, `--bh-social-linkedin-hover`, `--bh-social-linkedin-icon`, `--bh-social-linkedin-outline-icon`, `--bh-social-linkedin-width`, `--bh-social-linkedin-width-rtl`, `--bh-social-twitter-bg`, `--bh-social-twitter-border`, `--bh-social-twitter-fg`, `--bh-social-twitter-hover`, `--bh-social-twitter-icon`, `--bh-social-twitter-icon-border`, `--bh-social-twitter-outline-icon`, `--bh-social-twitter-width`, `--bh-social-twitter-width-rtl`, `--bh-social-whatsapp-bg`, `--bh-social-whatsapp-fg`, `--bh-social-whatsapp-hover`, `--bh-social-whatsapp-icon`, `--bh-social-whatsapp-outline-icon`, `--bh-social-whatsapp-width`, `--bh-social-whatsapp-width-rtl`, `--bh-text-button-font-size`, `--bh-text-button-font-weight`, `--bh-text-button-letter-spacing`, `--bh-text-button-line-height`, `--elevation-component-default`, `--elevation-social-button`, `--elevation-social-button-focus` |
|
|
64
64
|
| `variants` | `solid`, `outline` |
|
|
65
65
|
<!-- registry-contract:end -->
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Streaming Text
|
|
2
|
+
|
|
3
|
+
`StreamingText` shows a response arriving token by token. A slim caret trails the text: solid while `streaming`, then a slow blink once the response is complete.
|
|
4
|
+
|
|
5
|
+
The component renders only what it is given. When your transport streams tokens, pass the text so far and keep `streaming` on until the response ends. To reveal a finished response locally, run it through `useTypewriter`, which returns the revealed part and whether it is still typing.
|
|
6
|
+
|
|
7
|
+
## API
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { StreamingText, useTypewriter } from "@/components/ui/streaming-text"
|
|
11
|
+
|
|
12
|
+
<StreamingText streaming={isStreaming} text={partial} />
|
|
13
|
+
|
|
14
|
+
const typed = useTypewriter(response)
|
|
15
|
+
<StreamingText streaming={typed.typing} text={typed.text} />
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### StreamingText
|
|
19
|
+
|
|
20
|
+
| Prop | Type | Default | Description |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| `text` | `string` | – | The text that has arrived so far. |
|
|
23
|
+
| `streaming` | `boolean` | `false` | More text is on its way: the caret stays solid and the root sets `aria-busy`. |
|
|
24
|
+
| `caret` | `boolean` | `true` | Shows the caret after the text. |
|
|
25
|
+
| `as` | `"p" \| "div" \| "span"` | `"p"` | Rendered element. The ref is typed as `HTMLElement` for all three. |
|
|
26
|
+
|
|
27
|
+
### useTypewriter
|
|
28
|
+
|
|
29
|
+
`useTypewriter(text, options)` returns `{ text, typing }`: the revealed part of `text` and whether more is still to come.
|
|
30
|
+
|
|
31
|
+
| Option | Type | Default | Description |
|
|
32
|
+
| --- | --- | --- | --- |
|
|
33
|
+
| `speed` | `number` | `220` | Characters revealed per second. |
|
|
34
|
+
| `onComplete` | `() => void` | – | Called once each time the reveal reaches the end of a text, including an empty one. |
|
|
35
|
+
|
|
36
|
+
Text that grows continues from what is already revealed, and different text starts over; the returned text is always a prefix of the current one. To replay a reveal, remount the component that calls the hook, for example with a `key`.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx banhaten add streaming-text
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
Primary registry example: `examples/streaming-text-demo.tsx`.
|
|
47
|
+
|
|
48
|
+
## Token Contract
|
|
49
|
+
|
|
50
|
+
Prose uses the `body-sm-regular` scale in `--bh-content-default`. The caret is `--bh-streaming-text-caret-width` wide (aliasing `--bh-space-xxs-2`), one em tall, rounded with `--bh-radius-full`, offset by `--bh-space-xxs-2`, and blinks on `--bh-streaming-text-caret-duration`.
|
|
51
|
+
|
|
52
|
+
Covered source files: `components/streaming-text.tsx` and `components/streaming-text.css`.
|
|
53
|
+
|
|
54
|
+
## RTL Rules
|
|
55
|
+
|
|
56
|
+
- Text inherits document direction.
|
|
57
|
+
- The caret follows the text through a logical start margin, so it trails Arabic text at its end edge.
|
|
58
|
+
|
|
59
|
+
## Accessibility
|
|
60
|
+
|
|
61
|
+
The root exposes `aria-busy` while streaming and `data-state` for styling. The caret is decorative and hidden from assistive technology. Under `prefers-reduced-motion`, `useTypewriter` reveals the whole text at once and the caret does not blink.
|
|
62
|
+
|
|
63
|
+
<!-- registry-contract:start -->
|
|
64
|
+
## Registry Contract
|
|
65
|
+
|
|
66
|
+
This table is generated from `registry/index.json`; edit the component-owned source under `registry/catalog/components/`, not this table or the compiled index.
|
|
67
|
+
`supportedExports` is the exhaustive runtime import contract grouped by source module. `parts` is an ordered, non-exhaustive anatomy list.
|
|
68
|
+
|
|
69
|
+
| Field | Values |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `supportedExports` | `components/streaming-text.tsx: StreamingText, useTypewriter` |
|
|
72
|
+
| `parts` | `StreamingText` |
|
|
73
|
+
| `registryDependencies` | None |
|
|
74
|
+
| `props.text` | `string; default null; The text that has arrived so far.` |
|
|
75
|
+
| `props.streaming` | `boolean; default "false"; True while more text is on its way; keeps the caret solid and sets aria-busy.` |
|
|
76
|
+
| `props.caret` | `boolean; default "true"; Shows the caret after the visible text.` |
|
|
77
|
+
| `props.as` | `"p" \| "div" \| "span"; default "p"; Rendered element; the ref is an HTMLElement for all three.` |
|
|
78
|
+
| `props.speed` | `number; default "220"; useTypewriter option: characters revealed per second.` |
|
|
79
|
+
| `features` | `caller-driven streaming`, `local typewriter reveal`, `reduced motion`, `RTL` |
|
|
80
|
+
| `states` | `streaming`, `complete` |
|
|
81
|
+
| `tokens` | `--bh-content-default`, `--bh-font-family`, `--bh-opacity-0`, `--bh-opacity-100`, `--bh-radius-full`, `--bh-space-xxs-2`, `--bh-streaming-text-caret-duration`, `--bh-streaming-text-caret-width`, `--bh-text-body-sm-regular-font-size`, `--bh-text-body-sm-regular-font-weight`, `--bh-text-body-sm-regular-letter-spacing`, `--bh-text-body-sm-regular-line-height` |
|
|
82
|
+
<!-- registry-contract:end -->
|
|
@@ -57,7 +57,10 @@ The table is composed from these parts:
|
|
|
57
57
|
- `TableItem`: the content variant rendered inside a cell.
|
|
58
58
|
- `DataTable`: composed workflow shell for search, filters, actions, pagination, and table states.
|
|
59
59
|
- `DataTableSurface`: ARIA table surface for bespoke CSS-grid rows while keeping the table root owned by Banhaten.
|
|
60
|
-
- `TableRoot`, `TableHeader`, `TableBody`, `TableRowElement`, `TableHead`, and `TableCell`: owned semantic elements for product-specific read-only tables that do not fit the data-column API.
|
|
60
|
+
- `TableRoot`, `TableHeader`, `TableBody`, `TableRowElement`, `TableHead`, and `TableCell`: owned semantic elements for product-specific read-only tables that do not fit the data-column API. They are unstyled unless `TableRoot` has `size="comfortable"`.
|
|
61
|
+
- `TableContainer`: the rounded, bordered frame for the owned elements.
|
|
62
|
+
- `TableCellText`: line + subline content for an owned `TableCell`, with an optional caption and leading icon or avatar.
|
|
63
|
+
- `TableCellLink`: a flush text action for a cell label that opens a record.
|
|
61
64
|
|
|
62
65
|
Use semantic `<table>` markup when the table is mostly read-only data. Use an ARIA grid only when the product needs cell-level keyboard navigation. Interactive content inside cells, such as checkboxes, toggles, and actions, must remain real controls.
|
|
63
66
|
|
|
@@ -77,7 +80,7 @@ const exampleColumns = [
|
|
|
77
80
|
];
|
|
78
81
|
```
|
|
79
82
|
|
|
80
|
-
RTL tables reverse the visual column order and align cell content to the end. The selection cell moves to the far right, and the action cell moves to the far left. The row data order should not change.
|
|
83
|
+
RTL tables reverse the visual column order and align cell content to the end. The selection cell moves to the far right, and the action cell moves to the far left. The row data order should not change. An `avatarText` name and caption each resolve their own content direction, so an opposing script truncates at its actual end without changing the avatar layout.
|
|
81
84
|
|
|
82
85
|
## Sizing
|
|
83
86
|
|
|
@@ -86,9 +89,77 @@ RTL tables reverse the visual column order and align cell content to the end. Th
|
|
|
86
89
|
| Header row | `sm` | `44px` | `44px` |
|
|
87
90
|
| Body row | `sm` | `48px` | `44px` |
|
|
88
91
|
| Body row | `lg` | `56px` | `56px` |
|
|
92
|
+
| Header row | `comfortable` | `44px` | `44px` |
|
|
93
|
+
| Body row | `comfortable` | `64px` | `64px` |
|
|
94
|
+
|
|
95
|
+
`sm` rows use body-sm (14/20) text and `lg` rows use body-md (15/24), matching the `sm` and `lg` cells of `Table item 1.2`.
|
|
89
96
|
|
|
90
97
|
Cell padding is `12px` on the inline axis. The fixed `44px` action column keeps `8px` inline padding around its `28px` action button. Inline content gap is `6px`; header label and sort icon gap is `4px`. The base checkbox and action columns use fixed dimensions so hover, selected, and icon states do not shift the table.
|
|
91
98
|
|
|
99
|
+
### Comfortable size
|
|
100
|
+
|
|
101
|
+
`size="comfortable"` follows the `Table item 1.2` spec used in product frames:
|
|
102
|
+
|
|
103
|
+
- Header cells are `44px` tall with `8px` inline padding, body-sm medium (14/20) labels in `content/subtle`, on the `interactive/table/header` fill.
|
|
104
|
+
- Body cells are `64px` tall with `8px` inline padding and body-md (15/24) text in `content/default`.
|
|
105
|
+
- Rows have no dividers.
|
|
106
|
+
- The frame has a `12px` radius.
|
|
107
|
+
|
|
108
|
+
Items inside a comfortable table use the `lg` item sizes. Use `Badge size="lg"` for status badges in comfortable rows.
|
|
109
|
+
|
|
110
|
+
The owned table elements take the same spec. Set `size="comfortable"` on `TableRoot` and wrap it in `TableContainer`, which draws the `12px` frame with a `1px` subtle border inside its edge and scrolls the table horizontally. Without a size, `TableRoot` and its cells stay unstyled.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<TableContainer>
|
|
114
|
+
<TableRoot aria-label="Requisitions" size="comfortable">
|
|
115
|
+
<TableHeader>
|
|
116
|
+
<TableRowElement>
|
|
117
|
+
<TableHead>Request</TableHead>
|
|
118
|
+
<TableHead>Approval</TableHead>
|
|
119
|
+
</TableRowElement>
|
|
120
|
+
</TableHeader>
|
|
121
|
+
<TableBody>
|
|
122
|
+
<TableRowElement>
|
|
123
|
+
<TableCell>Senior Product Designer</TableCell>
|
|
124
|
+
<TableCell><Badge color="amber" size="lg">Your approval</Badge></TableCell>
|
|
125
|
+
</TableRowElement>
|
|
126
|
+
</TableBody>
|
|
127
|
+
</TableRoot>
|
|
128
|
+
</TableContainer>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Line + subline cells
|
|
132
|
+
|
|
133
|
+
`Table item 1.2` gives its `lg` text cells an optional caption (`hasCaption`) under the label. This covers base leading, base regular, subtle, avatar + text, brand logo + text, payment and image cells.
|
|
134
|
+
|
|
135
|
+
- The label keeps the row's text size: body-md (15/24) in `lg` and `comfortable` rows.
|
|
136
|
+
- The caption is body-2xs (12/16) in `content/subtle`.
|
|
137
|
+
- There is no gap between the two lines, so the pair fills `40px`. That leaves `8px` above and below in a `56px` `lg` row, and `12px` in a `64px` comfortable row.
|
|
138
|
+
- A leading icon or avatar sits `6px` before the text, and the pair centres on it.
|
|
139
|
+
- `sm` cells in Figma are single-line.
|
|
140
|
+
|
|
141
|
+
In `Table`, set `caption` on a `text`, `avatarText`, `brandLogoText`, `payment` or `image` item. DataTable search also matches captions, while sorting uses the label.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
{ id: "request", header: "Request", width: "fill", item: (row) => ({ type: "text", weight: "medium", value: row.title, caption: row.id }) }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
With the owned elements, put `TableCellText` inside `TableCell`. Its children are the label. Set `weight="medium"` for a leading label and `tone="subtle"` for secondary text. When the label opens the record, make it a `TableCellLink`, not a link `Button`. A Button brings its control padding, which moves the label off the caption's start edge and adds space above the pair. `TableCellLink` keeps the cell's text style and colour and sits flush with the cell padding. It underlines on hover and focus and draws the focus ring 2px outside the text. It renders a button by default; with `asChild` it styles an anchor or router link.
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
<TableCell>
|
|
151
|
+
<TableCellText caption="REQ-1042 · Product">
|
|
152
|
+
<TableCellLink onClick={() => openRequest("1042")}>Senior Product Designer</TableCellLink>
|
|
153
|
+
</TableCellText>
|
|
154
|
+
</TableCell>
|
|
155
|
+
|
|
156
|
+
<TableCellText caption="REQ-1038 · Platform">
|
|
157
|
+
<TableCellLink asChild>
|
|
158
|
+
<a href="#/requisitions/1038">Frontend Engineer</a>
|
|
159
|
+
</TableCellLink>
|
|
160
|
+
</TableCellText>
|
|
161
|
+
```
|
|
162
|
+
|
|
92
163
|
## States
|
|
93
164
|
|
|
94
165
|
| State | Treatment |
|
|
@@ -98,7 +169,7 @@ Cell padding is `12px` on the inline axis. The fixed `44px` action column keeps
|
|
|
98
169
|
| `selected` | Selected row background and checked selection control. |
|
|
99
170
|
| `disabled` | Disabled content color and no row actions. |
|
|
100
171
|
|
|
101
|
-
The selected checkbox uses the brand or checkbox fill and a
|
|
172
|
+
The selected checkbox uses the brand or checkbox fill and a mode-aware contrasting check icon. Header checkbox supports checked, unchecked, and indeterminate states.
|
|
102
173
|
|
|
103
174
|
## Token Mapping
|
|
104
175
|
|
|
@@ -151,6 +222,8 @@ type TableSortState = { columnId: string; direction: "asc" | "desc" } | null;
|
|
|
151
222
|
|
|
152
223
|
Use `item` for standard content variants and `renderCell` only for custom product-specific cells. Custom cells still render inside `TableCell`, so spacing, alignment, row height, truncation, RTL behavior, and disabled state stay consistent.
|
|
153
224
|
|
|
225
|
+
Cell content resolves in this order: `renderCell`, `item`, then an own row field whose key matches the column `id`. The implicit row-field fallback applies only to ordinary data columns and renders string or number values—including `0`—through the standard text item. Use `item` or `renderCell` for formatted values, booleans, dates, objects, and custom nodes. Selection and action columns never read an implicit row value; return `null` from `renderCell` when a deliberately blank data cell is required.
|
|
226
|
+
|
|
154
227
|
Use `sortValue` when the visible cell content should sort by an underlying value, such as dates, percentages, status rank, or formatted numbers. Use `searchValue` when the cell should contribute searchable text that is different from the visible content.
|
|
155
228
|
|
|
156
229
|
## Table Items
|
|
@@ -160,10 +233,10 @@ The item catalog in Figma supports these cell content variants. Code may normali
|
|
|
160
233
|
| Figma item type | Use for | Notes |
|
|
161
234
|
| --- | --- | --- |
|
|
162
235
|
| `checkbox` | Row selection | Fixed selection column. |
|
|
163
|
-
| `baseLeading` | Primary label | Medium body label. |
|
|
164
|
-
| `baseRegular` | Standard body text | Regular body label. |
|
|
165
|
-
| `subtle` | Secondary text | Uses subtle content color. |
|
|
166
|
-
| `avatarText` | Person, owner, assignee | Avatar plus label; `lg` may include caption. |
|
|
236
|
+
| `baseLeading` | Primary label | Medium body label; `lg` may include caption. |
|
|
237
|
+
| `baseRegular` | Standard body text | Regular body label; `lg` may include caption. |
|
|
238
|
+
| `subtle` | Secondary text | Uses subtle content color; `lg` may include caption. |
|
|
239
|
+
| `avatarText` | Person, owner, assignee | Avatar plus medium label; `lg` may include caption. Name and caption resolve direction independently. |
|
|
167
240
|
| `rating` | Star rating | Five 20px stars with full, half, and empty states. |
|
|
168
241
|
| `progress` | Percent completion | 8px track, percent label, mirrored fill in RTL. |
|
|
169
242
|
| `toggle` | Binary row setting | 32px switch in `sm`; preserve native button semantics. |
|
|
@@ -172,16 +245,16 @@ The item catalog in Figma supports these cell content variants. Code may normali
|
|
|
172
245
|
| `avatarStack` | Team or group membership | Overlapped 20px avatars, optional count and add button. |
|
|
173
246
|
| `tags` | Neutral labels | Bordered tags with compact 24px height. |
|
|
174
247
|
| `file` | Attachment indicator | Icon-only file cell. |
|
|
175
|
-
| `brandLogoText` | Brand or account identity | Logo plus label. |
|
|
176
|
-
| `payment` | Payment method | Payment icon plus optional label. |
|
|
177
|
-
| `image` | Thumbnail item | Image thumbnail plus label. |
|
|
248
|
+
| `brandLogoText` | Brand or account identity | Logo plus medium label; `lg` may include caption. |
|
|
249
|
+
| `payment` | Payment method | Payment icon plus optional medium label; `lg` may include caption. |
|
|
250
|
+
| `image` | Thumbnail item | Image thumbnail plus medium label; `lg` may include caption. |
|
|
178
251
|
| `action` | Single row menu | Icon button, usually overflow menu. |
|
|
179
252
|
| `actionGroup` | Multiple row actions | View, edit, delete group; reverse order in RTL. |
|
|
180
253
|
|
|
181
254
|
```ts
|
|
182
255
|
type TableItem =
|
|
183
256
|
| { type: "checkbox"; checked: boolean; indeterminate?: boolean }
|
|
184
|
-
| { type: "text"; tone?: "default" | "subtle"; weight?: "regular" | "medium"; value: string }
|
|
257
|
+
| { type: "text"; tone?: "default" | "subtle"; weight?: "regular" | "medium"; value: string; caption?: string }
|
|
185
258
|
| { type: "avatarText"; name: string; caption?: string; src?: string; status?: "top" | "bottom" }
|
|
186
259
|
| { type: "rating"; value: number; max?: 5 }
|
|
187
260
|
| { type: "progress"; value: number; label?: string; tone?: "success" | "brand" }
|
|
@@ -191,9 +264,9 @@ type TableItem =
|
|
|
191
264
|
| { type: "avatarStack"; avatars: AvatarItem[]; count?: number; canAdd?: boolean }
|
|
192
265
|
| { type: "tags"; items: string[]; maxVisible?: number }
|
|
193
266
|
| { type: "file"; label?: string }
|
|
194
|
-
| { type: "brandLogoText"; label: string; logoSrc?: string }
|
|
195
|
-
| { type: "payment"; brand: "maestro" | "mastercard" | "visa" | string; label?: string }
|
|
196
|
-
| { type: "image"; label: string; src: string }
|
|
267
|
+
| { type: "brandLogoText"; label: string; caption?: string; logoSrc?: string }
|
|
268
|
+
| { type: "payment"; brand: "maestro" | "mastercard" | "visa" | string; label?: string; caption?: string }
|
|
269
|
+
| { type: "image"; label: string; caption?: string; src: string }
|
|
197
270
|
| { type: "action"; label: string; icon: "more"; onAction: () => void }
|
|
198
271
|
| { type: "actionGroup"; actions: TableAction[] };
|
|
199
272
|
|
|
@@ -337,7 +410,9 @@ const filters = [
|
|
|
337
410
|
/>
|
|
338
411
|
```
|
|
339
412
|
|
|
340
|
-
`DataTable` applies active filter predicates, searches row text from `getRowSearchText`, column `searchValue`, or table item text, sorts by column `sortValue` or table item value, and paginates the resulting rows. Set `pagination={false}` for unpaginated datasets.
|
|
413
|
+
`DataTable` applies active filter predicates, searches row text from `getRowSearchText`, column `searchValue`, or table item text, sorts by column `sortValue` or table item value, and paginates the resulting rows. Set `pagination={false}` for unpaginated datasets. When `pagination.showCaption` is enabled, the Pagination caption receives the same measured visible range and total as the DataTable footer summary; the standalone Pagination demo default is never used for table data.
|
|
414
|
+
|
|
415
|
+
Direction does not choose language. Pass `pagination.messages` to localize Pagination controls and accessible names, and pass `labels.resultsSummary` to format the measured DataTable range and total. An explicit `pagination.ariaLabel` overrides the message catalog's navigation label.
|
|
341
416
|
|
|
342
417
|
## Accessibility
|
|
343
418
|
|
|
@@ -391,7 +466,7 @@ npx banhaten add table
|
|
|
391
466
|
|
|
392
467
|
## API
|
|
393
468
|
|
|
394
|
-
Supported runtime exports: `DataTable`, `Table`, `DataTableSurface`, `TableBody`, `TableCell`, `TableHead`, `TableHeader`, `TableRoot`, `TableRowElement`.
|
|
469
|
+
Supported runtime exports: `DataTable`, `Table`, `DataTableSurface`, `TableBody`, `TableCell`, `TableCellLink`, `TableCellText`, `TableContainer`, `TableHead`, `TableHeader`, `TableRoot`, `TableRowElement`.
|
|
395
470
|
|
|
396
471
|
Use the live registry docs for the generated API table and source-backed examples.
|
|
397
472
|
|
|
@@ -409,7 +484,7 @@ Covered source files: `components/expanded/table.css`, `components/expanded/Tabl
|
|
|
409
484
|
|
|
410
485
|
## RTL Rules
|
|
411
486
|
|
|
412
|
-
|
|
487
|
+
The table layout inherits document direction and uses logical spacing. Within `avatarText`, the independently truncated name and caption use `dir="auto"`; this preserves the identifying start of Latin content in an RTL table (and Arabic content in an LTR table) while the avatar and columns continue to mirror with the table.
|
|
413
488
|
|
|
414
489
|
<!-- registry-contract:start -->
|
|
415
490
|
## Registry Contract
|
|
@@ -419,11 +494,18 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
419
494
|
|
|
420
495
|
| Field | Values |
|
|
421
496
|
| --- | --- |
|
|
422
|
-
| `supportedExports` | `components/expanded/Table.tsx: DataTable, Table`, `components/table-elements.tsx: DataTableSurface, TableBody, TableCell, TableHead, TableHeader, TableRoot, TableRowElement` |
|
|
423
|
-
| `parts` | `Table`, `DataTable`, `DataTableSurface`, `TableRoot`, `TableHeader`, `TableBody`, `TableRowElement`, `TableHead`, `TableCell` |
|
|
497
|
+
| `supportedExports` | `components/expanded/Table.tsx: DataTable, Table`, `components/table-elements.tsx: DataTableSurface, TableBody, TableCell, TableCellLink, TableCellText, TableContainer, TableHead, TableHeader, TableRoot, TableRowElement` |
|
|
498
|
+
| `parts` | `Table`, `DataTable`, `DataTableSurface`, `TableContainer`, `TableRoot`, `TableHeader`, `TableBody`, `TableRowElement`, `TableHead`, `TableCell`, `TableCellText`, `TableCellLink` |
|
|
424
499
|
| `registryDependencies` | `avatar`, `badge`, `button`, `checkbox`, `pagination`, `progress`, `spinner`, `tag`, `toggle`, `toolbar` |
|
|
500
|
+
| `props.Table.size` | `"sm" \| "lg" \| "comfortable"; default "sm"; sm and lg are 48px and 56px rows; sm uses body-sm text and lg uses body-md, as in the Table item 1.2 sm and lg cells. comfortable follows Table item 1.2: 44px headers in body-sm medium on the table-header fill, 64px rows with 8px cells and body-md text, no row rules, and a 12px radius.` |
|
|
501
|
+
| `props.TableItem.caption` | `string; default null; Second line under the label on text, avatarText, brandLogoText, payment and image items (Table item 1.2 hasCaption): body-2xs in content/subtle with no gap. Use it in lg or comfortable rows; the two lines fill 40px of a 56px row. Search matches captions; sorting uses the label.` |
|
|
502
|
+
| `props.TableRoot.size` | `"comfortable"; default null; Applies the comfortable spec to TableHead and TableCell. Without a size the owned elements stay unstyled.` |
|
|
503
|
+
| `props.TableContainer` | `div; default null; Frame for the owned elements: 12px radius, default background, and a 1px subtle border drawn inside the edge. The table scrolls horizontally inside it.` |
|
|
504
|
+
| `props.TableCellText` | `span; default null; Text content for an owned TableCell: children are the label, caption adds the second line, leading adds an icon or avatar 6px before the text, and tone (default \| subtle) and weight (regular \| medium) set the label. The label keeps the cell text size; use TableCellLink when it opens a record.` |
|
|
505
|
+
| `props.TableCellLink` | `button; default null; A cell label that opens a record: it keeps the cell text style and colour, sits flush with the cell padding, underlines on hover and focus, and shows the focus ring 2px outside the text. Renders a button, or its child (such as an anchor) with asChild. Use it as the TableCellText label instead of a padded Button.` |
|
|
506
|
+
| `features` | `measured DataTable pagination captions`, `explicit DataTable pagination message catalogs`, `safe row-key cell fallback`, `comfortable size with 44px headers, 64px rows, 8px cells, body-md text, and no row rules`, `TableContainer frame with a 12px radius and an inside border`, `comfortable size on the owned table elements through TableRoot size`, `line + subline cells: an optional caption under text, avatarText, brandLogoText, payment and image items, and TableCellText for the owned elements`, `TableCellLink for flush record links in owned cells` |
|
|
425
507
|
| `modes` | `table`, `data-table` |
|
|
426
|
-
| `sizes` | `sm`, `lg` |
|
|
508
|
+
| `sizes` | `sm`, `lg`, `comfortable` |
|
|
427
509
|
| `states` | `default`, `selected`, `disabled`, `loading`, `empty`, `error` |
|
|
428
|
-
| `tokens` | `--bh-avatar-fallback-font-size-md`, `--bh-avatar-fallback-font-size-xs`, `--bh-avatar-size-md`, `--bh-avatar-size-xs`, `--bh-bg-accent-amber-subtle`, `--bh-bg-accent-fuchsia-subtle`, `--bh-bg-accent-golden-default`, `--bh-bg-brand-default`, `--bh-bg-brand-soft`, `--bh-bg-danger-subtle`, `--bh-bg-default`, `--bh-bg-muted`, `--bh-bg-neutral-medium`, `--bh-bg-neutral-soft`, `--bh-bg-neutral-subtle`, `--bh-bg-raised`, `--bh-bg-success-default`, `--bh-bg-success-subtle`, `--bh-bg-warning-default`, `--bh-bg-warning-subtle`, `--bh-border-brand-strong`, `--bh-border-default`, `--bh-border-focus`, `--bh-border-subtle`, `--bh-content-accent-amber-strong`, `--bh-content-accent-fuchsia-strong`, `--bh-content-brand-default`, `--bh-content-danger-strong`, `--bh-content-default`, `--bh-content-muted`, `--bh-content-on-brand`, `--bh-content-subtle`, `--bh-content-success-default`, `--bh-content-warning-strong`, `--bh-control-default`, `--bh-duration-fast`, `--bh-easing-standard`, `--bh-focus-ring-width`, `--bh-font-family`, `--bh-font-weight-semibold`, `--bh-icon-size-default`, `--bh-icon-slot-size-default`, `--bh-icon-stroke-190`, `--bh-icon-stroke-225`, `--bh-input-width`, `--bh-interactive-ghost-hover`, `--bh-opacity-0`, `--bh-opacity-100`, `--bh-opacity-muted`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-md-6`, `--bh-radius-sm-4`, `--bh-space-12xl-128`, `--bh-space-16xl-256`, `--bh-space-19xl-384`, `--bh-space-2xl-14`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-8xl-48`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-space-xxs-2`, `--bh-space-xxxs-1`, `--bh-table-min-width`, `--bh-table-selection-column-min-width`, `--bh-text-base-letter-spacing`, `--bh-text-body-2xs-regular-font-size`, `--bh-text-body-2xs-regular-line-height`, `--bh-text-body-3xs-regular-font-size`, `--bh-text-body-3xs-regular-line-height`, `--bh-text-body-lg-semibold-font-size`, `--bh-text-body-lg-semibold-font-weight`, `--bh-text-body-lg-semibold-line-height`, `--bh-text-body-md-semibold-font-size`, `--bh-text-body-md-semibold-font-weight`, `--bh-text-body-md-semibold-line-height`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-regular-font-size`, `--bh-text-body-sm-regular-font-weight`, `--bh-text-body-sm-regular-line-height`, `--bh-text-body-xs-regular-font-size`, `--
|
|
510
|
+
| `tokens` | `--bh-avatar-fallback-font-size-md`, `--bh-avatar-fallback-font-size-xs`, `--bh-avatar-size-md`, `--bh-avatar-size-xs`, `--bh-bg-accent-amber-subtle`, `--bh-bg-accent-fuchsia-subtle`, `--bh-bg-accent-golden-default`, `--bh-bg-brand-default`, `--bh-bg-brand-soft`, `--bh-bg-danger-subtle`, `--bh-bg-default`, `--bh-bg-muted`, `--bh-bg-neutral-medium`, `--bh-bg-neutral-soft`, `--bh-bg-neutral-subtle`, `--bh-bg-raised`, `--bh-bg-success-default`, `--bh-bg-success-subtle`, `--bh-bg-warning-default`, `--bh-bg-warning-subtle`, `--bh-border-brand-strong`, `--bh-border-default`, `--bh-border-focus`, `--bh-border-subtle`, `--bh-border-width-default`, `--bh-content-accent-amber-strong`, `--bh-content-accent-fuchsia-strong`, `--bh-content-brand-default`, `--bh-content-danger-strong`, `--bh-content-default`, `--bh-content-muted`, `--bh-content-on-brand`, `--bh-content-subtle`, `--bh-content-success-default`, `--bh-content-warning-strong`, `--bh-control-default`, `--bh-duration-fast`, `--bh-easing-standard`, `--bh-focus-ring-width`, `--bh-font-family`, `--bh-font-weight-medium`, `--bh-font-weight-regular`, `--bh-font-weight-semibold`, `--bh-icon-size-default`, `--bh-icon-slot-size-default`, `--bh-icon-stroke-190`, `--bh-icon-stroke-225`, `--bh-input-width`, `--bh-interactive-ghost-hover`, `--bh-opacity-0`, `--bh-opacity-100`, `--bh-opacity-muted`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-md-6`, `--bh-radius-sm-4`, `--bh-space-12xl-128`, `--bh-space-16xl-256`, `--bh-space-19xl-384`, `--bh-space-2xl-14`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-8xl-48`, `--bh-space-md-8`, `--bh-space-none`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-space-xxs-2`, `--bh-space-xxxs-1`, `--bh-table-caption-content`, `--bh-table-cell-text-gap`, `--bh-table-comfortable-cell-content`, `--bh-table-comfortable-cell-padding-x`, `--bh-table-comfortable-header-bg`, `--bh-table-comfortable-header-content`, `--bh-table-comfortable-header-height`, `--bh-table-comfortable-min-width`, `--bh-table-comfortable-radius`, `--bh-table-comfortable-row-height`, `--bh-table-container-bg`, `--bh-table-container-border`, `--bh-table-container-radius`, `--bh-table-min-width`, `--bh-table-selection-column-min-width`, `--bh-text-base-letter-spacing`, `--bh-text-body-2xs-regular-font-size`, `--bh-text-body-2xs-regular-font-weight`, `--bh-text-body-2xs-regular-letter-spacing`, `--bh-text-body-2xs-regular-line-height`, `--bh-text-body-3xs-regular-font-size`, `--bh-text-body-3xs-regular-line-height`, `--bh-text-body-lg-semibold-font-size`, `--bh-text-body-lg-semibold-font-weight`, `--bh-text-body-lg-semibold-line-height`, `--bh-text-body-md-regular-font-size`, `--bh-text-body-md-regular-font-weight`, `--bh-text-body-md-regular-letter-spacing`, `--bh-text-body-md-regular-line-height`, `--bh-text-body-md-semibold-font-size`, `--bh-text-body-md-semibold-font-weight`, `--bh-text-body-md-semibold-line-height`, `--bh-text-body-sm-medium-font-size`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-medium-letter-spacing`, `--bh-text-body-sm-medium-line-height`, `--bh-text-body-sm-regular-font-size`, `--bh-text-body-sm-regular-font-weight`, `--bh-text-body-sm-regular-line-height`, `--bh-text-body-xs-regular-font-size`, `--elevation-component-focus-ring` |
|
|
429
511
|
<!-- registry-contract:end -->
|
|
@@ -15,18 +15,37 @@ Tabs is a Radix-backed local navigation primitive with underline, segmented, and
|
|
|
15
15
|
| Prop | Values |
|
|
16
16
|
| --- | --- |
|
|
17
17
|
| `variant` | `underline`, `segment`, `rounded` |
|
|
18
|
-
| `size` | `md`, `sm`, `xs` |
|
|
19
|
-
| `fullWidth` | `boolean` |
|
|
18
|
+
| `size` | `md`, `sm`, `xs`, `comfortable` |
|
|
19
|
+
| `fullWidth` | `boolean`; default `false`. Equal shares while labels fit; content-safe scrolling under pressure. |
|
|
20
20
|
| `activeIndex`, `defaultActiveIndex` | controlled or uncontrolled index |
|
|
21
21
|
| `onActiveIndexChange` | selected-index callback |
|
|
22
22
|
|
|
23
|
+
### Comfortable size
|
|
24
|
+
|
|
25
|
+
`size="comfortable"` is the view navigation in an app page: the `View navigation` row in the ATS360 product frames.
|
|
26
|
+
|
|
27
|
+
- Underline tabs are `48px` tall with a `36px` content row and `2px` label padding.
|
|
28
|
+
- Tabs sit `8px` apart, with no divider under the row.
|
|
29
|
+
- The active tab's `2px` brand indicator is an inset shadow, so the label stays centred in the full tab height.
|
|
30
|
+
- Active labels are body-md medium in `content/default`; the others are body-md regular in `content/subtle`.
|
|
31
|
+
|
|
32
|
+
The library `Tabs` frame keeps `16px` gaps and a `1px` divider; the other sizes still follow it. Segment and rounded tabs render `comfortable` like `md`.
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<Tabs ariaLabel="Requisition views" items={["All requests", "Needs my approval", "Drafts"]} size="comfortable" />
|
|
36
|
+
```
|
|
37
|
+
|
|
23
38
|
## Token Contract
|
|
24
39
|
|
|
25
40
|
Tabs uses Banhaten tab CSS variables for sizes, spacing, border, radius, active indicators, surfaces, and content states.
|
|
26
41
|
|
|
27
42
|
## RTL
|
|
28
43
|
|
|
29
|
-
Tabs accepts `dir="rtl"` and mirrors layout through inherited direction.
|
|
44
|
+
Tabs accepts `dir="rtl"` and mirrors layout through inherited direction. Trigger labels stay complete in both directions: content-hug tabs keep their intrinsic width, while full-width tabs keep equal shares until a label's intrinsic width requires horizontal overflow.
|
|
45
|
+
|
|
46
|
+
## Overflow
|
|
47
|
+
|
|
48
|
+
Tab labels stay on one line and are never painted outside their trigger. When all triggers cannot fit, the tab list owns horizontal scrolling. `fullWidth` changes how spare inline space is distributed; it does not enable truncation or unsafe shrinking.
|
|
30
49
|
|
|
31
50
|
## Accessibility
|
|
32
51
|
|
|
@@ -62,8 +81,8 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
62
81
|
| `supportedExports` | `components/tabs.tsx: Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger` |
|
|
63
82
|
| `parts` | `Tabs`, `TabsRoot`, `TabsList`, `TabsTrigger`, `TabsContent` |
|
|
64
83
|
| `registryDependencies` | None |
|
|
65
|
-
| `features` | `Radix keyboard behavior`, `controlled active index`, `controlled value`, `optional panels`, `full-width tabs` |
|
|
66
|
-
| `sizes` | `md`, `sm`, `xs` |
|
|
67
|
-
| `tokens` | `--bh-bg-default`, `--bh-bg-neutral-subtle`, `--bh-border-default`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-subtle`, `--bh-font-family`, `--bh-font-weight-medium`, `--bh-opacity-60`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-md-6`, `--bh-space-3xl-16`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-8xl-48`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-space-xxs-2`, `--bh-space-xxxs-1`, `--bh-text-body-md-regular-font-size`, `--bh-text-body-md-regular-font-weight`, `--bh-text-body-md-regular-letter-spacing`, `--bh-text-body-md-regular-line-height`, `--
|
|
84
|
+
| `features` | `Radix keyboard behavior`, `controlled active index`, `controlled value`, `optional panels`, `content-hug tabs`, `content-safe full-width tabs`, `horizontal overflow without label overlap`, `comfortable view-navigation size: 48px underline tabs 8px apart, inset indicator, no divider` |
|
|
85
|
+
| `sizes` | `md`, `sm`, `xs`, `comfortable` |
|
|
86
|
+
| `tokens` | `--bh-bg-default`, `--bh-bg-neutral-subtle`, `--bh-border-default`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-subtle`, `--bh-font-family`, `--bh-font-weight-medium`, `--bh-opacity-60`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-md-6`, `--bh-space-3xl-16`, `--bh-space-6xl-32`, `--bh-space-7xl-40`, `--bh-space-8xl-48`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-space-xxs-2`, `--bh-space-xxxs-1`, `--bh-text-body-md-regular-font-size`, `--bh-text-body-md-regular-font-weight`, `--bh-text-body-md-regular-letter-spacing`, `--bh-text-body-md-regular-line-height`, `--elevation-button-soft` |
|
|
68
87
|
| `variants` | `underline`, `segment`, `rounded` |
|
|
69
88
|
<!-- registry-contract:end -->
|
|
@@ -90,6 +90,6 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
90
90
|
| `registryDependencies` | None |
|
|
91
91
|
| `sizes` | `xs`, `md`, `lg` |
|
|
92
92
|
| `states` | `default`, `active`, `disabled` |
|
|
93
|
-
| `tokens` | `--bh-bg-accent-green-default`, `--bh-bg-always-dark`, `--bh-bg-always-white`, `--bh-bg-brand-subtle`, `--bh-bg-danger-default`, `--bh-bg-neutral-bold`, `--bh-bg-neutral-subtle`, `--bh-border-brand-strong`, `--bh-border-default`, `--bh-border-disabled`, `--bh-border-focus`, `--bh-border-subtle`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-muted`, `--bh-content-on-neutral`, `--bh-control-default`, `--bh-control-md`, `--bh-icon-size-default`, `--bh-icon-slot-size-default`, `--bh-icon-stroke-200`, `--bh-icon-stroke-225`, `--bh-interactive-ghost-hover`, `--bh-interactive-secondary-default`, `--bh-interactive-secondary-disabled`, `--bh-interactive-secondary-hover`, `--bh-opacity-30`, `--bh-radius-full`, `--bh-radius-xs-2`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-none`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-tag-active-border-width`, `--bh-tag-focus-ring-inset`, `--bh-tag-focus-ring-width`, `--bh-text-body-3xs-semibold-font-size`, `--bh-text-body-3xs-semibold-font-weight`, `--bh-text-body-3xs-semibold-line-height`, `--bh-text-body-md-medium-font-size`, `--bh-text-body-md-medium-font-weight`, `--bh-text-body-md-medium-letter-spacing`, `--bh-text-body-md-medium-line-height`, `--bh-text-body-sm-medium-font-size`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-medium-letter-spacing`, `--bh-text-body-sm-medium-line-height`, `--bh-text-body-xs-medium-font-size`, `--bh-text-body-xs-medium-font-weight`, `--bh-text-body-xs-medium-letter-spacing`, `--bh-text-body-xs-medium-line-height`, `--bh-z-raised`, `--
|
|
93
|
+
| `tokens` | `--bh-bg-accent-green-default`, `--bh-bg-always-dark`, `--bh-bg-always-white`, `--bh-bg-brand-subtle`, `--bh-bg-danger-default`, `--bh-bg-neutral-bold`, `--bh-bg-neutral-subtle`, `--bh-border-brand-strong`, `--bh-border-default`, `--bh-border-disabled`, `--bh-border-focus`, `--bh-border-subtle`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-muted`, `--bh-content-on-neutral`, `--bh-control-default`, `--bh-control-md`, `--bh-icon-size-default`, `--bh-icon-slot-size-default`, `--bh-icon-stroke-200`, `--bh-icon-stroke-225`, `--bh-interactive-ghost-hover`, `--bh-interactive-secondary-default`, `--bh-interactive-secondary-disabled`, `--bh-interactive-secondary-hover`, `--bh-opacity-30`, `--bh-radius-full`, `--bh-radius-xs-2`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-none`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-tag-active-border-width`, `--bh-tag-focus-ring-inset`, `--bh-tag-focus-ring-width`, `--bh-text-body-3xs-semibold-font-size`, `--bh-text-body-3xs-semibold-font-weight`, `--bh-text-body-3xs-semibold-line-height`, `--bh-text-body-md-medium-font-size`, `--bh-text-body-md-medium-font-weight`, `--bh-text-body-md-medium-letter-spacing`, `--bh-text-body-md-medium-line-height`, `--bh-text-body-sm-medium-font-size`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-medium-letter-spacing`, `--bh-text-body-sm-medium-line-height`, `--bh-text-body-xs-medium-font-size`, `--bh-text-body-xs-medium-font-weight`, `--bh-text-body-xs-medium-letter-spacing`, `--bh-text-body-xs-medium-line-height`, `--bh-z-raised`, `--elevation-button-focus` |
|
|
94
94
|
| `types` | `simple`, `dot`, `flag`, `avatar`, `icon` |
|
|
95
95
|
<!-- registry-contract:end -->
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Task List
|
|
2
|
+
|
|
3
|
+
`TaskList` is an agent progress tracker. Rows move from a dashed pending circle to an arrow while active (with a shimmering label) to a check when done. The header icon crossfades from a list to a determinate pie to a filled check, and the counter rolls its digits as work completes through [Rolling Count](./rolling-count.md).
|
|
4
|
+
|
|
5
|
+
For any other list, compose the parts instead of configuring the tracker: `TaskListRoot` draws the surface, `TaskListHeader` is a static header, `TaskListItems` renders rows, and `TaskListPreview` shows the first rows and folds the rest. [Approval Card](./approval-card.md) builds its plan this way.
|
|
6
|
+
|
|
7
|
+
## API
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import {
|
|
11
|
+
TaskList,
|
|
12
|
+
TaskListHeader,
|
|
13
|
+
TaskListPreview,
|
|
14
|
+
TaskListRoot,
|
|
15
|
+
type TaskListItem,
|
|
16
|
+
} from "@/components/ui/task-list"
|
|
17
|
+
|
|
18
|
+
const items: TaskListItem[] = [
|
|
19
|
+
{ id: "1", label: "Scaffold the project structure", status: "done" },
|
|
20
|
+
{ id: "2", label: "Build the component registry", status: "active" },
|
|
21
|
+
{ id: "3", label: "Wire up checkout", status: "pending" },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
<TaskList items={items} />
|
|
25
|
+
|
|
26
|
+
<TaskListRoot surface="well">
|
|
27
|
+
<TaskListHeader count={items.length}>To-dos</TaskListHeader>
|
|
28
|
+
<TaskListPreview items={items} visibleCount={2} />
|
|
29
|
+
</TaskListRoot>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### TaskList
|
|
33
|
+
|
|
34
|
+
| Prop | Type | Default | Description |
|
|
35
|
+
| --- | --- | --- | --- |
|
|
36
|
+
| `items` | `readonly TaskListItem[]` | – | Rows with `id`, `label`, and `pending`, `active`, or `done` status. |
|
|
37
|
+
| `title` | `React.ReactNode` | `"To-dos"` | Header label. |
|
|
38
|
+
| `surface` | `"card" \| "well" \| "plain"` | `"card"` | Raised card, subtle well for embedding, or no surface. |
|
|
39
|
+
| `open` / `defaultOpen` / `onOpenChange` | `boolean` | `true` | Fold state. |
|
|
40
|
+
| `labels` | `Partial<TaskListLabels>` | English | `count(done, total)` copy, which the toggle reads after its title. |
|
|
41
|
+
|
|
42
|
+
### Parts
|
|
43
|
+
|
|
44
|
+
| Part | Props | Description |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `TaskListRoot` | `surface` | Surface of a composed list. A header inside it names the rows. |
|
|
47
|
+
| `TaskListHeader` | `children`, `count`, `items` | Static header. `items` drives the icon from list to progress pie to check; `count` renders at the end. |
|
|
48
|
+
| `TaskListItems` | `items` | Rows. They start flush when no header sits above them. |
|
|
49
|
+
| `TaskListPreview` | `items`, `visibleCount` (`3`), `labels` | The first rows, then a Collapsible fold for the rest with `more(count)` and `less` copy. |
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx banhaten add task-list
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Examples
|
|
58
|
+
|
|
59
|
+
Primary registry example: `examples/task-list-demo.tsx`.
|
|
60
|
+
|
|
61
|
+
## Token Contract
|
|
62
|
+
|
|
63
|
+
Surfaces use `--bh-bg-raised` with `--elevation-02` and `--bh-radius-2xl-12` (card) or `--bh-bg-subtle` with `--bh-radius-lg-8` (well). Rows use the `body-xs` scale, `--bh-task-list-icon-size` icons, `--bh-content-subtle` for muted rows, `--bh-content-default` for the active row, and `--bh-content-success-default` for the finished check. Motion uses the `--bh-duration-*` foundation tokens, rows enter `--bh-task-list-stagger` apart, and the active row uses the shared shimmer.
|
|
64
|
+
|
|
65
|
+
Covered source files: `components/task-list.tsx`, `components/task-list.css`, and the `collapsible`, `rolling-count`, and `shimmer-text` registry dependencies.
|
|
66
|
+
|
|
67
|
+
## RTL Rules
|
|
68
|
+
|
|
69
|
+
- Rows, counters, and the collapse chevron use logical placement.
|
|
70
|
+
- The active arrow icon mirrors horizontally in RTL; the check and dashed circle are symmetric.
|
|
71
|
+
|
|
72
|
+
## Accessibility
|
|
73
|
+
|
|
74
|
+
The collapsible header and the "more" control are Collapsible triggers with `aria-expanded` and `aria-controls`, named by what they show: the header by its title and progress, for example "To-dos 2 of 5 done". Folded rows unmount after their closing animation, so nothing hidden can be focused. Rows are labelled by the header title of their list, including a `TaskListHeader` composed inside `TaskListRoot`. Status changes are expressed through icons plus strikethrough, never color alone, and the counter carries an accessible "done of total" summary. Rolling digits, crossfades, and shimmer collapse to instant changes under `prefers-reduced-motion`.
|
|
75
|
+
|
|
76
|
+
<!-- registry-contract:start -->
|
|
77
|
+
## Registry Contract
|
|
78
|
+
|
|
79
|
+
This table is generated from `registry/index.json`; edit the component-owned source under `registry/catalog/components/`, not this table or the compiled index.
|
|
80
|
+
`supportedExports` is the exhaustive runtime import contract grouped by source module. `parts` is an ordered, non-exhaustive anatomy list.
|
|
81
|
+
|
|
82
|
+
| Field | Values |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `supportedExports` | `components/task-list.tsx: TaskList, TaskListHeader, TaskListItems, TaskListPreview, TaskListRoot` |
|
|
85
|
+
| `parts` | `TaskList`, `TaskListRoot`, `TaskListHeader`, `TaskListItems`, `TaskListPreview` |
|
|
86
|
+
| `registryDependencies` | `collapsible`, `rolling-count`, `shimmer-text` |
|
|
87
|
+
| `props.items` | `readonly TaskListItem[]; default null; Rows with id, label, and pending, active, or done status.` |
|
|
88
|
+
| `props.surface` | `"card" \| "well" \| "plain"; default "card"; Raised card, subtle well for embedding, or no surface.` |
|
|
89
|
+
| `props.labels` | `Partial<TaskListLabels>; default "English"; Localized counter copy, read after the title in the toggle's name.` |
|
|
90
|
+
| `props.visibleCount` | `number; default "3"; TaskListPreview: rows shown before the rest fold behind a more control.` |
|
|
91
|
+
| `features` | `controlled state`, `uncontrolled state`, `composable parts`, `preview fold`, `reduced motion`, `RTL` |
|
|
92
|
+
| `states` | `pending`, `active`, `done` |
|
|
93
|
+
| `tokens` | `--bh-bg-raised`, `--bh-bg-subtle`, `--bh-content-default`, `--bh-content-subtle`, `--bh-content-success-default`, `--bh-duration-default`, `--bh-duration-fast`, `--bh-duration-moderate`, `--bh-duration-slow`, `--bh-easing-standard`, `--bh-font-family`, `--bh-icon-stroke-180`, `--bh-icon-stroke-200`, `--bh-opacity-0`, `--bh-opacity-100`, `--bh-radius-2xl-12`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-sm-4`, `--bh-space-4xl-20`, `--bh-space-lg-10`, `--bh-space-md-8`, `--bh-space-sm-6`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--bh-space-xxs-2`, `--bh-space-xxxs-1`, `--bh-task-list-icon-size`, `--bh-task-list-stagger`, `--bh-text-body-xs-medium-font-weight`, `--bh-text-body-xs-regular-font-size`, `--bh-text-body-xs-regular-font-weight`, `--bh-text-body-xs-regular-letter-spacing`, `--bh-text-body-xs-regular-line-height`, `--elevation-02`, `--elevation-component-focus-ring` |
|
|
94
|
+
| `variants` | `card`, `well`, `plain` |
|
|
95
|
+
<!-- registry-contract:end -->
|
|
@@ -60,5 +60,5 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
60
60
|
| `registryDependencies` | None |
|
|
61
61
|
| `modes` | `default`, `tags` |
|
|
62
62
|
| `states` | `placeholder`, `filled`, `disabled` |
|
|
63
|
-
| `tokens` | `--bh-bg-accent-blue-soft`, `--bh-border-danger-strong`, `--bh-border-default`, `--bh-border-disabled`, `--bh-border-focus`, `--bh-border-input`, `--bh-border-subtle`, `--bh-border-width-default`, `--bh-content-accent-blue-strong`, `--bh-content-danger-default`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-muted`, `--bh-content-subtle`, `--bh-control-md`, `--bh-font-weight-semibold`, `--bh-icon-stroke-225`, `--bh-icon-stroke-250`, `--bh-interactive-input-default`, `--bh-interactive-input-disabled`, `--bh-interactive-secondary-default`, `--bh-interactive-secondary-disabled`, `--bh-opacity-0`, `--bh-opacity-30`, `--bh-opacity-50`, `--bh-radius-full`, `--bh-space-xs-4`, `--bh-text-body-md-regular-font-size`, `--bh-text-body-md-regular-font-weight`, `--bh-text-body-md-regular-letter-spacing`, `--bh-text-body-md-regular-line-height`, `--bh-text-body-sm-medium-font-size`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-medium-letter-spacing`, `--bh-text-body-sm-medium-line-height`, `--bh-text-body-xs-medium-font-size`, `--bh-text-body-xs-medium-font-weight`, `--bh-text-body-xs-medium-letter-spacing`, `--bh-text-body-xs-medium-line-height`, `--bh-text-body-xs-regular-font-size`, `--bh-text-body-xs-regular-font-weight`, `--bh-text-body-xs-regular-letter-spacing`, `--bh-text-body-xs-regular-line-height`, `--bh-textarea-border`, `--bh-textarea-close-button-size`, `--bh-textarea-close-icon-size`, `--bh-textarea-focus-ring-inset`, `--bh-textarea-focus-ring-width`, `--bh-textarea-gap`, `--bh-textarea-helper-icon-size`, `--bh-textarea-icon-size`, `--bh-textarea-inline-gap`, `--bh-textarea-min-height`, `--bh-textarea-padding`, `--bh-textarea-radius`, `--bh-textarea-tag-avatar-font-size`, `--bh-textarea-tag-avatar-size`, `--bh-textarea-tag-height`, `--bh-textarea-width`, `--
|
|
63
|
+
| `tokens` | `--bh-bg-accent-blue-soft`, `--bh-border-danger-strong`, `--bh-border-default`, `--bh-border-disabled`, `--bh-border-focus`, `--bh-border-input`, `--bh-border-subtle`, `--bh-border-width-default`, `--bh-content-accent-blue-strong`, `--bh-content-danger-default`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-muted`, `--bh-content-subtle`, `--bh-control-md`, `--bh-font-weight-semibold`, `--bh-icon-stroke-225`, `--bh-icon-stroke-250`, `--bh-interactive-input-default`, `--bh-interactive-input-disabled`, `--bh-interactive-secondary-default`, `--bh-interactive-secondary-disabled`, `--bh-opacity-0`, `--bh-opacity-30`, `--bh-opacity-50`, `--bh-radius-full`, `--bh-space-xs-4`, `--bh-text-body-md-regular-font-size`, `--bh-text-body-md-regular-font-weight`, `--bh-text-body-md-regular-letter-spacing`, `--bh-text-body-md-regular-line-height`, `--bh-text-body-sm-medium-font-size`, `--bh-text-body-sm-medium-font-weight`, `--bh-text-body-sm-medium-letter-spacing`, `--bh-text-body-sm-medium-line-height`, `--bh-text-body-xs-medium-font-size`, `--bh-text-body-xs-medium-font-weight`, `--bh-text-body-xs-medium-letter-spacing`, `--bh-text-body-xs-medium-line-height`, `--bh-text-body-xs-regular-font-size`, `--bh-text-body-xs-regular-font-weight`, `--bh-text-body-xs-regular-letter-spacing`, `--bh-text-body-xs-regular-line-height`, `--bh-textarea-border`, `--bh-textarea-close-button-size`, `--bh-textarea-close-icon-size`, `--bh-textarea-focus-ring-inset`, `--bh-textarea-focus-ring-width`, `--bh-textarea-gap`, `--bh-textarea-helper-icon-size`, `--bh-textarea-icon-size`, `--bh-textarea-inline-gap`, `--bh-textarea-min-height`, `--bh-textarea-padding`, `--bh-textarea-radius`, `--bh-textarea-tag-avatar-font-size`, `--bh-textarea-tag-avatar-size`, `--bh-textarea-tag-height`, `--bh-textarea-width`, `--elevation-component-default`, `--elevation-textarea` |
|
|
64
64
|
<!-- registry-contract:end -->
|