banhaten 0.2.0 → 0.4.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 (120) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +5 -5
  3. package/docs/design-system/component-implementation-ownership-audit.md +245 -0
  4. package/docs/design-system/component-quality-audit-2026-08-20.md +225 -0
  5. package/docs/design-system/component-showcase-consistency-report.md +2 -0
  6. package/docs/design-system/components/README.md +1 -0
  7. package/docs/design-system/components/activity-feed.md +2 -0
  8. package/docs/design-system/components/activity-heatmap.md +73 -0
  9. package/docs/design-system/components/alert-dialog.md +1 -1
  10. package/docs/design-system/components/alert.md +3 -3
  11. package/docs/design-system/components/attribute.md +1 -1
  12. package/docs/design-system/components/avatar.md +2 -2
  13. package/docs/design-system/components/badge.md +1 -1
  14. package/docs/design-system/components/banner.md +6 -2
  15. package/docs/design-system/components/breadcrumbs.md +5 -2
  16. package/docs/design-system/components/button-group.md +3 -2
  17. package/docs/design-system/components/button.md +2 -2
  18. package/docs/design-system/components/card.md +11 -3
  19. package/docs/design-system/components/catalog-components.md +3 -0
  20. package/docs/design-system/components/date-picker.md +3 -1
  21. package/docs/design-system/components/file-upload.md +5 -1
  22. package/docs/design-system/components/input.md +8 -6
  23. package/docs/design-system/components/item.md +3 -1
  24. package/docs/design-system/components/kbd.md +11 -8
  25. package/docs/design-system/components/menu.md +2 -2
  26. package/docs/design-system/components/modal.md +1 -1
  27. package/docs/design-system/components/navigation-menu.md +1 -1
  28. package/docs/design-system/components/onboarding-step-list-item.md +3 -2
  29. package/docs/design-system/components/page-header.md +3 -1
  30. package/docs/design-system/components/pagination.md +3 -2
  31. package/docs/design-system/components/radio-card.md +1 -1
  32. package/docs/design-system/components/resizable.md +1 -1
  33. package/docs/design-system/components/scroll-area.md +7 -3
  34. package/docs/design-system/components/select.md +1 -1
  35. package/docs/design-system/components/sidebar.md +60 -18
  36. package/docs/design-system/components/slideout.md +4 -1
  37. package/docs/design-system/components/steps.md +7 -2
  38. package/docs/design-system/components/table.md +25 -7
  39. package/docs/design-system/components/tag.md +3 -3
  40. package/docs/design-system/components/timeline.md +4 -1
  41. package/docs/design-system/components/toggle.md +1 -1
  42. package/docs/design-system/components/toolbar.md +1 -1
  43. package/docs/design-system/components/tooltip.md +1 -1
  44. package/docs/design-system/density-and-elevation.md +23 -2
  45. package/docs/design-system/gap-promotion-report.md +1 -67
  46. package/docs/design-system/icons.md +6 -1
  47. package/docs/design-system/installation.md +1 -1
  48. package/docs/design-system/launch-comparison-shadcn.md +10 -10
  49. package/docs/design-system/token-governance.md +7 -8
  50. package/package.json +3 -2
  51. package/registry/components/alert-dialog.tsx +1 -1
  52. package/registry/components/alert.tsx +17 -13
  53. package/registry/components/attribute.tsx +8 -2
  54. package/registry/components/avatar.tsx +28 -20
  55. package/registry/components/badge.tsx +17 -17
  56. package/registry/components/button-group.tsx +4 -2
  57. package/registry/components/button.tsx +3 -3
  58. package/registry/components/card.tsx +19 -3
  59. package/registry/components/carousel.tsx +14 -0
  60. package/registry/components/command.tsx +37 -3
  61. package/registry/components/date-picker.tsx +83 -35
  62. package/registry/components/expanded/ActivityFeed.tsx +3 -7
  63. package/registry/components/expanded/ActivityHeatmap.tsx +64 -0
  64. package/registry/components/expanded/Banner.tsx +0 -1
  65. package/registry/components/expanded/Breadcrumbs.tsx +3 -0
  66. package/registry/components/expanded/CatalogComponentsShowcase.tsx +3 -4
  67. package/registry/components/expanded/CatalogDivider.tsx +3 -2
  68. package/registry/components/expanded/FileUpload.tsx +7 -5
  69. package/registry/components/expanded/OnboardingStepListItem.tsx +6 -7
  70. package/registry/components/expanded/PageHeader.tsx +35 -44
  71. package/registry/components/expanded/Slideout.tsx +18 -13
  72. package/registry/components/expanded/Steps.tsx +147 -9
  73. package/registry/components/expanded/Table.tsx +50 -18
  74. package/registry/components/expanded/Timeline.tsx +11 -24
  75. package/registry/components/expanded/activityFeed.css +8 -8
  76. package/registry/components/expanded/activityHeatmap.css +66 -0
  77. package/registry/components/expanded/banner.css +30 -15
  78. package/registry/components/expanded/breadcrumbs.css +5 -5
  79. package/registry/components/expanded/fileUpload.css +4 -4
  80. package/registry/components/expanded/onboardingStepListItem.css +16 -7
  81. package/registry/components/expanded/pageHeader.css +14 -11
  82. package/registry/components/expanded/slideout.css +3 -7
  83. package/registry/components/expanded/steps.css +75 -12
  84. package/registry/components/expanded/table.css +63 -69
  85. package/registry/components/expanded/timeline.css +17 -16
  86. package/registry/components/field.tsx +61 -8
  87. package/registry/components/input.tsx +12 -5
  88. package/registry/components/item.tsx +1 -1
  89. package/registry/components/kbd.tsx +109 -15
  90. package/registry/components/menu.tsx +57 -7
  91. package/registry/components/modal.tsx +1 -1
  92. package/registry/components/navigation-menu.tsx +1 -1
  93. package/registry/components/pagination.css +36 -0
  94. package/registry/components/pagination.tsx +20 -13
  95. package/registry/components/progress-slider.tsx +1 -1
  96. package/registry/components/radio-card.tsx +1 -2
  97. package/registry/components/resizable.tsx +36 -2
  98. package/registry/components/scroll-area.tsx +30 -6
  99. package/registry/components/select.tsx +35 -12
  100. package/registry/components/sidebar.tsx +940 -62
  101. package/registry/components/social-button.tsx +12 -5
  102. package/registry/components/table-elements.tsx +128 -0
  103. package/registry/components/tabs.tsx +5 -10
  104. package/registry/components/tag.tsx +2 -2
  105. package/registry/components/toggle.tsx +2 -2
  106. package/registry/components/toolbar.tsx +6 -6
  107. package/registry/components/tooltip.tsx +1 -1
  108. package/registry/examples/attribute-demo.tsx +1 -1
  109. package/registry/examples/avatar-demo.tsx +7 -4
  110. package/registry/examples/command-palette-demo.tsx +0 -1
  111. package/registry/examples/expanded/activity-heatmap-demo.tsx +29 -0
  112. package/registry/examples/expanded/breadcrumbs-demo.tsx +3 -3
  113. package/registry/examples/kbd-demo.tsx +2 -1
  114. package/registry/examples/pagination-demo.tsx +5 -5
  115. package/registry/examples/sidebar-demo.tsx +59 -13
  116. package/registry/guidance/USAGE.md +1 -1
  117. package/registry/index.json +621 -60
  118. package/registry/styles/globals.css +211 -151
  119. package/registry.json +44 -6
  120. package/tokens/banhaten.tokens.json +325 -95
