banhaten 0.3.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.
- package/CHANGELOG.md +27 -0
- package/README.md +4 -4
- package/docs/design-system/component-implementation-ownership-audit.md +2 -1
- package/docs/design-system/component-quality-audit-2026-08-20.md +4 -2
- package/docs/design-system/components/alert-dialog.md +1 -1
- package/docs/design-system/components/alert.md +2 -2
- package/docs/design-system/components/attribute.md +1 -1
- package/docs/design-system/components/avatar.md +2 -2
- package/docs/design-system/components/banner.md +2 -2
- package/docs/design-system/components/breadcrumbs.md +2 -2
- package/docs/design-system/components/button-group.md +2 -2
- package/docs/design-system/components/button.md +2 -2
- package/docs/design-system/components/date-picker.md +3 -1
- package/docs/design-system/components/file-upload.md +1 -1
- package/docs/design-system/components/input.md +8 -6
- package/docs/design-system/components/item.md +3 -1
- package/docs/design-system/components/kbd.md +11 -8
- package/docs/design-system/components/menu.md +2 -2
- package/docs/design-system/components/modal.md +1 -1
- package/docs/design-system/components/onboarding-step-list-item.md +2 -2
- package/docs/design-system/components/pagination.md +2 -2
- package/docs/design-system/components/radio-card.md +1 -1
- package/docs/design-system/components/select.md +1 -1
- package/docs/design-system/components/sidebar.md +59 -19
- package/docs/design-system/components/table.md +1 -1
- package/docs/design-system/components/tag.md +3 -3
- package/docs/design-system/components/toolbar.md +1 -1
- package/docs/design-system/density-and-elevation.md +16 -12
- package/docs/design-system/icons.md +6 -1
- package/docs/design-system/installation.md +1 -1
- package/docs/design-system/token-governance.md +7 -8
- package/package.json +1 -1
- package/registry/components/alert-dialog.tsx +1 -1
- package/registry/components/alert.tsx +3 -3
- package/registry/components/attribute.tsx +8 -2
- package/registry/components/avatar.tsx +21 -17
- package/registry/components/button-group.tsx +1 -1
- package/registry/components/button.tsx +1 -1
- package/registry/components/date-picker.tsx +16 -4
- package/registry/components/expanded/OnboardingStepListItem.tsx +6 -5
- package/registry/components/expanded/banner.css +10 -5
- package/registry/components/expanded/breadcrumbs.css +4 -4
- package/registry/components/expanded/fileUpload.css +3 -3
- package/registry/components/expanded/onboardingStepListItem.css +14 -5
- package/registry/components/expanded/table.css +5 -2
- package/registry/components/input.tsx +12 -5
- package/registry/components/item.tsx +1 -1
- package/registry/components/kbd.tsx +109 -15
- package/registry/components/menu.tsx +1 -1
- package/registry/components/modal.tsx +1 -1
- package/registry/components/pagination.tsx +12 -7
- package/registry/components/radio-card.tsx +1 -2
- package/registry/components/select.tsx +1 -1
- package/registry/components/sidebar.tsx +929 -57
- package/registry/components/tag.tsx +1 -1
- package/registry/components/toolbar.tsx +1 -1
- package/registry/examples/attribute-demo.tsx +1 -1
- package/registry/examples/command-palette-demo.tsx +0 -1
- package/registry/examples/kbd-demo.tsx +2 -1
- package/registry/examples/sidebar-demo.tsx +60 -16
- package/registry/guidance/USAGE.md +1 -1
- package/registry/index.json +233 -31
- package/registry/styles/globals.css +152 -118
- package/registry.json +4 -3
- package/tokens/banhaten.tokens.json +169 -49
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Sidebar
|
|
2
2
|
|
|
3
|
-
Sidebar is
|
|
3
|
+
Sidebar is the complete Figma-aligned application navigation system. It provides a 280px expanded shell and 48px icon rail, brand and workspace headers, grouped and nested menus, tree navigation, badges and actions, user footers, tooltips, controlled state, and logical RTL placement.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
All sidebar parts render inside `SidebarProvider`. The provider owns controlled or uncontrolled collapse state and supplies the tooltip context used by icon-only menu rows.
|
|
6
6
|
|
|
7
7
|
## API
|
|
8
8
|
|
|
@@ -10,19 +10,33 @@ Sidebar is a composable app navigation surface with collapse state, grouped menu
|
|
|
10
10
|
<SidebarProvider>
|
|
11
11
|
<Sidebar>
|
|
12
12
|
<SidebarHeader>
|
|
13
|
-
|
|
14
|
-
<SidebarToggle />
|
|
13
|
+
<SidebarHeaderWorkspaceSwitch label="Acme Inc" />
|
|
15
14
|
</SidebarHeader>
|
|
16
15
|
<SidebarBody>
|
|
17
16
|
<SidebarGroup>
|
|
18
17
|
<SidebarGroupLabel>Main</SidebarGroupLabel>
|
|
19
18
|
<SidebarMenu>
|
|
20
19
|
<SidebarMenuItem>
|
|
21
|
-
<SidebarMenuButton active>
|
|
20
|
+
<SidebarMenuButton active icon={<LayoutGridIcon />}>
|
|
21
|
+
Overview
|
|
22
|
+
</SidebarMenuButton>
|
|
23
|
+
</SidebarMenuItem>
|
|
24
|
+
<SidebarMenuItem>
|
|
25
|
+
<SidebarMenuButton icon={<CreditCardIcon />}>
|
|
26
|
+
Transactions
|
|
27
|
+
</SidebarMenuButton>
|
|
28
|
+
<SidebarMenuSub>
|
|
29
|
+
<SidebarMenuSubButton active showLine>
|
|
30
|
+
Payments
|
|
31
|
+
</SidebarMenuSubButton>
|
|
32
|
+
</SidebarMenuSub>
|
|
22
33
|
</SidebarMenuItem>
|
|
23
34
|
</SidebarMenu>
|
|
24
35
|
</SidebarGroup>
|
|
25
36
|
</SidebarBody>
|
|
37
|
+
<SidebarFooter>
|
|
38
|
+
<SidebarFooterUser name="Olivia Rhye" supportText="olivia@acme.com" />
|
|
39
|
+
</SidebarFooter>
|
|
26
40
|
</Sidebar>
|
|
27
41
|
</SidebarProvider>
|
|
28
42
|
```
|
|
@@ -30,26 +44,37 @@ Sidebar is a composable app navigation surface with collapse state, grouped menu
|
|
|
30
44
|
| Part | Purpose |
|
|
31
45
|
| --- | --- |
|
|
32
46
|
| `SidebarProvider` | controlled or uncontrolled collapsed state |
|
|
33
|
-
| `Sidebar` | navigation surface |
|
|
47
|
+
| `Sidebar` | 280px navigation surface and 48px collapsed rail |
|
|
34
48
|
| `SidebarHeader`, `SidebarBody`, `SidebarFooter` | layout regions |
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
49
|
+
| `SidebarHeaderBrand`, `SidebarHeaderWorkspaceSwitch` | brand and workspace header compositions |
|
|
50
|
+
| `SidebarHeaderAction` | compact search or custom header action |
|
|
51
|
+
| `SidebarFooterUser` | supporting-text or bordered user switcher |
|
|
52
|
+
| `SidebarGroup`, `SidebarGroupLabel`, `SidebarGroupAction` | grouped navigation, disclosure, and actions |
|
|
53
|
+
| `SidebarMenu`, `SidebarMenuItem`, `SidebarMenuButton` | default and avatar menu structure with optional slots |
|
|
54
|
+
| `SidebarMenuBadge` | neutral count badge for a menu row |
|
|
55
|
+
| `SidebarMenuSub`, `SidebarMenuSubButton` | plain or line-guided Level 2 navigation |
|
|
56
|
+
| `SidebarTreeGroup`, `SidebarMenuTreeItem` | four-level file or project tree navigation |
|
|
37
57
|
| `SidebarToggle`, `SidebarRail` | collapse controls |
|
|
38
58
|
| `SidebarTrigger` | shadcn-compatible alias for `SidebarToggle` |
|
|
39
59
|
| `SidebarInset` | main content wrapper for app-shell layouts |
|
|
40
60
|
|
|
61
|
+
`SidebarMenuButton` accepts `variant="default" | "avatar" | "level2" | "level2-line"`. Its optional `icon`, `supportText`, `badge`, and `trailingIcon` props keep every row aligned to the Figma slots. Prefer `SidebarMenuSubButton` for nested rows because its `showLine` prop selects the nested treatment directly.
|
|
62
|
+
|
|
63
|
+
Use `SidebarHeaderBrand` for product navigation. Use `SidebarHeaderWorkspaceSwitch` with `variant="actions"` for the compact search/collapse header or `variant="card"` for the bordered workspace card.
|
|
64
|
+
|
|
41
65
|
## Token Contract
|
|
42
66
|
|
|
43
|
-
Sidebar uses generated `--bh-sidebar
|
|
67
|
+
Sidebar uses generated `--bh-sidebar-*` tokens for expanded and collapsed widths, row heights, header actions, compact controls, workspace cards, tree indentation, padding, backgrounds, borders, and menu states. Expanded and collapsed navigation share the same 18px glyph in a centered 20px slot. Do not replace these dimensions with local literals in product compositions.
|
|
44
68
|
|
|
45
69
|
## RTL
|
|
46
70
|
|
|
47
|
-
Use `dir="rtl"` on `SidebarProvider` and `side="start"` or `side="end"` for logical placement.
|
|
71
|
+
Use `dir="rtl"` on `SidebarProvider` and `side="start"` or `side="end"` for logical placement. Inline borders, padding, submenu guides, tree indentation, collapse icons, and disclosure chevrons mirror automatically.
|
|
48
72
|
|
|
49
73
|
## Accessibility
|
|
50
74
|
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
Provide a text child or explicit `aria-label` for every menu button. Collapsed rows reuse that label in a tooltip. Active menu and tree rows expose `aria-current="page"`; expandable tree rows expose `aria-expanded`; `SidebarToggle` announces both its action and pressed collapse state.
|
|
76
|
+
|
|
77
|
+
Level 2 rows, group labels, and tree groups unmount while collapsed so keyboard users do not encounter invisible controls. Use native links through `asChild` when a row navigates, and keep button rendering for in-place actions.
|
|
53
78
|
|
|
54
79
|
## Install
|
|
55
80
|
|
|
@@ -66,8 +91,9 @@ Open the live component page or run `npx banhaten docs sidebar` to inspect sourc
|
|
|
66
91
|
## RTL Rules
|
|
67
92
|
|
|
68
93
|
- `inheritsDirection`: true
|
|
69
|
-
- `placement`: Use side="start" or side="end" with dir on SidebarProvider for logical placement.
|
|
70
|
-
- `spacing`:
|
|
94
|
+
- `placement`: Use side="start" or side="end" with dir on SidebarProvider for logical placement and border selection.
|
|
95
|
+
- `spacing`: Headers, menu rows, nested guides, tree indentation, group actions, and footer content use logical inline spacing.
|
|
96
|
+
- `icons`: Directional collapse, disclosure, and tree chevrons mirror in RTL; semantic icons retain their orientation.
|
|
71
97
|
|
|
72
98
|
<!-- registry-contract:start -->
|
|
73
99
|
## Registry Contract
|
|
@@ -77,9 +103,23 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
77
103
|
|
|
78
104
|
| Field | Values |
|
|
79
105
|
| --- | --- |
|
|
80
|
-
| `supportedExports` | `components/sidebar.tsx: Sidebar, SidebarBody, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarRail, SidebarToggle, SidebarTrigger` |
|
|
81
|
-
| `parts` | `SidebarProvider`, `Sidebar`, `SidebarHeader`, `SidebarBody`, `SidebarFooter`, `SidebarGroup`, `SidebarGroupLabel`, `SidebarMenu`, `SidebarMenuItem`, `SidebarMenuButton`, `SidebarToggle`, `SidebarTrigger`, `SidebarInset`, `SidebarRail` |
|
|
82
|
-
| `registryDependencies` |
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
106
|
+
| `supportedExports` | `components/sidebar.tsx: Sidebar, SidebarBody, SidebarFooter, SidebarFooterUser, SidebarGroup, SidebarGroupAction, SidebarGroupLabel, SidebarHeader, SidebarHeaderAction, SidebarHeaderBrand, SidebarHeaderWorkspaceSwitch, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuTreeItem, SidebarProvider, SidebarRail, SidebarToggle, SidebarTreeGroup, SidebarTrigger` |
|
|
107
|
+
| `parts` | `SidebarProvider`, `Sidebar`, `SidebarHeader`, `SidebarHeaderAction`, `SidebarHeaderBrand`, `SidebarHeaderWorkspaceSwitch`, `SidebarBody`, `SidebarFooter`, `SidebarFooterUser`, `SidebarGroup`, `SidebarGroupAction`, `SidebarGroupLabel`, `SidebarMenu`, `SidebarMenuItem`, `SidebarMenuBadge`, `SidebarMenuButton`, `SidebarMenuSub`, `SidebarMenuSubButton`, `SidebarMenuTreeItem`, `SidebarTreeGroup`, `SidebarToggle`, `SidebarTrigger`, `SidebarInset`, `SidebarRail` |
|
|
108
|
+
| `registryDependencies` | `tooltip` |
|
|
109
|
+
| `props.collapsed` | `boolean; default false; Controls the provider-owned 280px expanded or 48px rail state. Pair with onCollapsedChange for controlled usage.` |
|
|
110
|
+
| `props.defaultCollapsed` | `boolean; default false; Sets the initial rail state when SidebarProvider owns collapse state internally.` |
|
|
111
|
+
| `props.side` | `"start" \| "end"; default "start"; Places Sidebar on the logical start or end edge and selects the matching logical border.` |
|
|
112
|
+
| `props.variant` | `SidebarMenuButtonVariant \| SidebarWorkspaceVariant \| "default" \| "supporting-text"; default "part-specific"; Selects default/avatar/nested menu rows, action/card workspace headers, or default/supporting-text footers on the part that owns it.` |
|
|
113
|
+
| `props.active` | `boolean; default false; Applies the selected menu or tree treatment and exposes aria-current="page".` |
|
|
114
|
+
| `props.state` | `"default" \| "hover" \| "active" \| "focus"; default "default"; Deterministic interaction state for documentation matrices; product hover and focus remain native CSS interactions.` |
|
|
115
|
+
| `props.showLine` | `boolean; default false; Selects the neutral/brand line-guided Level 2 treatment on SidebarMenuSubButton.` |
|
|
116
|
+
| `props.level` | `1 \| 2 \| 3 \| 4; default 1; Controls tokenized logical indentation for SidebarMenuTreeItem.` |
|
|
117
|
+
| `props.expandable` | `boolean; default false; Adds a disclosure affordance to group labels or tree-group headings.` |
|
|
118
|
+
| `props.asChild` | `boolean; default false; Renders menu, tree, or inset content through Radix Slot while preserving sidebar layout and semantics.` |
|
|
119
|
+
| `directions` | `ltr`, `rtl` |
|
|
120
|
+
| `features` | `280px expanded and 48px collapsed shells`, `controlled collapse`, `uncontrolled collapse`, `brand header with search and collapse actions`, `workspace action and card headers`, `default, avatar, Level 2, and Level 2 line menu rows`, `supporting text, count badge, and trailing action slots`, `group labels with disclosure and icon actions`, `four-level tree navigation`, `supporting-text and bordered user footers`, `icon-only rail with tooltips`, `logical side placement and RTL mirroring` |
|
|
121
|
+
| `sides` | `start`, `end` |
|
|
122
|
+
| `states` | `default`, `hover`, `active`, `focus`, `expanded`, `collapsed` |
|
|
123
|
+
| `tokens` | `--bh-bg-brand-default`, `--bh-bg-default`, `--bh-bg-default-hover`, `--bh-bg-neutral-subtle`, `--bh-border-focus`, `--bh-border-subtle`, `--bh-border-width-default`, `--bh-border-width-strong`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-muted`, `--bh-content-on-brand`, `--bh-content-subtle`, `--bh-control-sm`, `--bh-icon-stroke-200`, `--bh-interactive-ghost-active`, `--bh-interactive-ghost-default`, `--bh-interactive-ghost-hover`, `--bh-opacity-0`, `--bh-opacity-100`, `--bh-radius-full`, `--bh-radius-lg-8`, `--bh-radius-md-6`, `--bh-sidebar-active-guide-height`, `--bh-sidebar-bg`, `--bh-sidebar-bg-collapsed`, `--bh-sidebar-border`, `--bh-sidebar-brand-height`, `--bh-sidebar-compact-control-size`, `--bh-sidebar-header-action-size`, `--bh-sidebar-icon-size`, `--bh-sidebar-icon-size-collapsed`, `--bh-sidebar-icon-slot-size`, `--bh-sidebar-item-bg-active`, `--bh-sidebar-item-bg-hover`, `--bh-sidebar-item-content`, `--bh-sidebar-item-content-active`, `--bh-sidebar-item-height`, `--bh-sidebar-item-height-collapsed`, `--bh-sidebar-item-padding-x`, `--bh-sidebar-level-2-indent-width`, `--bh-sidebar-padding`, `--bh-sidebar-rail-width`, `--bh-sidebar-tree-indent-step`, `--bh-sidebar-tree-item-height`, `--bh-sidebar-width`, `--bh-sidebar-width-collapsed`, `--bh-sidebar-workspace-card-height`, `--bh-sidebar-workspace-trigger-max-width`, `--bh-sidebar-workspace-trigger-width`, `--bh-space-19xl-384`, `--bh-space-3xl-16`, `--bh-space-4xl-20`, `--bh-space-5xl-24`, `--bh-space-6xl-32`, `--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-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-semibold-font-size`, `--bh-text-body-3xs-semibold-font-weight`, `--bh-text-body-3xs-semibold-letter-spacing`, `--bh-text-body-3xs-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-letter-spacing`, `--bh-text-body-sm-regular-line-height`, `--bh-text-body-xl-semibold-font-size`, `--bh-text-body-xl-semibold-font-weight`, `--bh-text-body-xl-semibold-letter-spacing`, `--bh-text-body-xl-semibold-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-text-body-xs-semibold-font-weight`, `--shadow-button-focus` |
|
|
124
|
+
| `variants` | `default menu`, `avatar menu`, `Level 2 menu`, `Level 2 line menu`, `workspace actions`, `workspace card`, `default footer`, `supporting-text footer` |
|
|
85
125
|
<!-- registry-contract:end -->
|
|
@@ -425,5 +425,5 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
425
425
|
| `modes` | `table`, `data-table` |
|
|
426
426
|
| `sizes` | `sm`, `lg` |
|
|
427
427
|
| `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-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`, `--shadow-component-focus-ring` |
|
|
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`, `--shadow-component-focus-ring` |
|
|
429
429
|
<!-- registry-contract:end -->
|
|
@@ -47,9 +47,9 @@ The component consumes semantic design-system tokens only:
|
|
|
47
47
|
- `md` tags are `28px` tall and use body-sm medium text.
|
|
48
48
|
- `lg` tags are `36px` tall and use body-md medium text.
|
|
49
49
|
- Simple tags use centered text and no leading slot.
|
|
50
|
-
- Dot, flag, avatar, and icon tags use a `20px` leading slot
|
|
50
|
+
- Dot, flag, avatar, and icon tags use a `20px` leading slot; the default icon glyph is `18px`.
|
|
51
51
|
- Close buttons are `16px` with a `12px` icon.
|
|
52
|
-
- Focused tags use a brand border plus
|
|
52
|
+
- Focused tags use a brand border plus a solid outside focus ring.
|
|
53
53
|
- Active tags use selected/brand treatment and a stronger brand border.
|
|
54
54
|
- Disabled tags use disabled background, border, and content tokens.
|
|
55
55
|
- RTL uses logical inline padding and inherited direction so leading media, label, and close button mirror naturally.
|
|
@@ -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-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`, `--shadow-button-focus` |
|
|
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`, `--shadow-button-focus` |
|
|
94
94
|
| `types` | `simple`, `dot`, `flag`, `avatar`, `icon` |
|
|
95
95
|
<!-- registry-contract:end -->
|
|
@@ -101,5 +101,5 @@ This table is generated from `registry/index.json`; edit the component-owned sou
|
|
|
101
101
|
| `densities` | `compact`, `default`, `comfortable` |
|
|
102
102
|
| `layouts` | `inline`, `split`, `stack`, `wrap` |
|
|
103
103
|
| `styles` | `filter-fields`, `filter-actions`, `selection-actions`, `grouped-fields`, `field-grid`, `inline-search`, `filter-chips`, `saved-segment` |
|
|
104
|
-
| `tokens` | `--bh-bg-brand-subtle`, `--bh-bg-default-hover`, `--bh-bg-neutral-subtle`, `--bh-border-brand-strong`, `--bh-border-danger-strong`, `--bh-border-default`, `--bh-border-disabled`, `--bh-border-width-default`, `--bh-button-gap`, `--bh-button-gap-xs`, `--bh-button-icon-offset`, `--bh-button-icon-size`, `--bh-button-label-padding-x`, `--bh-button-md-height`, `--bh-button-md-padding-x`, `--bh-button-sm-height`, `--bh-button-sm-padding-x`, `--bh-button-sm-padding-y`, `--bh-button-xs-height`, `--bh-button-xs-padding-x`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-muted`, `--bh-content-on-brand`, `--bh-content-subtle`, `--bh-control-default`, `--bh-input-comfortable-height`, `--bh-input-comfortable-padding-x`, `--bh-input-content-gap`, `--bh-input-lg-height`, `--bh-input-lg-padding-x`, `--bh-input-md-height`, `--bh-input-md-padding-x`, `--bh-input-radius`, `--bh-input-width`, `--bh-interactive-brand-default`, `--bh-interactive-brand-disabled`, `--bh-interactive-brand-hover`, `--bh-interactive-brand-pressed`, `--bh-interactive-ghost-default`, `--bh-interactive-ghost-disabled`, `--bh-interactive-ghost-hover`, `--bh-interactive-ghost-pressed`, `--bh-interactive-input-default`, `--bh-interactive-input-disabled`, `--bh-interactive-outlined-default`, `--bh-interactive-outlined-disabled`, `--bh-interactive-outlined-hover`, `--bh-interactive-outlined-pressed`, `--bh-interactive-soft-default`, `--bh-interactive-soft-disabled`, `--bh-interactive-soft-hover`, `--bh-interactive-soft-pressed`, `--bh-radius-full`, `--bh-select-icon-size`, `--bh-select-icon-slot-size`, `--bh-select-trigger-padding-x`, `--bh-select-width`, `--bh-space-19xl-384`, `--bh-space-3xl-16`, `--bh-space-5xl-24`, `--bh-space-md-8`, `--bh-space-none`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--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-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-button-font-size`, `--bh-text-button-font-weight`, `--bh-text-button-letter-spacing`, `--bh-text-button-line-height`, `--shadow-button-danger-focus`, `--shadow-button-focus`, `--shadow-button-raised`, `--shadow-button-soft`, `--shadow-input`, `--shadow-input-focus-ring`, `--shadow-select-trigger` |
|
|
104
|
+
| `tokens` | `--bh-bg-brand-subtle`, `--bh-bg-default-hover`, `--bh-bg-neutral-subtle`, `--bh-border-brand-strong`, `--bh-border-danger-strong`, `--bh-border-default`, `--bh-border-disabled`, `--bh-border-width-default`, `--bh-button-gap`, `--bh-button-gap-xs`, `--bh-button-icon-offset`, `--bh-button-icon-size`, `--bh-button-icon-slot-size`, `--bh-button-label-padding-x`, `--bh-button-md-height`, `--bh-button-md-padding-x`, `--bh-button-sm-height`, `--bh-button-sm-padding-x`, `--bh-button-sm-padding-y`, `--bh-button-xs-height`, `--bh-button-xs-padding-x`, `--bh-content-brand-default`, `--bh-content-default`, `--bh-content-disabled`, `--bh-content-muted`, `--bh-content-on-brand`, `--bh-content-subtle`, `--bh-control-default`, `--bh-input-comfortable-height`, `--bh-input-comfortable-padding-x`, `--bh-input-content-gap`, `--bh-input-lg-height`, `--bh-input-lg-padding-x`, `--bh-input-md-height`, `--bh-input-md-padding-x`, `--bh-input-radius`, `--bh-input-width`, `--bh-interactive-brand-default`, `--bh-interactive-brand-disabled`, `--bh-interactive-brand-hover`, `--bh-interactive-brand-pressed`, `--bh-interactive-ghost-default`, `--bh-interactive-ghost-disabled`, `--bh-interactive-ghost-hover`, `--bh-interactive-ghost-pressed`, `--bh-interactive-input-default`, `--bh-interactive-input-disabled`, `--bh-interactive-outlined-default`, `--bh-interactive-outlined-disabled`, `--bh-interactive-outlined-hover`, `--bh-interactive-outlined-pressed`, `--bh-interactive-soft-default`, `--bh-interactive-soft-disabled`, `--bh-interactive-soft-hover`, `--bh-interactive-soft-pressed`, `--bh-radius-full`, `--bh-select-icon-size`, `--bh-select-icon-slot-size`, `--bh-select-trigger-padding-x`, `--bh-select-width`, `--bh-space-19xl-384`, `--bh-space-3xl-16`, `--bh-space-5xl-24`, `--bh-space-md-8`, `--bh-space-none`, `--bh-space-xl-12`, `--bh-space-xs-4`, `--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-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-button-font-size`, `--bh-text-button-font-weight`, `--bh-text-button-letter-spacing`, `--bh-text-button-line-height`, `--shadow-button-danger-focus`, `--shadow-button-focus`, `--shadow-button-raised`, `--shadow-button-soft`, `--shadow-input`, `--shadow-input-focus-ring`, `--shadow-select-trigger` |
|
|
105
105
|
<!-- registry-contract:end -->
|
|
@@ -38,20 +38,24 @@ not silently resize navigation, cards, typography, or unrelated layout spacing.
|
|
|
38
38
|
|
|
39
39
|
Elevation uses two layers:
|
|
40
40
|
|
|
41
|
-
- General steps: `--shadow-xs` through `--shadow-
|
|
41
|
+
- General steps: `--shadow-xs` through `--shadow-3xl`.
|
|
42
42
|
- Semantic component aliases: `--shadow-component-*`, focus/danger rings, menus, controls, and component-specific aliases.
|
|
43
43
|
|
|
44
|
-
The
|
|
45
|
-
`
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
44
|
+
The seven general steps mirror the current Figma `elevation/01` through
|
|
45
|
+
`elevation/07` effect styles in [Shadow tests → With Variables](https://www.figma.com/design/LVAWtr5A5oEcAvsBmk6vdt/Banhaten-Design-system-3.0?node-id=6436-13190).
|
|
46
|
+
The geometry is shared across modes. Figma's bound shadow color variables make
|
|
47
|
+
the dark-mode drops, rings, and highlights stronger, so Banhaten publishes a
|
|
48
|
+
separate exact recipe for each mode.
|
|
49
|
+
|
|
50
|
+
| CSS step | Figma style | Current component roles |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| `xs` | `elevation/01` | Flat surfaces with an ambient ring only |
|
|
53
|
+
| `sm` | `elevation/02` | Subtle lift for inner cards, inset panels, and compact raised details |
|
|
54
|
+
| `md` | `elevation/03` | Cards, surfaces, slider thumbs, and toggle handles |
|
|
55
|
+
| `lg` | `elevation/04` | Menus, select menus, popovers, tooltips, and date surfaces |
|
|
56
|
+
| `xl` | `elevation/05` | Sticky and floating surfaces, including floating banners |
|
|
57
|
+
| `2xl` | `elevation/06` | Modals and dialogs |
|
|
58
|
+
| `3xl` | `elevation/07` | Full-overlay surfaces that require maximum depth |
|
|
55
59
|
|
|
56
60
|
Components consume the semantic alias that describes purpose rather than
|
|
57
61
|
selecting a visual step directly. Focus rings, inset borders, and the Figma
|
|
@@ -40,13 +40,18 @@ Keep `Simple Icons` as a separate brand-logo source. It should not be the primar
|
|
|
40
40
|
|
|
41
41
|
## Decision Criteria
|
|
42
42
|
|
|
43
|
-
-
|
|
43
|
+
- Default UI glyph clarity at 18px, with the glyph centered in a 20px layout slot.
|
|
44
|
+
- Small-control icon clarity at 16px without promoting those intentionally compact glyphs to the default size.
|
|
44
45
|
- Menu, dropdown, and table icon legibility in dense rows.
|
|
45
46
|
- Visual match with `default`, `rounded`, and `sharp` appearances.
|
|
46
47
|
- Variant usefulness: filled states, two-tone states, sharp geometry, or rounded geometry.
|
|
47
48
|
- Coverage for navigation, data, files, users, settings, alerts, billing, integrations, and brand logos.
|
|
48
49
|
- Licensing and attribution obligations. Keep source license notices in the project when redistributing SVG/source files.
|
|
49
50
|
|
|
51
|
+
## Default Geometry Contract
|
|
52
|
+
|
|
53
|
+
Default UI icons use `--bh-icon-size-default` (`18px`) inside `--bh-icon-slot-size-default` (`20px`). The resulting `--bh-icon-slot-inset-default` is `1px` on each side. Components must preserve that slot so switching from the former 20px glyph does not change alignment, gaps, or control dimensions. Intentional 12px, 16px, 24px, brand-logo, avatar, spinner, and control-indicator sizes remain component-specific.
|
|
54
|
+
|
|
50
55
|
## Sources
|
|
51
56
|
|
|
52
57
|
- Google Material Icons repository: https://github.com/google/material-design-icons
|
|
@@ -53,7 +53,7 @@ Without Vite, the CLI installs Tailwind and writes the configured global stylesh
|
|
|
53
53
|
The generated stylesheet imports Inter weights 400–700 and IBM Plex Sans Arabic weights 400–700. Set document language and direction at the application boundary:
|
|
54
54
|
|
|
55
55
|
```tsx
|
|
56
|
-
<html lang="ar" dir="rtl" data-theme="
|
|
56
|
+
<html lang="ar" dir="rtl" data-theme="gray" data-radius="default">
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
Wrap server-rendered component trees in `DirectionProvider` when request data already determines the direction; this keeps the first server and client snapshots identical. It is also the boundary for nested surfaces that differ from the document. Without it, components hydrate from a stable LTR snapshot and then synchronize from the document through one shared observer.
|
|
@@ -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
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
`cssContract.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
@@ -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-
|
|
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: {
|
|
@@ -159,7 +159,7 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
|
|
|
159
159
|
>
|
|
160
160
|
<span
|
|
161
161
|
data-slot="alert-icon"
|
|
162
|
-
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)]"
|
|
163
163
|
>
|
|
164
164
|
<AlertStatusIcon status={resolvedStatus} />
|
|
165
165
|
</span>
|
|
@@ -216,7 +216,7 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
|
|
|
216
216
|
<Button
|
|
217
217
|
data-slot="alert-close"
|
|
218
218
|
aria-label={resolvedCloseLabel}
|
|
219
|
-
className="size-[var(--bh-alert-icon-size)] shrink-0 rounded-[var(--bh-radius-full)] p-0 text-[var(--bh-content-subtle)]"
|
|
219
|
+
className="size-[var(--bh-alert-icon-slot-size)] shrink-0 rounded-[var(--bh-radius-full)] p-0 text-[var(--bh-content-subtle)]"
|
|
220
220
|
onClick={onClose}
|
|
221
221
|
size="icon-xs"
|
|
222
222
|
type="button"
|
|
@@ -277,7 +277,7 @@ function AlertStatusIcon({ status }: { status: AlertStatus }) {
|
|
|
277
277
|
|
|
278
278
|
function CloseIcon() {
|
|
279
279
|
return (
|
|
280
|
-
<XIcon aria-hidden="true" className="size-[var(--bh-
|
|
280
|
+
<XIcon aria-hidden="true" className="size-[var(--bh-space-3xl-16)]" strokeWidth="var(--bh-icon-stroke-225)" />
|
|
281
281
|
)
|
|
282
282
|
}
|
|
283
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)]
|
|
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
|
-
|
|
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(
|
|
@@ -212,13 +212,17 @@ function AvatarIcon({
|
|
|
212
212
|
className={cn(avatarIconVariants({ size, className }))}
|
|
213
213
|
{...props}
|
|
214
214
|
>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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>
|
|
222
226
|
</span>
|
|
223
227
|
)
|
|
224
228
|
}
|
|
@@ -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: {
|
|
@@ -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: {
|
|
@@ -224,7 +224,7 @@ const monthButton = cva(
|
|
|
224
224
|
"flex size-[var(--bh-date-nav-button-size)] shrink-0 items-center justify-center rounded-[var(--bh-control-default)]",
|
|
225
225
|
"bg-[var(--bh-interactive-soft-default)] text-[var(--bh-content-default)] outline-none transition-[background-color,box-shadow,color]",
|
|
226
226
|
"hover:bg-[var(--bh-interactive-soft-hover)] focus-visible:shadow-[var(--shadow-button-focus)] disabled:opacity-[var(--bh-opacity-0)]",
|
|
227
|
-
"[&
|
|
227
|
+
"[&_[data-slot=date-picker-nav-icon]>svg]:size-[var(--bh-date-nav-icon-size)]",
|
|
228
228
|
]
|
|
229
229
|
)
|
|
230
230
|
|
|
@@ -764,7 +764,7 @@ function DatePickerTrigger({
|
|
|
764
764
|
<span
|
|
765
765
|
aria-hidden="true"
|
|
766
766
|
data-slot="date-picker-trigger-icon"
|
|
767
|
-
className="flex size-[var(--bh-date-trigger-icon-size)] shrink-0 items-center justify-center text-[var(--bh-content-muted)] group-disabled/date-trigger:text-[var(--bh-content-disabled)] [&_svg:not([class*='size-'])]:size-[var(--bh-date-trigger-icon-size)]"
|
|
767
|
+
className="flex size-[var(--bh-date-trigger-icon-slot-size)] shrink-0 items-center justify-center text-[var(--bh-content-muted)] group-disabled/date-trigger:text-[var(--bh-content-disabled)] [&_svg:not([class*='size-'])]:size-[var(--bh-date-trigger-icon-size)]"
|
|
768
768
|
>
|
|
769
769
|
{icon ?? <CalendarDaysIcon strokeWidth="var(--bh-icon-stroke-180)" />}
|
|
770
770
|
</span>
|
|
@@ -873,7 +873,13 @@ function CalendarMonth({
|
|
|
873
873
|
className={monthButton()}
|
|
874
874
|
onClick={() => onDisplayMonthChange(addMonths(displayMonth, -1))}
|
|
875
875
|
>
|
|
876
|
-
<
|
|
876
|
+
<span
|
|
877
|
+
aria-hidden="true"
|
|
878
|
+
data-slot="date-picker-nav-icon"
|
|
879
|
+
className="inline-flex size-[var(--bh-date-nav-icon-slot-size)] items-center justify-center"
|
|
880
|
+
>
|
|
881
|
+
<ChevronLeftIcon data-rtl-flip="true" />
|
|
882
|
+
</span>
|
|
877
883
|
</button>
|
|
878
884
|
|
|
879
885
|
{hasMonthToggle ? (
|
|
@@ -912,7 +918,13 @@ function CalendarMonth({
|
|
|
912
918
|
className={monthButton()}
|
|
913
919
|
onClick={() => onDisplayMonthChange(addMonths(displayMonth, 1))}
|
|
914
920
|
>
|
|
915
|
-
<
|
|
921
|
+
<span
|
|
922
|
+
aria-hidden="true"
|
|
923
|
+
data-slot="date-picker-nav-icon"
|
|
924
|
+
className="inline-flex size-[var(--bh-date-nav-icon-slot-size)] items-center justify-center"
|
|
925
|
+
>
|
|
926
|
+
<ChevronRightIcon data-rtl-flip="true" />
|
|
927
|
+
</span>
|
|
916
928
|
</button>
|
|
917
929
|
</div>
|
|
918
930
|
|
|
@@ -50,10 +50,11 @@ export function OnboardingStepListItem({
|
|
|
50
50
|
|
|
51
51
|
function CheckCircleIcon() {
|
|
52
52
|
return (
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
<span className="ds-onboarding-step__check-slot" aria-hidden="true">
|
|
54
|
+
<CircleCheckIcon
|
|
55
|
+
className="ds-onboarding-step__check"
|
|
56
|
+
strokeWidth="var(--bh-icon-stroke-225)"
|
|
57
|
+
/>
|
|
58
|
+
</span>
|
|
58
59
|
);
|
|
59
60
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
--banner-height-default: calc(var(--bh-space-8xl-48) + var(--bh-space-xl-12));
|
|
18
18
|
--banner-height-title: calc(var(--bh-space-8xl-48) + var(--bh-space-xs-4));
|
|
19
19
|
--banner-input-width: var(--bh-input-width);
|
|
20
|
-
--banner-shadow-floating: var(--shadow-
|
|
20
|
+
--banner-shadow-floating: var(--shadow-xl);
|
|
21
21
|
|
|
22
22
|
box-sizing: border-box;
|
|
23
23
|
position: relative;
|
|
@@ -148,10 +148,15 @@
|
|
|
148
148
|
color: var(--banner-icon-fg);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
.ds-banner__icon-wrap
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
.ds-banner__icon-wrap {
|
|
152
|
+
width: var(--bh-icon-slot-size-default);
|
|
153
|
+
height: var(--bh-icon-slot-size-default);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.ds-banner__icon,
|
|
157
|
+
.ds-banner__icon-wrap > svg {
|
|
158
|
+
width: var(--bh-icon-size-default);
|
|
159
|
+
height: var(--bh-icon-size-default);
|
|
155
160
|
}
|
|
156
161
|
|
|
157
162
|
.ds-banner__icon,
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
.ds-breadcrumbs__icon {
|
|
76
|
-
width: var(--bh-
|
|
77
|
-
height: var(--bh-
|
|
76
|
+
width: var(--bh-icon-slot-size-default);
|
|
77
|
+
height: var(--bh-icon-slot-size-default);
|
|
78
78
|
flex: 0 0 auto;
|
|
79
79
|
display: inline-grid;
|
|
80
80
|
place-items: center;
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
.ds-breadcrumbs__icon svg {
|
|
89
|
-
width: var(--bh-
|
|
90
|
-
height: var(--bh-
|
|
89
|
+
width: var(--bh-icon-size-default);
|
|
90
|
+
height: var(--bh-icon-size-default);
|
|
91
91
|
display: block;
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
--ds-file-upload-row-border: var(--bh-border-danger-strong);
|
|
196
196
|
--ds-file-upload-row-shadow:
|
|
197
197
|
inset 0px 0px 0px var(--bh-border-width-default) var(--ds-file-upload-row-border),
|
|
198
|
-
var(--shadow-
|
|
198
|
+
var(--shadow-sm);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
.ds-file-upload-row__content {
|
|
@@ -442,8 +442,8 @@
|
|
|
442
442
|
}
|
|
443
443
|
|
|
444
444
|
.ds-file-upload-row__action svg {
|
|
445
|
-
width: var(--bh-
|
|
446
|
-
height: var(--bh-
|
|
445
|
+
width: var(--bh-icon-size-default);
|
|
446
|
+
height: var(--bh-icon-size-default);
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
.ds-file-upload:dir(rtl) {
|