@@ -1,12 +1,12 @@
1
1
  # Launch Comparison: Banhaten and shadcn/ui
2
2
 
3
- Reviewed against the official shadcn/ui CLI, registry, theming, and RTL documentation on 2026-07-10. shadcn/ui is the distribution and documentation benchmark—not Banhaten’s visual source.
3
+ Benchmark references reviewed against the official shadcn/ui CLI, registry, theming, and RTL documentation on 2026-07-10; Banhaten implementation status refreshed on 2026-08-20. shadcn/ui is the distribution and documentation benchmark—not Banhaten’s visual source.
4
4
 
5
5
  ## Outcome
6
6
 
7
7
  Banhaten is a launch candidate with a distinct product contract: Arabic and English-first typography, inherited RTL by default, seven brand themes, three radius modes, stable primitives separated from larger Patterns, and a hard rule that distributable components consume canonical Banhaten tokens.
8
8
 
9
- The only intentionally unresolved visual exception is color contrast. The reviewed default-theme correction composes blue brand roles with the gray neutral ramp; the remaining source color pairs are documented rather than changed automatically. The Color Contrast page evaluates 742 theme/mode scenarios, reports 274 failures and 468 passes, and records the pair, score, threshold, state, swatches, recommendation, and best existing foreground candidate.
9
+ The former color-contrast exception is closed. The reviewed semantic-role corrections preserve the Banhaten palette while selecting valid foreground/background roles across themes and states. The Color Contrast page now evaluates 910 theme/mode scenarios and reports **910 passes and 0 failures**, with every pair, score, threshold, state, and swatch retained as inspectable evidence.
10
10
 
11
11
  ## Benchmark Summary
12
12
 
@@ -14,7 +14,7 @@ The only intentionally unresolved visual exception is color contrast. The review
14
14
  | --- | --- | --- | --- |
15
15
  | Distribution | Source registry and CLI installation | Canonical Banhaten registry plus generated shadcn-compatible `registry.json` | Verified |
16
16
  | Component ownership | Copied source is consumer-owned | Receipt-backed generated ownership; local drift blocks updates unless explicitly forced | Stronger safety |
17
- | Registry dependencies | Dependency closure and typed registry items | Dependency graph, schema validation, mirror checks, and isolated compilation of all 67 items | Verified |
17
+ | Registry dependencies | Dependency closure and typed registry items | Dependency graph, schema validation, mirror checks, and isolated compilation of all 68 items | Verified |
18
18
  | CLI | `init`, `add`, `view`, `docs`, migrations, presets | `init`, `add`, real `diff`, safe `update`, `list`, `search`, full `docs`, and `view` | Launch scope verified |
19
19
  | Framework setup | Multiple framework templates | Vite auto-configuration and framework-neutral/Next-style source installation | Verified |
20
20
  | Package managers | npm, pnpm, Yarn, Bun | Detection and install hints tested for all four | Verified |
@@ -23,7 +23,7 @@ The only intentionally unresolved visual exception is color contrast. The review
23
23
  | RTL | First-class RTL and install-time transformations | Authored logical layout, inherited direction hook/provider, Arabic font, explicit message catalogs, and RTL keyboard tests | Banhaten-specific |
24
24
  | Documentation | Live preview/code, component examples, API and CLI docs | Live registry previews, exact example source, extracted TypeScript props, token contract, accessibility contract, RTL, files, navigation/token search, and copy actions | Verified |
25
25
  | Components vs blocks | Components and larger blocks | Stable Components and broader Patterns; experiments and visual QA isolated under Lab | Verified |
26
- | Accessibility | Primitive semantics and per-component guidance | Native/Radix semantics, focus handling, dialog traps, tabs contract, reduced motion, forced colors, axe, Chromium/Firefox/WebKit tests | Verified except documented colors |
26
+ | Accessibility | Primitive semantics and per-component guidance | Native/Radix semantics, focus handling, dialog traps, tabs contract, reduced motion, forced colors, 910/910 contrast, strict route Axe, and Chromium/Firefox/WebKit tests | Verified |
27
27
  | Release governance | Changelog and frequent CLI/schema evolution | Changelog, security, contributing, support matrix, stability tiers, deprecation, release gate, and roadmap | Verified |
28
28
 
29
29
  ## Full Finding Register
@@ -35,18 +35,18 @@ The only intentionally unresolved visual exception is color contrast. The review
35
35
  - Repaired Command Bar search/clear/submit behavior, Table disabled/action behavior, File Upload progress semantics, Spinner reduced-motion behavior, and Date Picker presets/month-year interaction.
36
36
  - Standardized direction resolution for Alert, Pagination, Input OTP, Carousel, and Toast through explicit props, `DirectionProvider`, nearest `[dir]`, then document direction.
37
37
  - Corrected Arabic Pagination copy and kept product strings explicit rather than silently localizing demo text.
38
- - Added focused behavior tests; the component suite currently runs 171 tests.
38
+ - Added focused behavior tests; the component suite currently runs 205 tests.
39
39
 
40
40
  ### Colors and tokens
41
41
 
42
- - Preserved every requested source color value and moved all failing combinations into the generated Color Contrast report.
42
+ - Repaired semantic foreground/background role selection across component states while preserving the canonical palette; all 910 generated contrast combinations now pass.
43
43
  - Split system, typography, effects, component variables, dark effects, and
44
44
  scoped aliases into owned `tokens/source/` fragments that compile to
45
45
  `tokens/banhaten.tokens.json`.
46
46
  - Generated live `var(...)` alias graphs rather than flattened semantic values and verified custom-property closure.
47
47
  - Added named categorical chart semantics, spinner/skeleton/motion tokens, exact icon stroke tokens, Item tokens, default border/transition wiring, reduced motion, and forced-color focus rules.
48
- - Generated exact component token metadata from source. All 67 registry components have non-empty token contracts.
49
- - Deduplicated theme CSS against the default cascade without changing values, reducing generated CSS from 466,671 bytes to 154,130 bytes.
48
+ - Generated exact component token metadata from source. All 68 registry components have non-empty, current token contracts.
49
+ - Deduplicated theme CSS against the default cascade and kept generated CSS at 156,729 bytes after the accessibility and component-token additions.
50
50
  - Documented the no-global-density decision and the semantic elevation hierarchy.
51
51
 
52
52
  ### Usage and composition
@@ -70,7 +70,7 @@ The only intentionally unresolved visual exception is color contrast. The review
70
70
  ### Documentation and presentation
71
71
 
72
72
  - Removed launch-blocking `noindex`, added description/social metadata, dynamic page titles, skip link, release status/version, and responsive searchable navigation.
73
- - Reframed the overview around quick start, token rules, safe updates, and the documented contrast exception.
73
+ - Reframed the overview around quick start, token rules, safe updates, and the now-enforced contrast contract.
74
74
  - Added focus trap, Escape, initial focus, and focus return to Customize and Component Matrix dialogs.
75
75
  - Completed Preview/Code tab IDs, `aria-controls`, `aria-labelledby`, roving focus, and keyboard navigation.
76
76
  - Renamed ambiguous “Copy Page” to “Copy page link” and added clipboard fallback.
@@ -79,7 +79,7 @@ The only intentionally unresolved visual exception is color contrast. The review
79
79
  ### Verification and release
80
80
 
81
81
  - Browser checks cover Chromium, Firefox, and WebKit; visual baselines stay on Chromium for deterministic snapshots.
82
- - Automated checks cover LTR/RTL behavior, portals, focus return, docs navigation, mobile navigation, token copy/search, live theme overrides, the frozen contrast totals, reduced motion, forced colors, and serious/critical axe findings outside the intentionally documented color rule.
82
+ - Automated checks cover LTR/RTL behavior, portals, focus return, docs navigation, mobile navigation, token copy/search, live theme overrides, the 910/910 contrast totals, reduced motion, forced colors, and strict route-wide Axe findings without a color-rule exception.
83
83
  - Bundle budgets, schema validation, token generation, token-only styling, registry mirrors, documentation coverage, CLI integration, isolated installs, package audit, and tarball contents are release gates.
84
84
 
85
85
  ## Deliberate Differences, Not Missing Copies
@@ -29,14 +29,13 @@ that authored CSS source.
29
29
 
30
30
  The generator emits the complete default-theme graph once, then emits only declarations whose values differ for each alternate theme. Dark mode and alternate themes inherit unchanged scoped aliases through the cascade. This keeps the live graph auditable without duplicating identical declarations.
31
31
 
32
- The default appearance deliberately composes two concerns: `blue` owns brand
33
- roles and `gray` owns the neutral ramp. As a result, default product surfaces
34
- are achromatic, while links, focus states, and primary actions remain blue.
35
- `cssContract.defaultTheme` and `cssContract.defaultNeutralTheme` record that
36
- composition. `npm run tokens:build` synchronizes it into the authored default
37
- theme and semantic color-mode values before compiling the public artifacts.
38
- Alternate brand themes keep their reviewed neutral ramps unless their source is
39
- explicitly changed.
32
+ The default appearance uses `gray` for both brand roles and the neutral ramp.
33
+ As a result, product surfaces, links, focus states, and primary actions are
34
+ monochrome by default. `cssContract.defaultTheme` and
35
+ `cssContract.defaultNeutralTheme` record that composition. `npm run
36
+ tokens:build` synchronizes it into the authored default theme and semantic
37
+ color-mode values before compiling the public artifacts. Alternate brand themes
38
+ keep their reviewed neutral ramps unless their source is explicitly changed.
40
39
 
41
40
  Color values are design-owned. The generated Color Contrast page reports failing combinations and recommendations, but automation must not silently change source colors. A color change requires an explicit design review and regeneration of the contrast baseline.
42
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "banhaten",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Banhaten design system: an Arabic and English-first React design system with shadcn-style component installation.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "build": "npm --prefix playground ci --include=dev --include=optional && npm --prefix playground run build",
35
35
  "build:railway": "npm --prefix playground ci --include=dev --include=optional && npm --prefix playground run build:railway",
36
36
  "start": "node playground/scripts/serve-static.mjs playground/dist",
37
- "check": "node --check src/cli/index.js && node --check src/cli/command-line.js && node --check src/cli/doctor.js && node --check src/cli/config-editors.js && node --check src/cli/source-scanner.js && node --check src/cli/jsonc.js && node --check src/cli/vite-config.js && node --check src/cli/install-plan.js && node --check src/cli/install-receipt.js && node --check src/cli/package-install-state.js && node --check src/cli/package-manager.js && node --check src/cli/package-plan.js && node --check src/cli/package-version.js && node --check scripts/compile-token-catalog.mjs && node --check scripts/sync-default-theme-neutral-values.mjs && node --check scripts/token-catalog-test.mjs && node --check scripts/registry-catalog.mjs && node --check scripts/generate-registry-index.mjs && node --check scripts/generate-shadcn-registry.mjs && node --check scripts/normalize-component-docs.mjs && node --check scripts/package-artifact.mjs && node --check scripts/check-package-contents.mjs && node --check scripts/select-react-compatibility.mjs && npm run contracts:check && npm run cli:test && npm run tokens:check && npm run registry:check && npm run docs:check && npm run package:check && npm run playground:build && npm run playground:test:browser && npm run playground:audit",
37
+ "check": "node --check src/cli/index.js && node --check src/cli/command-line.js && node --check src/cli/doctor.js && node --check src/cli/config-editors.js && node --check src/cli/source-scanner.js && node --check src/cli/jsonc.js && node --check src/cli/vite-config.js && node --check src/cli/install-plan.js && node --check src/cli/install-receipt.js && node --check src/cli/package-install-state.js && node --check src/cli/package-manager.js && node --check src/cli/package-plan.js && node --check src/cli/package-version.js && node --check scripts/compile-token-catalog.mjs && node --check scripts/sync-default-theme-neutral-values.mjs && node --check scripts/token-catalog-test.mjs && node --check scripts/registry-catalog.mjs && node --check scripts/generate-registry-index.mjs && node --check scripts/generate-shadcn-registry.mjs && node --check scripts/normalize-component-docs.mjs && node --check scripts/package-artifact.mjs && node --check scripts/check-package-contents.mjs && node --check scripts/select-react-compatibility.mjs && npm run contracts:check && npm run cli:test && npm run tokens:check && npm run components:ownership:check && npm run registry:check && npm run docs:check && npm run package:check && npm run playground:build && npm run playground:test:browser && npm run playground:audit",
38
38
  "contracts:check": "node scripts/validate-contracts.mjs",
39
39
  "cli:test": "node scripts/cli-argument-test.mjs && node scripts/cli-dependency-test.mjs && node scripts/cli-install-plan-test.mjs && node scripts/cli-integration-test.mjs && node scripts/cli-doctor-test.mjs",
40
40
  "playground:build": "npm --prefix playground run build",
@@ -49,6 +49,7 @@
49
49
  "registry:exports": "node scripts/check-isolated-registry-installs.mjs --write-exports",
50
50
  "registry:sync": "node scripts/check-registry-mirrors.mjs --write",
51
51
  "components:tokens:check": "node scripts/check-component-token-usage.mjs",
52
+ "components:ownership:check": "npm --prefix playground run test:ownership",
52
53
  "components:tokens:metadata": "node scripts/sync-component-token-metadata.mjs",
53
54
  "docs:check": "node scripts/check-component-docs.mjs && node scripts/normalize-component-docs.mjs",
54
55
  "docs:normalize": "node scripts/normalize-component-docs.mjs --write",
@@ -16,7 +16,7 @@ const alertDialogContentVariants = cva(
16
16
  "fixed left-1/2 top-1/2 z-[var(--bh-z-overlay)] flex max-h-[calc(100vh-(var(--bh-space-8xl-48)*2))] w-[var(--bh-alert-dialog-width)]",
17
17
  "max-w-[calc(100vw-(var(--bh-space-4xl-20)*2))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden",
18
18
  "rounded-[var(--bh-radius-2xl-12)] bg-[var(--bh-bg-raised)] text-[var(--bh-content-default)] shadow-[var(--shadow-modal)] outline-none",
19
- "[--bh-alert-dialog-border:var(--bh-border-subtle)] [--shadow-modal:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-alert-dialog-border),var(--shadow-component-default)]",
19
+ "[--bh-alert-dialog-border:var(--bh-border-subtle)] [--shadow-modal:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-alert-dialog-border),var(--shadow-2xl)]",
20
20
  ],
21
21
  {
22
22
  variants: {
@@ -11,6 +11,7 @@ import {
11
11
 
12
12
  import { cn } from "@/lib/utils"
13
13
  import { useResolvedDirection } from "@/hooks/use-direction"
14
+ import { Button } from "@/components/ui/button"
14
15
 
15
16
  const alertVariants = cva(
16
17
  [
@@ -22,13 +23,13 @@ const alertVariants = cva(
22
23
  variants: {
23
24
  status: {
24
25
  danger:
25
- "[--alert-bg:var(--bh-bg-danger-subtle)] [--alert-border:var(--bh-border-danger-subtle)] [--alert-title:var(--bh-content-danger-strong)] [--alert-description:var(--bh-content-danger-default)] [--alert-icon:var(--bh-content-danger-default)]",
26
+ "[--alert-bg:var(--bh-bg-danger-subtle)] [--alert-border:var(--bh-border-danger-subtle)] [--alert-title:var(--bh-content-danger-strong)] [--alert-description:var(--bh-content-danger-strong)] [--alert-icon:var(--bh-content-danger-default)]",
26
27
  success:
27
- "[--alert-bg:var(--bh-bg-success-subtle)] [--alert-border:var(--bh-border-success-subtle)] [--alert-title:var(--bh-content-success-strong)] [--alert-description:var(--bh-content-success-default)] [--alert-icon:var(--bh-content-success-default)]",
28
+ "[--alert-bg:var(--bh-bg-success-subtle)] [--alert-border:var(--bh-border-success-subtle)] [--alert-title:var(--bh-content-success-strong)] [--alert-description:var(--bh-content-success-strong)] [--alert-icon:var(--bh-content-success-default)]",
28
29
  warning:
29
- "[--alert-bg:var(--bh-bg-warning-subtle)] [--alert-border:var(--bh-border-warning-subtle)] [--alert-title:var(--bh-content-warning-strong)] [--alert-description:var(--bh-content-warning-default)] [--alert-icon:var(--bh-content-warning-default)]",
30
+ "[--alert-bg:var(--bh-bg-warning-subtle)] [--alert-border:var(--bh-border-warning-subtle)] [--alert-title:var(--bh-content-warning-strong)] [--alert-description:var(--bh-content-warning-strong)] [--alert-icon:var(--bh-content-warning-default)]",
30
31
  info:
31
- "[--alert-bg:var(--bh-bg-info-subtle)] [--alert-border:var(--bh-border-info-subtle)] [--alert-title:var(--bh-content-brand-strong)] [--alert-description:var(--bh-content-brand-default)] [--alert-icon:var(--bh-content-brand-default)]",
32
+ "[--alert-bg:var(--bh-bg-info-subtle)] [--alert-border:var(--bh-border-info-subtle)] [--alert-title:var(--bh-content-brand-strong)] [--alert-description:var(--bh-content-brand-strong)] [--alert-icon:var(--bh-content-brand-default)]",
32
33
  neutral:
33
34
  "[--alert-bg:var(--bh-bg-neutral-subtle)] [--alert-border:var(--bh-border-subtle)] [--alert-title:var(--bh-content-default)] [--alert-description:var(--bh-content-default)] [--alert-icon:var(--bh-content-default)]",
34
35
  },
@@ -158,7 +159,7 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
158
159
  >
159
160
  <span
160
161
  data-slot="alert-icon"
161
- className="inline-flex size-[var(--bh-alert-icon-size)] shrink-0 items-center justify-center text-[var(--alert-icon)]"
162
+ className="inline-flex size-[var(--bh-alert-icon-slot-size)] shrink-0 items-center justify-center text-[var(--alert-icon)]"
162
163
  >
163
164
  <AlertStatusIcon status={resolvedStatus} />
164
165
  </span>
@@ -194,7 +195,7 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
194
195
  <p
195
196
  data-slot="alert-description"
196
197
  dir="auto"
197
- className="m-0 w-full break-words text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-font-weight-regular)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--alert-description)] opacity-[var(--bh-opacity-80)]"
198
+ className="m-0 w-full break-words text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-font-weight-regular)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--alert-description)]"
198
199
  >
199
200
  {description}
200
201
  </p>
@@ -212,15 +213,17 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
212
213
  )}
213
214
 
214
215
  {visibleCloseButton && (
215
- <button
216
+ <Button
216
217
  data-slot="alert-close"
217
218
  aria-label={resolvedCloseLabel}
218
- className="inline-flex size-[var(--bh-alert-icon-size)] shrink-0 cursor-pointer items-center justify-center rounded-[var(--bh-radius-full)] border-0 bg-transparent p-0 text-[var(--bh-content-subtle)] outline-none focus-visible:shadow-[var(--shadow-button-focus)]"
219
+ className="size-[var(--bh-alert-icon-slot-size)] shrink-0 rounded-[var(--bh-radius-full)] p-0 text-[var(--bh-content-subtle)]"
219
220
  onClick={onClose}
221
+ size="icon-xs"
220
222
  type="button"
223
+ variant="ghost"
221
224
  >
222
225
  <CloseIcon />
223
- </button>
226
+ </Button>
224
227
  )}
225
228
  </section>
226
229
  )
@@ -237,15 +240,16 @@ function AlertActions({
237
240
  className="flex shrink-0 items-center gap-[var(--bh-space-3xl-16)]"
238
241
  >
239
242
  {actions.map((action, index) => (
240
- <button
243
+ <Button
241
244
  data-slot="alert-action"
242
- className="inline-flex shrink-0 cursor-pointer items-center justify-center gap-[var(--bh-space-xxs-2)] border-0 bg-transparent p-0 text-start text-[length:var(--bh-text-body-md-underline-font-size)] font-[var(--bh-text-body-md-underline-font-weight)] leading-[var(--bh-text-body-md-underline-line-height)] tracking-[var(--bh-text-body-md-underline-letter-spacing)] text-[var(--bh-content-default)] underline outline-none [text-underline-position:from-font] focus-visible:shadow-[var(--shadow-button-focus)]"
245
+ className="h-auto shrink-0 gap-[var(--bh-space-xxs-2)] p-0 text-start text-[length:var(--bh-text-body-md-underline-font-size)] font-[var(--bh-text-body-md-underline-font-weight)] leading-[var(--bh-text-body-md-underline-line-height)] tracking-[var(--bh-text-body-md-underline-letter-spacing)] text-[var(--bh-content-default)] underline [text-underline-position:from-font]"
243
246
  key={index}
244
247
  onClick={action.onClick}
245
248
  type="button"
249
+ variant="ghost"
246
250
  >
247
251
  {action.label}
248
- </button>
252
+ </Button>
249
253
  ))}
250
254
  </div>
251
255
  )
@@ -273,7 +277,7 @@ function AlertStatusIcon({ status }: { status: AlertStatus }) {
273
277
 
274
278
  function CloseIcon() {
275
279
  return (
276
- <XIcon aria-hidden="true" className="size-[var(--bh-alert-icon-size)]" strokeWidth="var(--bh-icon-stroke-225)" />
280
+ <XIcon aria-hidden="true" className="size-[var(--bh-space-3xl-16)]" strokeWidth="var(--bh-icon-stroke-225)" />
277
281
  )
278
282
  }
279
283
 
@@ -208,9 +208,15 @@ const AttributeHeader = React.forwardRef<HTMLElement, AttributeHeaderProps>(
208
208
  {icon ? (
209
209
  <span
210
210
  data-slot="attribute-header-icon"
211
- className="grid size-[var(--bh-attribute-header-icon-size)] place-items-center rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-soft)] text-[var(--bh-content-subtle)] sm:size-[var(--bh-attribute-header-icon-size-lg)] [&_svg]:size-[var(--bh-space-4xl-20)] sm:[&_svg]:size-[var(--bh-space-5xl-24)]"
211
+ className="grid size-[var(--bh-attribute-header-icon-size)] place-items-center rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-soft)] text-[var(--bh-content-subtle)] sm:size-[var(--bh-attribute-header-icon-size-lg)]"
212
212
  >
213
- {icon}
213
+ <span
214
+ aria-hidden="true"
215
+ data-slot="attribute-header-icon-slot"
216
+ className="grid size-[var(--bh-icon-slot-size-default)] place-items-center [&_svg]:size-[var(--bh-icon-size-default)] sm:size-[var(--bh-space-5xl-24)] sm:[&_svg]:size-[var(--bh-space-5xl-24)]"
217
+ >
218
+ {icon}
219
+ </span>
214
220
  </span>
215
221
  ) : null}
216
222
  <span
@@ -16,20 +16,20 @@ import { cn } from "@/lib/utils"
16
16
 
17
17
  const avatarSizeVariants = {
18
18
  "2xs":
19
- "[--bh-avatar-size:var(--bh-avatar-size-2xs)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-2xs)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-2xs)]",
20
- xs: "[--bh-avatar-size:var(--bh-avatar-size-xs)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-xs)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-xs)]",
21
- sm: "[--bh-avatar-size:var(--bh-avatar-size-sm)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-sm)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-sm)]",
22
- md: "[--bh-avatar-size:var(--bh-avatar-size-md)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-md)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-md)]",
23
- lg: "[--bh-avatar-size:var(--bh-avatar-size-lg)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-lg)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-lg)]",
19
+ "[--bh-avatar-size:var(--bh-avatar-size-2xs)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-2xs)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-2xs)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
20
+ xs: "[--bh-avatar-size:var(--bh-avatar-size-xs)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-xs)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-xs)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
21
+ sm: "[--bh-avatar-size:var(--bh-avatar-size-sm)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-sm)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-sm)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
22
+ md: "[--bh-avatar-size:var(--bh-avatar-size-md)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-md)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-md)] [--bh-avatar-icon-slot-size:var(--bh-icon-slot-size-default)]",
23
+ lg: "[--bh-avatar-size:var(--bh-avatar-size-lg)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-lg)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-lg)] [--bh-avatar-icon-slot-size:var(--bh-icon-slot-size-default)]",
24
24
  default:
25
- "[--bh-avatar-size:var(--bh-avatar-size-xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-xl)]",
26
- xl: "[--bh-avatar-size:var(--bh-avatar-size-xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-xl)]",
25
+ "[--bh-avatar-size:var(--bh-avatar-size-xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-xl)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
26
+ xl: "[--bh-avatar-size:var(--bh-avatar-size-xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-xl)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
27
27
  "2xl":
28
- "[--bh-avatar-size:var(--bh-avatar-size-2xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-2xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-2xl)]",
28
+ "[--bh-avatar-size:var(--bh-avatar-size-2xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-2xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-2xl)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
29
29
  "3xl":
30
- "[--bh-avatar-size:var(--bh-avatar-size-3xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-3xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-3xl)]",
30
+ "[--bh-avatar-size:var(--bh-avatar-size-3xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-3xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-3xl)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
31
31
  "4xl":
32
- "[--bh-avatar-size:var(--bh-avatar-size-4xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-4xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-4xl)]",
32
+ "[--bh-avatar-size:var(--bh-avatar-size-4xl)] [--bh-avatar-fallback-size:var(--bh-avatar-fallback-font-size-4xl)] [--bh-avatar-icon-size:var(--bh-avatar-icon-size-4xl)] [--bh-avatar-icon-slot-size:var(--bh-avatar-icon-size)]",
33
33
  } as const
34
34
 
35
35
  const avatarVariants = cva(
@@ -50,7 +50,7 @@ const avatarVariants = cva(
50
50
  )
51
51
 
52
52
  const avatarFallbackVariants = cva(
53
- "flex size-full items-center justify-center rounded-[var(--bh-avatar-radius,var(--bh-radius-full))] bg-[var(--bh-bg-neutral-subtle)] text-[length:var(--bh-avatar-fallback-size,var(--bh-text-body-sm-medium-font-size))] font-[var(--bh-font-weight-semibold)] leading-none text-[var(--bh-content-subtle)] whitespace-nowrap",
53
+ "flex size-full items-center justify-center rounded-[var(--bh-avatar-radius,var(--bh-radius-full))] bg-[var(--bh-bg-neutral-subtle)] text-[length:var(--bh-avatar-fallback-size,var(--bh-text-body-sm-medium-font-size))] font-[var(--bh-font-weight-semibold)] leading-none text-[var(--bh-content-default)] whitespace-nowrap",
54
54
  {
55
55
  variants: {
56
56
  size: avatarSizeVariants,
@@ -62,7 +62,7 @@ const avatarFallbackVariants = cva(
62
62
  )
63
63
 
64
64
  const avatarIconVariants = cva(
65
- "flex size-full items-center justify-center rounded-[var(--bh-avatar-radius,var(--bh-radius-full))] bg-[var(--bh-bg-neutral-subtle)] text-[var(--bh-content-subtle)]",
65
+ "flex size-full items-center justify-center rounded-[var(--bh-avatar-radius,var(--bh-radius-full))] bg-[var(--bh-bg-neutral-subtle)] text-[var(--bh-content-default)]",
66
66
  {
67
67
  variants: {
68
68
  size: avatarSizeVariants,
@@ -147,7 +147,9 @@ const avatarActionVariants = cva(
147
147
  )
148
148
 
149
149
  function Avatar({
150
+ "aria-label": ariaLabel,
150
151
  className,
152
+ role,
151
153
  size,
152
154
  shape,
153
155
  ...props
@@ -155,9 +157,11 @@ function Avatar({
155
157
  VariantProps<typeof avatarVariants>) {
156
158
  return (
157
159
  <AvatarPrimitive.Root
160
+ aria-label={ariaLabel}
158
161
  data-slot="avatar"
159
162
  data-size={size ?? "default"}
160
163
  data-shape={shape ?? "circular"}
164
+ role={role ?? (ariaLabel ? "img" : undefined)}
161
165
  className={cn(avatarVariants({ size, shape, className }))}
162
166
  {...props}
163
167
  />
@@ -208,13 +212,17 @@ function AvatarIcon({
208
212
  className={cn(avatarIconVariants({ size, className }))}
209
213
  {...props}
210
214
  >
211
- {children ?? (
212
- <UserIcon
213
- aria-hidden="true"
214
- className="size-[var(--bh-avatar-icon-size)]"
215
- strokeWidth="var(--bh-icon-stroke-210)"
216
- />
217
- )}
215
+ <span
216
+ data-slot="avatar-icon-slot"
217
+ className="flex size-[var(--bh-avatar-icon-slot-size,var(--bh-avatar-icon-size))] shrink-0 items-center justify-center [&_svg]:size-[var(--bh-avatar-icon-size)]"
218
+ >
219
+ {children ?? (
220
+ <UserIcon
221
+ aria-hidden="true"
222
+ strokeWidth="var(--bh-icon-stroke-210)"
223
+ />
224
+ )}
225
+ </span>
218
226
  </span>
219
227
  )
220
228
  }
@@ -307,7 +315,7 @@ function AvatarGroupCount({
307
315
  data-slot="avatar-group-count"
308
316
  className={cn(
309
317
  avatarVariants({ size }),
310
- "-ms-[var(--bh-space-md-8)] bg-[var(--bh-bg-neutral-subtle)] text-[length:var(--bh-avatar-fallback-size,var(--bh-text-body-sm-medium-font-size))] font-[var(--bh-font-weight-semibold)] leading-none text-[var(--bh-content-subtle)] ring-[var(--bh-space-xxs-2)] ring-[var(--bh-bg-default)]",
318
+ "-ms-[var(--bh-space-md-8)] bg-[var(--bh-bg-neutral-subtle)] text-[length:var(--bh-avatar-fallback-size,var(--bh-text-body-sm-medium-font-size))] font-[var(--bh-font-weight-semibold)] leading-none text-[var(--bh-content-default)] ring-[var(--bh-space-xxs-2)] ring-[var(--bh-bg-default)]",
311
319
  className
312
320
  )}
313
321
  {...props}
@@ -192,13 +192,13 @@ const badgeVariants = cva(
192
192
  badgeStyle: "outline",
193
193
  color: "neutral",
194
194
  class:
195
- "border-[var(--bh-border-strong)] text-[var(--bh-content-subtle)] [--badge-dot-color:var(--bh-content-subtle)] [--badge-split-border-color:var(--bh-border-default)]",
195
+ "border-[var(--bh-border-strong)] text-[var(--bh-content-default)] [--badge-dot-color:var(--bh-content-default)] [--badge-split-border-color:var(--bh-border-default)]",
196
196
  },
197
197
  {
198
198
  badgeStyle: "solid",
199
199
  color: "neutral",
200
200
  class:
201
- "bg-[var(--bh-bg-neutral-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-subtle)]",
201
+ "bg-[var(--bh-bg-neutral-bold)] text-[var(--bh-content-on-neutral)] [--badge-dot-color:var(--bh-content-on-neutral)] [--badge-split-border-color:var(--bh-border-subtle)]",
202
202
  },
203
203
  {
204
204
  badgeStyle: "light",
@@ -210,13 +210,13 @@ const badgeVariants = cva(
210
210
  badgeStyle: "outline",
211
211
  color: "blue",
212
212
  class:
213
- "border-[var(--bh-border-accent-blue-strong)] text-[var(--bh-content-accent-blue-default)] [--badge-dot-color:var(--bh-content-accent-blue-default)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
213
+ "border-[var(--bh-border-accent-blue-strong)] text-[var(--bh-content-accent-blue-strong)] [--badge-dot-color:var(--bh-content-accent-blue-strong)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
214
214
  },
215
215
  {
216
216
  badgeStyle: "solid",
217
217
  color: "blue",
218
218
  class:
219
- "bg-[var(--bh-bg-accent-blue-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
219
+ "bg-[var(--bh-bg-accent-blue-bold)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-blue-subtle)]",
220
220
  },
221
221
  {
222
222
  badgeStyle: "light",
@@ -228,13 +228,13 @@ const badgeVariants = cva(
228
228
  badgeStyle: "outline",
229
229
  color: "green",
230
230
  class:
231
- "border-[var(--bh-border-accent-green-strong)] text-[var(--bh-content-accent-green-default)] [--badge-dot-color:var(--bh-content-accent-green-default)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
231
+ "border-[var(--bh-border-accent-green-strong)] text-[var(--bh-content-accent-green-strong)] [--badge-dot-color:var(--bh-content-accent-green-strong)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
232
232
  },
233
233
  {
234
234
  badgeStyle: "solid",
235
235
  color: "green",
236
236
  class:
237
- "bg-[var(--bh-bg-accent-green-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
237
+ "bg-[var(--bh-bg-accent-green-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-green-subtle)]",
238
238
  },
239
239
  {
240
240
  badgeStyle: "light",
@@ -246,13 +246,13 @@ const badgeVariants = cva(
246
246
  badgeStyle: "outline",
247
247
  color: "amber",
248
248
  class:
249
- "border-[var(--bh-border-accent-amber-strong)] text-[var(--bh-content-accent-amber-default)] [--badge-dot-color:var(--bh-content-accent-amber-default)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
249
+ "border-[var(--bh-border-accent-amber-strong)] text-[var(--bh-content-accent-amber-strong)] [--badge-dot-color:var(--bh-content-accent-amber-strong)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
250
250
  },
251
251
  {
252
252
  badgeStyle: "solid",
253
253
  color: "amber",
254
254
  class:
255
- "bg-[var(--bh-bg-accent-amber-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
255
+ "bg-[var(--bh-bg-accent-amber-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-amber-subtle)]",
256
256
  },
257
257
  {
258
258
  badgeStyle: "light",
@@ -264,7 +264,7 @@ const badgeVariants = cva(
264
264
  badgeStyle: "outline",
265
265
  color: "danger",
266
266
  class:
267
- "border-[var(--bh-border-accent-red-strong)] text-[var(--bh-content-accent-red-default)] [--badge-dot-color:var(--bh-content-accent-red-default)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
267
+ "border-[var(--bh-border-accent-red-strong)] text-[var(--bh-content-accent-red-strong)] [--badge-dot-color:var(--bh-content-accent-red-strong)] [--badge-split-border-color:var(--bh-border-accent-red-subtle)]",
268
268
  },
269
269
  {
270
270
  badgeStyle: "solid",
@@ -282,7 +282,7 @@ const badgeVariants = cva(
282
282
  badgeStyle: "outline",
283
283
  color: "purple",
284
284
  class:
285
- "border-[var(--bh-border-accent-purple-strong)] text-[var(--bh-content-accent-purple-default)] [--badge-dot-color:var(--bh-content-accent-purple-default)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
285
+ "border-[var(--bh-border-accent-purple-strong)] text-[var(--bh-content-accent-purple-strong)] [--badge-dot-color:var(--bh-content-accent-purple-strong)] [--badge-split-border-color:var(--bh-border-accent-purple-subtle)]",
286
286
  },
287
287
  {
288
288
  badgeStyle: "solid",
@@ -300,13 +300,13 @@ const badgeVariants = cva(
300
300
  badgeStyle: "outline",
301
301
  color: "fuchsia",
302
302
  class:
303
- "border-[var(--bh-border-accent-fuchsia-strong)] text-[var(--bh-content-accent-fuchsia-default)] [--badge-dot-color:var(--bh-content-accent-fuchsia-default)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
303
+ "border-[var(--bh-border-accent-fuchsia-strong)] text-[var(--bh-content-accent-fuchsia-strong)] [--badge-dot-color:var(--bh-content-accent-fuchsia-strong)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
304
304
  },
305
305
  {
306
306
  badgeStyle: "solid",
307
307
  color: "fuchsia",
308
308
  class:
309
- "bg-[var(--bh-bg-accent-fuchsia-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
309
+ "bg-[var(--bh-bg-accent-fuchsia-default)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-fuchsia-subtle)]",
310
310
  },
311
311
  {
312
312
  badgeStyle: "light",
@@ -318,13 +318,13 @@ const badgeVariants = cva(
318
318
  badgeStyle: "outline",
319
319
  color: "rose",
320
320
  class:
321
- "border-[var(--bh-border-accent-rose-strong)] text-[var(--bh-content-accent-rose-default)] [--badge-dot-color:var(--bh-content-accent-rose-default)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
321
+ "border-[var(--bh-border-accent-rose-strong)] text-[var(--bh-content-accent-rose-strong)] [--badge-dot-color:var(--bh-content-accent-rose-strong)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
322
322
  },
323
323
  {
324
324
  badgeStyle: "solid",
325
325
  color: "rose",
326
326
  class:
327
- "bg-[var(--bh-bg-accent-rose-default)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
327
+ "bg-[var(--bh-bg-accent-rose-bold)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-rose-subtle)]",
328
328
  },
329
329
  {
330
330
  badgeStyle: "light",
@@ -336,13 +336,13 @@ const badgeVariants = cva(
336
336
  badgeStyle: "outline",
337
337
  color: "sky",
338
338
  class:
339
- "border-[var(--bh-border-accent-sky-strong)] text-[var(--bh-content-accent-sky-default)] [--badge-dot-color:var(--bh-content-accent-sky-default)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
339
+ "border-[var(--bh-border-accent-sky-strong)] text-[var(--bh-content-accent-sky-strong)] [--badge-dot-color:var(--bh-content-accent-sky-strong)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
340
340
  },
341
341
  {
342
342
  badgeStyle: "solid",
343
343
  color: "sky",
344
344
  class:
345
- "bg-[var(--bh-bg-accent-sky-base)] text-[var(--bh-content-on-color)] [--badge-dot-color:var(--bh-content-on-color)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
345
+ "bg-[var(--bh-bg-accent-sky-base)] text-[var(--bh-content-on-light)] [--badge-dot-color:var(--bh-content-on-light)] [--badge-split-border-color:var(--bh-border-accent-sky-subtle)]",
346
346
  },
347
347
  {
348
348
  badgeStyle: "light",
@@ -354,7 +354,7 @@ const badgeVariants = cva(
354
354
  badgeStyle: "outline",
355
355
  color: "golden",
356
356
  class:
357
- "border-[var(--bh-border-accent-golden-strong)] text-[var(--bh-content-accent-golden-default)] [--badge-dot-color:var(--bh-content-accent-golden-default)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
357
+ "border-[var(--bh-border-accent-golden-strong)] text-[var(--bh-content-accent-golden-strong)] [--badge-dot-color:var(--bh-content-accent-golden-strong)] [--badge-split-border-color:var(--bh-border-accent-golden-subtle)]",
358
358
  },
359
359
  {
360
360
  badgeStyle: "solid",
@@ -32,7 +32,7 @@ type ToggleGroupContextValue = {
32
32
  const toggleGroupContext = React.createContext<ToggleGroupContextValue | null>(null)
33
33
 
34
34
  const buttonGroupVariants = cva(
35
- "inline-flex w-fit items-center overflow-hidden rounded-[var(--bh-button-group-radius)] bg-[var(--bh-button-group-bg)] text-[var(--bh-button-group-content)] shadow-[var(--shadow-button-group)] [--shadow-button-group:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-button-group-border),var(--shadow-component-default)]",
35
+ "inline-flex w-fit max-w-full items-center overflow-x-auto overflow-y-hidden rounded-[var(--bh-button-group-radius)] bg-[var(--bh-button-group-bg)] text-[var(--bh-button-group-content)] shadow-[var(--shadow-button-group)] outline-none focus-visible:shadow-[var(--shadow-button-group-focus)] [--shadow-button-group:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-button-group-border),var(--shadow-component-default)]",
36
36
  {
37
37
  variants: {
38
38
  size: {
@@ -48,7 +48,7 @@ const buttonGroupVariants = cva(
48
48
  )
49
49
 
50
50
  const buttonGroupItemVariants = cva(
51
- "relative inline-flex shrink-0 items-center justify-center whitespace-nowrap bg-[var(--bh-button-group-item-bg)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] text-[var(--bh-button-group-content)] outline-none transition-[background-color,box-shadow,color] before:absolute before:inset-y-0 before:start-0 before:w-[var(--bh-border-width-default)] before:bg-[var(--bh-button-group-divider)] before:content-[''] first:before:hidden hover:bg-[var(--bh-button-group-item-hover-bg)] active:bg-[var(--bh-button-group-item-pressed-bg)] focus-visible:z-[var(--bh-z-raised)] focus-visible:shadow-[var(--shadow-button-group-focus)] aria-[pressed=true]:bg-[var(--bh-button-group-item-active-bg)] aria-[pressed=true]:text-[var(--bh-button-group-content-active)] data-[active=true]:bg-[var(--bh-button-group-item-active-bg)] data-[active=true]:text-[var(--bh-button-group-content-active)] disabled:pointer-events-none disabled:bg-[var(--bh-button-group-item-disabled-bg)] disabled:text-[var(--bh-button-group-content-disabled)] [&_svg]:pointer-events-none [&_svg]:size-[var(--bh-button-group-icon-size)] [&_svg]:shrink-0 rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
51
+ "relative inline-flex shrink-0 items-center justify-center whitespace-nowrap bg-[var(--bh-button-group-item-bg)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] text-[var(--bh-button-group-content)] outline-none transition-[background-color,box-shadow,color] before:absolute before:inset-y-0 before:start-0 before:w-[var(--bh-border-width-default)] before:bg-[var(--bh-button-group-divider)] before:content-[''] first:before:hidden hover:bg-[var(--bh-button-group-item-hover-bg)] active:bg-[var(--bh-button-group-item-pressed-bg)] focus-visible:z-[var(--bh-z-raised)] focus-visible:shadow-[var(--shadow-button-group-focus)] aria-[pressed=true]:bg-[var(--bh-button-group-item-active-bg)] aria-[pressed=true]:text-[var(--bh-button-group-content-active)] data-[active=true]:bg-[var(--bh-button-group-item-active-bg)] data-[active=true]:text-[var(--bh-button-group-content-active)] disabled:pointer-events-none disabled:bg-[var(--bh-button-group-item-disabled-bg)] disabled:text-[var(--bh-button-group-content-disabled)] [&_svg]:pointer-events-none [&_svg]:size-[var(--bh-button-group-icon-size)] [&_svg]:m-[calc((var(--bh-button-group-icon-slot-size)-var(--bh-button-group-icon-size))/2)] [&_svg]:shrink-0 rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
52
52
  {
53
53
  variants: {
54
54
  size: {
@@ -200,6 +200,7 @@ const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(function
200
200
  mode = "default",
201
201
  children,
202
202
  role = "group",
203
+ tabIndex = 0,
203
204
  ...props
204
205
  }, ref) {
205
206
  const resolvedSize = resolveButtonGroupSize(density, size)
@@ -222,6 +223,7 @@ const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(function
222
223
  data-slot="button-group"
223
224
  ref={ref}
224
225
  role={role}
226
+ tabIndex={tabIndex}
225
227
  className={cn(buttonGroupVariants({ size: contextValue.size }), className)}
226
228
  {...props}
227
229
  >
@@ -7,7 +7,7 @@ import { cn } from "@/lib/utils"
7
7
  type ControlDensity = "compact" | "default" | "comfortable"
8
8
 
9
9
  const buttonVariants = cva(
10
- "group/button inline-flex min-w-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-control-default)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:shadow-[var(--shadow-button-focus)] disabled:pointer-events-none disabled:shadow-none aria-invalid:border-[length:var(--bh-border-width-default)] aria-invalid:border-[var(--bh-border-danger-strong)] aria-invalid:shadow-[var(--shadow-button-danger-focus)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[var(--bh-button-icon-size)] [&_svg[data-icon='inline-start']]:ms-[var(--bh-button-icon-offset)] [&_svg[data-icon='inline-end']]:me-[var(--bh-button-icon-offset)] rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
10
+ "group/button inline-flex min-w-0 items-center justify-center whitespace-nowrap rounded-[var(--bh-control-default)] text-[length:var(--bh-text-button-font-size)] font-[var(--bh-text-button-font-weight)] leading-[var(--bh-text-button-line-height)] tracking-[var(--bh-text-button-letter-spacing)] outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:shadow-[var(--shadow-button-focus)] disabled:pointer-events-none disabled:shadow-none aria-invalid:border-[length:var(--bh-border-width-default)] aria-invalid:border-[var(--bh-border-danger-strong)] aria-invalid:shadow-[var(--shadow-button-danger-focus)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[var(--bh-button-icon-size)] [&_svg:not([class*='size-'])]:m-[calc((var(--bh-button-icon-slot-size)-var(--bh-button-icon-size))/2)] [&_svg[data-icon='inline-start']]:ms-[calc(var(--bh-button-icon-offset)+(var(--bh-button-icon-slot-size)-var(--bh-button-icon-size))/2)] [&_svg[data-icon='inline-end']]:me-[calc(var(--bh-button-icon-offset)+(var(--bh-button-icon-slot-size)-var(--bh-button-icon-size))/2)] rtl:[&_svg[data-rtl-flip='true']]:-scale-x-100",
11
11
  {
12
12
  variants: {
13
13
  variant: {
@@ -18,9 +18,9 @@ const buttonVariants = cva(
18
18
  destructive:
19
19
  "bg-[var(--bh-interactive-danger-default)] text-[var(--bh-content-on-color)] shadow-[var(--shadow-button-raised)] hover:bg-[var(--bh-interactive-danger-hover)] active:bg-[var(--bh-interactive-danger-pressed)] focus-visible:shadow-[var(--shadow-button-danger-focus)] disabled:bg-[var(--bh-interactive-danger-disabled)] disabled:text-[var(--bh-content-disabled)]",
20
20
  success:
21
- "bg-[var(--bh-bg-success-default)] text-[var(--bh-content-on-color)] shadow-[var(--shadow-button-raised)] hover:bg-[var(--bh-bg-success-bold)] active:bg-[var(--bh-bg-success-strong)] disabled:bg-[var(--bh-interactive-brand-disabled)] disabled:text-[var(--bh-content-disabled)]",
21
+ "bg-[var(--bh-interactive-success-default)] text-[var(--bh-content-on-success)] shadow-[var(--shadow-button-raised)] hover:bg-[var(--bh-interactive-success-hover)] active:bg-[var(--bh-interactive-success-pressed)] disabled:bg-[var(--bh-interactive-brand-disabled)] disabled:text-[var(--bh-content-disabled)]",
22
22
  warning:
23
- "bg-[var(--bh-bg-warning-default)] text-[var(--bh-content-on-light)] shadow-[var(--shadow-button-raised)] hover:bg-[var(--bh-bg-warning-bold)] active:bg-[var(--bh-bg-warning-strong)] disabled:bg-[var(--bh-interactive-brand-disabled)] disabled:text-[var(--bh-content-disabled)]",
23
+ "bg-[var(--bh-interactive-warning-default)] text-[var(--bh-content-on-warning)] shadow-[var(--shadow-button-raised)] hover:bg-[var(--bh-interactive-warning-hover)] active:bg-[var(--bh-interactive-warning-pressed)] disabled:bg-[var(--bh-interactive-brand-disabled)] disabled:text-[var(--bh-content-disabled)]",
24
24
  secondary:
25
25
  "bg-[var(--bh-interactive-secondary-default)] text-[var(--bh-content-default)] shadow-[var(--shadow-button-secondary)] [--bh-button-secondary-border:var(--bh-border-default)] hover:bg-[var(--bh-interactive-secondary-hover)] active:bg-[var(--bh-interactive-secondary-pressed)] focus-visible:[--bh-button-secondary-focus-border:var(--bh-border-brand-strong)] focus-visible:shadow-[var(--shadow-button-secondary-focus)] disabled:border-[length:var(--bh-border-width-default)] disabled:border-[var(--bh-border-disabled)] disabled:bg-[var(--bh-interactive-secondary-disabled)] disabled:text-[var(--bh-content-disabled)]",
26
26
  soft: