base-themes 0.1.2 → 0.1.3

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 (262) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/CODE_OF_CONDUCT.md +22 -0
  3. package/CONTRIBUTING.md +98 -0
  4. package/LICENSE +21 -0
  5. package/README.md +316 -3
  6. package/RELEASE.md +80 -0
  7. package/SECURITY.md +49 -0
  8. package/bin/base-themes.mjs +143 -0
  9. package/dist/base-themes.css +1 -1
  10. package/dist/base-themes.js +857 -302
  11. package/dist/llms-full.txt +288 -0
  12. package/dist/llms.txt +79 -0
  13. package/dist/types/blocks/AuthCard.d.ts +2 -0
  14. package/dist/types/blocks/CommandPaletteBlock.d.ts +2 -0
  15. package/dist/types/blocks/DashboardShell.d.ts +2 -0
  16. package/dist/types/blocks/DataTableBlock.d.ts +2 -0
  17. package/dist/types/blocks/PricingPanel.d.ts +2 -0
  18. package/dist/types/blocks/SettingsForm.d.ts +2 -0
  19. package/dist/types/blocks/TeamActivityFeed.d.ts +2 -0
  20. package/dist/types/blocks/ThemeShowcaseCard.d.ts +2 -0
  21. package/dist/types/blocks/index.d.ts +8 -0
  22. package/dist/types/components/ui/Input.d.ts +3 -0
  23. package/dist/types/components/ui/index.d.ts +1 -1
  24. package/dist/types/lib.d.ts +1 -0
  25. package/docs/adoption-dashboard.md +149 -0
  26. package/docs/analytics-setup.md +145 -0
  27. package/docs/community-gallery-proposal.md +64 -0
  28. package/docs/community-proof-telemetry.md +47 -0
  29. package/docs/contributor-issue-seeds.md +240 -0
  30. package/docs/registry-access-telemetry.md +87 -0
  31. package/docs/release-announcement-kit.md +229 -0
  32. package/docs/search-console-setup.md +111 -0
  33. package/docs/theme-token-contract.md +113 -0
  34. package/examples/dashboard/README.md +24 -0
  35. package/examples/dashboard/index.html +12 -0
  36. package/examples/dashboard/package-lock.json +1212 -0
  37. package/examples/dashboard/package.json +24 -0
  38. package/examples/dashboard/src/App.tsx +115 -0
  39. package/examples/dashboard/src/main.tsx +11 -0
  40. package/examples/dashboard/src/styles.css +129 -0
  41. package/examples/dashboard/src/vite-env.d.ts +4 -0
  42. package/examples/dashboard/tsconfig.app.json +23 -0
  43. package/examples/dashboard/tsconfig.json +7 -0
  44. package/examples/dashboard/tsconfig.node.json +15 -0
  45. package/examples/dashboard/vite.config.ts +6 -0
  46. package/examples/next/README.md +29 -0
  47. package/examples/next/app/base-themes-demo.tsx +70 -0
  48. package/examples/next/app/layout.tsx +16 -0
  49. package/examples/next/app/page.tsx +9 -0
  50. package/examples/next/app/styles.css +106 -0
  51. package/examples/next/next-env.d.ts +6 -0
  52. package/examples/next/next.config.ts +5 -0
  53. package/examples/next/package-lock.json +1199 -0
  54. package/examples/next/package.json +27 -0
  55. package/examples/next/tsconfig.json +36 -0
  56. package/examples/registry-copy/README.md +73 -0
  57. package/examples/registry-copy/plan-copy.mjs +130 -0
  58. package/examples/theme-customization/README.md +26 -0
  59. package/examples/theme-customization/index.html +12 -0
  60. package/examples/theme-customization/package-lock.json +1212 -0
  61. package/examples/theme-customization/package.json +24 -0
  62. package/examples/theme-customization/src/App.tsx +138 -0
  63. package/examples/theme-customization/src/main.tsx +11 -0
  64. package/examples/theme-customization/src/styles.css +138 -0
  65. package/examples/theme-customization/src/vite-env.d.ts +4 -0
  66. package/examples/theme-customization/tsconfig.app.json +23 -0
  67. package/examples/theme-customization/tsconfig.json +7 -0
  68. package/examples/theme-customization/tsconfig.node.json +15 -0
  69. package/examples/theme-customization/vite.config.ts +6 -0
  70. package/examples/vite/README.md +32 -0
  71. package/examples/vite/index.html +12 -0
  72. package/examples/vite/package-lock.json +1200 -0
  73. package/examples/vite/package.json +24 -0
  74. package/examples/vite/src/App.tsx +101 -0
  75. package/examples/vite/src/main.tsx +11 -0
  76. package/examples/vite/src/styles.css +125 -0
  77. package/examples/vite/src/vite-env.d.ts +4 -0
  78. package/examples/vite/tsconfig.app.json +23 -0
  79. package/examples/vite/tsconfig.json +7 -0
  80. package/examples/vite/tsconfig.node.json +15 -0
  81. package/examples/vite/vite.config.ts +6 -0
  82. package/llms-full.txt +288 -0
  83. package/llms.txt +79 -0
  84. package/package.json +157 -14
  85. package/registry/items/accordion.json +101 -0
  86. package/registry/items/alert-dialog.json +107 -0
  87. package/registry/items/autocomplete.json +106 -0
  88. package/registry/items/avatar.json +101 -0
  89. package/registry/items/block-auth-card.json +105 -0
  90. package/registry/items/block-command-palette.json +99 -0
  91. package/registry/items/block-dashboard-shell.json +101 -0
  92. package/registry/items/block-data-table.json +99 -0
  93. package/registry/items/block-pricing-panel.json +99 -0
  94. package/registry/items/block-settings-form.json +107 -0
  95. package/registry/items/block-team-activity-feed.json +99 -0
  96. package/registry/items/block-theme-showcase-card.json +99 -0
  97. package/registry/items/button.json +102 -0
  98. package/registry/items/checkbox-group.json +106 -0
  99. package/registry/items/checkbox.json +102 -0
  100. package/registry/items/collapsible.json +101 -0
  101. package/registry/items/combobox.json +101 -0
  102. package/registry/items/context-menu.json +106 -0
  103. package/registry/items/csp-provider.json +96 -0
  104. package/registry/items/dialog.json +102 -0
  105. package/registry/items/direction-provider.json +101 -0
  106. package/registry/items/drawer.json +101 -0
  107. package/registry/items/field.json +101 -0
  108. package/registry/items/fieldset.json +101 -0
  109. package/registry/items/form.json +101 -0
  110. package/registry/items/input.json +102 -0
  111. package/registry/items/menu.json +101 -0
  112. package/registry/items/menubar.json +106 -0
  113. package/registry/items/meter.json +101 -0
  114. package/registry/items/navigation-menu.json +101 -0
  115. package/registry/items/number-field.json +101 -0
  116. package/registry/items/otp-field.json +101 -0
  117. package/registry/items/popover.json +102 -0
  118. package/registry/items/preview-card.json +101 -0
  119. package/registry/items/progress.json +101 -0
  120. package/registry/items/radio-group.json +102 -0
  121. package/registry/items/radio.json +101 -0
  122. package/registry/items/scroll-area.json +101 -0
  123. package/registry/items/select.json +102 -0
  124. package/registry/items/separator.json +101 -0
  125. package/registry/items/slider.json +102 -0
  126. package/registry/items/switch.json +102 -0
  127. package/registry/items/tabs.json +101 -0
  128. package/registry/items/theme-bauhaus.json +107 -0
  129. package/registry/items/theme-bento.json +107 -0
  130. package/registry/items/theme-calm.json +107 -0
  131. package/registry/items/theme-cyberpunk.json +108 -0
  132. package/registry/items/theme-data-dense.json +107 -0
  133. package/registry/items/theme-editorial.json +107 -0
  134. package/registry/items/theme-enterprise.json +108 -0
  135. package/registry/items/theme-fluent.json +107 -0
  136. package/registry/items/theme-glass.json +107 -0
  137. package/registry/items/theme-linear.json +107 -0
  138. package/registry/items/theme-luxury.json +107 -0
  139. package/registry/items/theme-material.json +107 -0
  140. package/registry/items/theme-minimal.json +107 -0
  141. package/registry/items/theme-mono.json +107 -0
  142. package/registry/items/theme-neo-brutalism.json +107 -0
  143. package/registry/items/theme-playful.json +107 -0
  144. package/registry/items/theme-retro.json +107 -0
  145. package/registry/items/theme-shadcn.json +107 -0
  146. package/registry/items/theme-soft-ui.json +107 -0
  147. package/registry/items/theme-terminal.json +107 -0
  148. package/registry/items/toast.json +106 -0
  149. package/registry/items/toggle-group.json +101 -0
  150. package/registry/items/toggle.json +101 -0
  151. package/registry/items/toolbar.json +101 -0
  152. package/registry/items/tooltip.json +102 -0
  153. package/registry/registry.json +564 -49
  154. package/registry/shadcn-registry.json +415 -0
  155. package/research/telemetry-fixtures/analytics-events.jsonl +9 -0
  156. package/research/telemetry-fixtures/bundle-report.json +44 -0
  157. package/research/telemetry-fixtures/community-proof.csv +5 -0
  158. package/research/telemetry-fixtures/registry-access.jsonl +10 -0
  159. package/research/telemetry-fixtures/search-console-export.csv +4 -0
  160. package/scripts/registry-plan.mjs +434 -0
  161. package/scripts/render-launch-actions.mjs +405 -0
  162. package/scripts/render-launch-status.mjs +373 -0
  163. package/scripts/render-release-announcement.mjs +329 -0
  164. package/scripts/verify-launch-readiness.mjs +415 -0
  165. package/scripts/verify-telemetry-fixtures.mjs +85 -0
  166. package/scripts/verify-telemetry-report.mjs +89 -0
  167. package/skills/base-themes/SKILL.md +151 -47
  168. package/src/blocks/AuthCard.tsx +29 -0
  169. package/src/blocks/Blocks.css +182 -0
  170. package/src/blocks/CommandPaletteBlock.tsx +32 -0
  171. package/src/blocks/DashboardShell.tsx +36 -0
  172. package/src/blocks/DataTableBlock.tsx +44 -0
  173. package/src/blocks/PricingPanel.tsx +28 -0
  174. package/src/blocks/SettingsForm.tsx +37 -0
  175. package/src/blocks/TeamActivityFeed.tsx +38 -0
  176. package/src/blocks/ThemeShowcaseCard.tsx +32 -0
  177. package/src/blocks/index.ts +8 -0
  178. package/src/components/ui/Accordion.css +42 -0
  179. package/src/components/ui/Accordion.tsx +41 -0
  180. package/src/components/ui/AlertDialog.css +40 -0
  181. package/src/components/ui/AlertDialog.tsx +52 -0
  182. package/src/components/ui/Autocomplete.css +3 -0
  183. package/src/components/ui/Autocomplete.tsx +50 -0
  184. package/src/components/ui/Avatar.css +45 -0
  185. package/src/components/ui/Avatar.tsx +36 -0
  186. package/src/components/ui/Button.css +79 -0
  187. package/src/components/ui/Button.tsx +20 -0
  188. package/src/components/ui/Checkbox.css +37 -0
  189. package/src/components/ui/Checkbox.tsx +32 -0
  190. package/src/components/ui/CheckboxGroup.tsx +21 -0
  191. package/src/components/ui/Collapsible.css +34 -0
  192. package/src/components/ui/Collapsible.tsx +29 -0
  193. package/src/components/ui/Combobox.css +75 -0
  194. package/src/components/ui/Combobox.tsx +53 -0
  195. package/src/components/ui/ContextMenu.css +9 -0
  196. package/src/components/ui/ContextMenu.tsx +47 -0
  197. package/src/components/ui/CspProvider.tsx +10 -0
  198. package/src/components/ui/Dialog.css +41 -0
  199. package/src/components/ui/Dialog.tsx +45 -0
  200. package/src/components/ui/DirectionProvider.tsx +17 -0
  201. package/src/components/ui/Drawer.css +77 -0
  202. package/src/components/ui/Drawer.tsx +56 -0
  203. package/src/components/ui/Field.css +19 -0
  204. package/src/components/ui/Field.tsx +24 -0
  205. package/src/components/ui/Fieldset.css +16 -0
  206. package/src/components/ui/Fieldset.tsx +19 -0
  207. package/src/components/ui/Form.css +5 -0
  208. package/src/components/ui/Form.tsx +12 -0
  209. package/src/components/ui/Input.css +50 -0
  210. package/src/components/ui/Input.tsx +62 -0
  211. package/src/components/ui/Menu.css +59 -0
  212. package/src/components/ui/Menu.tsx +50 -0
  213. package/src/components/ui/Menubar.css +26 -0
  214. package/src/components/ui/Menubar.tsx +42 -0
  215. package/src/components/ui/Meter.css +45 -0
  216. package/src/components/ui/Meter.tsx +37 -0
  217. package/src/components/ui/NavigationMenu.css +103 -0
  218. package/src/components/ui/NavigationMenu.tsx +64 -0
  219. package/src/components/ui/NumberField.css +38 -0
  220. package/src/components/ui/NumberField.tsx +28 -0
  221. package/src/components/ui/OtpField.css +28 -0
  222. package/src/components/ui/OtpField.tsx +24 -0
  223. package/src/components/ui/Popover.css +25 -0
  224. package/src/components/ui/Popover.tsx +37 -0
  225. package/src/components/ui/PreviewCard.css +33 -0
  226. package/src/components/ui/PreviewCard.tsx +43 -0
  227. package/src/components/ui/Progress.css +33 -0
  228. package/src/components/ui/Progress.tsx +28 -0
  229. package/src/components/ui/Radio.tsx +22 -0
  230. package/src/components/ui/RadioGroup.css +42 -0
  231. package/src/components/ui/RadioGroup.tsx +29 -0
  232. package/src/components/ui/ScrollArea.css +42 -0
  233. package/src/components/ui/ScrollArea.tsx +22 -0
  234. package/src/components/ui/Select.css +86 -0
  235. package/src/components/ui/Select.tsx +39 -0
  236. package/src/components/ui/Separator.css +14 -0
  237. package/src/components/ui/Separator.tsx +12 -0
  238. package/src/components/ui/Slider.css +39 -0
  239. package/src/components/ui/Slider.tsx +21 -0
  240. package/src/components/ui/Switch.css +45 -0
  241. package/src/components/ui/Switch.tsx +29 -0
  242. package/src/components/ui/Tabs.css +72 -0
  243. package/src/components/ui/Tabs.tsx +44 -0
  244. package/src/components/ui/Toast.css +75 -0
  245. package/src/components/ui/Toast.tsx +48 -0
  246. package/src/components/ui/Toggle.tsx +12 -0
  247. package/src/components/ui/ToggleGroup.css +35 -0
  248. package/src/components/ui/ToggleGroup.tsx +30 -0
  249. package/src/components/ui/Toolbar.css +60 -0
  250. package/src/components/ui/Toolbar.tsx +36 -0
  251. package/src/components/ui/Tooltip.css +14 -0
  252. package/src/components/ui/Tooltip.tsx +31 -0
  253. package/src/components/ui/index.ts +83 -0
  254. package/src/components/ui/useDirection.ts +1 -0
  255. package/src/components/ui/useToastManager.ts +11 -0
  256. package/src/docs/blockMeta.json +66 -0
  257. package/src/docs/componentMeta.json +322 -0
  258. package/src/docs/staticPageMeta.json +143 -0
  259. package/src/docs/themeMeta.json +22 -0
  260. package/src/styles/tokenContract.json +61 -0
  261. package/workers/analytics-receiver.mjs +170 -0
  262. package/wrangler.analytics.jsonc +12 -0
@@ -0,0 +1,59 @@
1
+ .bento-menu-positioner {
2
+ z-index: 45;
3
+ }
4
+
5
+ .bento-menu-popup {
6
+ min-width: 180px;
7
+ padding: 6px;
8
+ border: 1px solid var(--bt-border-strong);
9
+ border-radius: var(--bt-radius);
10
+ background: var(--bt-surface);
11
+ box-shadow: var(--bt-shadow);
12
+ }
13
+
14
+ .bento-menu-item {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 10px;
18
+ min-height: 36px;
19
+ padding: 0 10px;
20
+ border: 0;
21
+ border-radius: var(--bt-radius-sm);
22
+ color: var(--bt-fg);
23
+ background: transparent;
24
+ cursor: pointer;
25
+ font-size: 14px;
26
+ font-weight: var(--bt-font-weight, 680);
27
+ width: 100%;
28
+ }
29
+
30
+ [dir='rtl'] .bento-menu-popup,
31
+ .bento-menu-popup:dir(rtl) {
32
+ text-align: right;
33
+ }
34
+
35
+ [dir='rtl'] .bento-menu-item,
36
+ .bento-menu-item:dir(rtl) {
37
+ flex-direction: row-reverse;
38
+ }
39
+
40
+ .bento-menu-item[data-highlighted] {
41
+ background: var(--bt-surface-muted);
42
+ }
43
+
44
+ .bento-menu-item[data-disabled] {
45
+ color: var(--bt-muted-fg);
46
+ opacity: 0.5;
47
+ cursor: default;
48
+ }
49
+
50
+ .bento-menu-separator {
51
+ height: 1px;
52
+ margin: 4px 8px;
53
+ background: var(--bt-border);
54
+ }
55
+
56
+ [data-theme='dark'] .bento-menu-popup {
57
+ background: var(--bt-select-popup-bg, var(--bt-surface));
58
+ box-shadow: var(--bt-shadow);
59
+ }
@@ -0,0 +1,50 @@
1
+ import { Menu as BaseMenu } from '@base-ui/react/menu'
2
+ import { type ReactElement, type ReactNode } from 'react'
3
+ import clsx from 'clsx'
4
+ import './Menu.css'
5
+
6
+ type MenuItemData = {
7
+ label: string
8
+ icon?: ReactNode
9
+ disabled?: boolean
10
+ onClick?: () => void
11
+ }
12
+
13
+ type MenuProps = {
14
+ trigger: ReactElement
15
+ items: (MenuItemData | 'separator')[]
16
+ className?: string
17
+ }
18
+
19
+ export function Menu({ trigger, items, className }: MenuProps) {
20
+ return (
21
+ <BaseMenu.Root>
22
+ <BaseMenu.Trigger render={trigger} />
23
+ <BaseMenu.Portal>
24
+ <BaseMenu.Positioner className="bento-menu-positioner" sideOffset={8}>
25
+ <BaseMenu.Popup className={clsx('bento-menu-popup', className)}>
26
+ {items.map((item, i) => {
27
+ if (item === 'separator') {
28
+ return <BaseMenu.Separator key={i} className="bento-menu-separator" />
29
+ }
30
+ return (
31
+ <BaseMenu.Item
32
+ key={i}
33
+ className="bento-menu-item"
34
+ disabled={item.disabled}
35
+ onClick={item.onClick}
36
+ >
37
+ {item.icon}
38
+ {item.label}
39
+ </BaseMenu.Item>
40
+ )
41
+ })}
42
+ </BaseMenu.Popup>
43
+ </BaseMenu.Positioner>
44
+ </BaseMenu.Portal>
45
+ </BaseMenu.Root>
46
+ )
47
+ }
48
+
49
+ export { BaseMenu }
50
+ export type { MenuProps, MenuItemData }
@@ -0,0 +1,26 @@
1
+ .bento-menubar {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: 4px;
5
+ padding: 4px;
6
+ border: 1px solid var(--bt-border);
7
+ border-radius: var(--bt-radius);
8
+ background: var(--bt-surface);
9
+ }
10
+
11
+ .bento-menubar-trigger {
12
+ height: 32px;
13
+ padding: 0 10px;
14
+ border: 0;
15
+ border-radius: var(--bt-radius-sm);
16
+ color: var(--bt-text);
17
+ background: transparent;
18
+ cursor: pointer;
19
+ font-weight: 650;
20
+ }
21
+
22
+ .bento-menubar-trigger:hover,
23
+ .bento-menubar-trigger[data-open] {
24
+ color: var(--bt-fg);
25
+ background: var(--bt-surface-muted);
26
+ }
@@ -0,0 +1,42 @@
1
+ import { Menubar as BaseMenubar } from '@base-ui/react/menubar'
2
+ import { Menu as BaseMenu } from '@base-ui/react/menu'
3
+ import { type ReactNode } from 'react'
4
+ import clsx from 'clsx'
5
+ import './Menu.css'
6
+ import './Menubar.css'
7
+
8
+ export type MenubarMenu = {
9
+ label: string
10
+ items: { label: string; icon?: ReactNode; disabled?: boolean }[]
11
+ }
12
+
13
+ export type MenubarProps = {
14
+ menus: MenubarMenu[]
15
+ className?: string
16
+ }
17
+
18
+ export function Menubar({ menus, className }: MenubarProps) {
19
+ return (
20
+ <BaseMenubar className={clsx('bento-menubar', className)}>
21
+ {menus.map((menu) => (
22
+ <BaseMenu.Root key={menu.label}>
23
+ <BaseMenu.Trigger className="bento-menubar-trigger">{menu.label}</BaseMenu.Trigger>
24
+ <BaseMenu.Portal>
25
+ <BaseMenu.Positioner className="bento-menu-positioner" sideOffset={8}>
26
+ <BaseMenu.Popup className="bento-menu-popup">
27
+ {menu.items.map((item) => (
28
+ <BaseMenu.Item key={item.label} className="bento-menu-item" disabled={item.disabled}>
29
+ {item.icon}
30
+ {item.label}
31
+ </BaseMenu.Item>
32
+ ))}
33
+ </BaseMenu.Popup>
34
+ </BaseMenu.Positioner>
35
+ </BaseMenu.Portal>
36
+ </BaseMenu.Root>
37
+ ))}
38
+ </BaseMenubar>
39
+ )
40
+ }
41
+
42
+ export { BaseMenubar }
@@ -0,0 +1,45 @@
1
+ .bento-meter {
2
+ width: 100%;
3
+ }
4
+
5
+ .bento-meter-track {
6
+ position: relative;
7
+ width: 100%;
8
+ height: 12px;
9
+ overflow: hidden;
10
+ border-radius: 999px;
11
+ background: var(--track-bg);
12
+ }
13
+
14
+ .bento-meter-indicator {
15
+ height: 100%;
16
+ border-radius: inherit;
17
+ background: var(--bt-primary);
18
+ transition: width 300ms ease;
19
+ }
20
+
21
+ .bento-meter-high {
22
+ background: var(--bt-success);
23
+ }
24
+
25
+ .bento-meter-mid {
26
+ background: var(--bt-primary);
27
+ }
28
+
29
+ .bento-meter-low {
30
+ background: var(--bt-danger);
31
+ }
32
+
33
+ .bento-meter-label {
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: space-between;
37
+ margin-bottom: 8px;
38
+ color: var(--bt-muted-fg);
39
+ font-size: 13px;
40
+ font-weight: 680;
41
+ }
42
+
43
+ .bento-meter-value {
44
+ color: var(--bt-fg);
45
+ }
@@ -0,0 +1,37 @@
1
+ import { Meter as BaseMeter } from '@base-ui/react/meter'
2
+ import { type ComponentPropsWithoutRef, forwardRef, useMemo } from 'react'
3
+ import clsx from 'clsx'
4
+ import './Meter.css'
5
+
6
+ export type MeterProps = ComponentPropsWithoutRef<typeof BaseMeter.Root> & {
7
+ showValue?: boolean
8
+ }
9
+
10
+ export const Meter = forwardRef<HTMLDivElement, MeterProps>(
11
+ ({ value = 0, min = 0, max = 100, showValue, className, ...rest }, ref) => {
12
+ const pct = ((value - min) / (max - min)) * 100
13
+ const colorClass = useMemo(() => {
14
+ if (pct >= 80) return 'bento-meter-high'
15
+ if (pct < 30) return 'bento-meter-low'
16
+ return 'bento-meter-mid'
17
+ }, [pct])
18
+
19
+ return (
20
+ <div className={clsx('bento-meter', className)}>
21
+ {showValue && (
22
+ <div className="bento-meter-label">
23
+ <span>{rest['aria-label'] ?? 'Meter'}</span>
24
+ <span className="bento-meter-value">{Math.round(pct)}%</span>
25
+ </div>
26
+ )}
27
+ <BaseMeter.Root ref={ref} value={value} min={min} max={max} {...rest}>
28
+ <BaseMeter.Track className="bento-meter-track">
29
+ <BaseMeter.Indicator className={clsx('bento-meter-indicator', colorClass)} />
30
+ </BaseMeter.Track>
31
+ </BaseMeter.Root>
32
+ </div>
33
+ )
34
+ },
35
+ )
36
+
37
+ Meter.displayName = 'Meter'
@@ -0,0 +1,103 @@
1
+ .bento-nav-menu {
2
+ position: relative;
3
+ display: flex;
4
+ align-items: center;
5
+ }
6
+
7
+ .bento-nav-menu-list {
8
+ display: flex;
9
+ align-items: center;
10
+ gap: 2px;
11
+ list-style: none;
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+
16
+ .bento-nav-menu-trigger {
17
+ display: inline-flex;
18
+ align-items: center;
19
+ gap: 6px;
20
+ min-height: 36px;
21
+ padding: 0 12px;
22
+ border: 0;
23
+ border-radius: var(--bt-radius-sm);
24
+ color: var(--bt-fg);
25
+ background: transparent;
26
+ cursor: pointer;
27
+ font-weight: 680;
28
+ font-size: 14px;
29
+ }
30
+
31
+ .bento-nav-menu-trigger:hover {
32
+ background: var(--bt-surface-muted);
33
+ }
34
+
35
+ [dir='rtl'] .bento-nav-menu-list,
36
+ .bento-nav-menu-list:dir(rtl) {
37
+ flex-direction: row-reverse;
38
+ }
39
+
40
+ [dir='rtl'] .bento-nav-menu-trigger,
41
+ .bento-nav-menu-trigger:dir(rtl) {
42
+ flex-direction: row-reverse;
43
+ }
44
+
45
+ .bento-nav-menu-trigger svg {
46
+ transition: transform 140ms ease;
47
+ }
48
+
49
+ .bento-nav-menu-trigger[data-expanded] svg {
50
+ transform: rotate(180deg);
51
+ }
52
+
53
+ .bento-nav-menu-popup {
54
+ min-width: 200px;
55
+ padding: 6px;
56
+ border: 1px solid var(--bt-border-strong);
57
+ border-radius: var(--bt-radius);
58
+ background: var(--bt-surface);
59
+ box-shadow: var(--bt-shadow);
60
+ }
61
+
62
+ .bento-nav-menu-item {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 10px;
66
+ min-height: 36px;
67
+ padding: 0 10px;
68
+ border: 0;
69
+ border-radius: var(--bt-radius-sm);
70
+ color: var(--bt-fg);
71
+ background: transparent;
72
+ cursor: pointer;
73
+ font-size: 14px;
74
+ font-weight: 680;
75
+ width: 100%;
76
+ }
77
+
78
+ [dir='rtl'] .bento-nav-menu-popup,
79
+ .bento-nav-menu-popup:dir(rtl) {
80
+ text-align: right;
81
+ }
82
+
83
+ [dir='rtl'] .bento-nav-menu-item,
84
+ .bento-nav-menu-item:dir(rtl) {
85
+ flex-direction: row-reverse;
86
+ }
87
+
88
+ .bento-nav-menu-item[data-highlighted] {
89
+ background: var(--bt-surface-muted);
90
+ }
91
+
92
+ .bento-nav-menu-viewport {
93
+ position: absolute;
94
+ top: 100%;
95
+ left: 0;
96
+ width: 100%;
97
+ overflow: hidden;
98
+ }
99
+
100
+ [data-theme='dark'] .bento-nav-menu-popup {
101
+ background: var(--bt-select-popup-bg, var(--bt-surface));
102
+ box-shadow: var(--bt-shadow);
103
+ }
@@ -0,0 +1,64 @@
1
+ import { NavigationMenu as BaseNavMenu } from '@base-ui/react/navigation-menu'
2
+ import clsx from 'clsx'
3
+ import { ChevronDown } from 'lucide-react'
4
+ import './NavigationMenu.css'
5
+
6
+ type NavMenuItem = {
7
+ label: string
8
+ href?: string
9
+ children?: { label: string; href: string; description?: string }[]
10
+ }
11
+
12
+ type NavigationMenuProps = {
13
+ items: NavMenuItem[]
14
+ className?: string
15
+ }
16
+
17
+ export function NavigationMenu({ items, className }: NavigationMenuProps) {
18
+ return (
19
+ <BaseNavMenu.Root className={clsx('bento-nav-menu', className)}>
20
+ <BaseNavMenu.List className="bento-nav-menu-list">
21
+ {items.map((item, i) => {
22
+ if (item.children) {
23
+ return (
24
+ <BaseNavMenu.Item key={i}>
25
+ <BaseNavMenu.Trigger className="bento-nav-menu-trigger">
26
+ {item.label}
27
+ <ChevronDown size={14} />
28
+ </BaseNavMenu.Trigger>
29
+ <BaseNavMenu.Portal>
30
+ <BaseNavMenu.Positioner sideOffset={8}>
31
+ <BaseNavMenu.Popup className="bento-nav-menu-popup">
32
+ {item.children.map((child, j) => (
33
+ <BaseNavMenu.Item
34
+ key={j}
35
+ className="bento-nav-menu-item"
36
+ render={<a href={child.href} />}
37
+ >
38
+ {child.label}
39
+ </BaseNavMenu.Item>
40
+ ))}
41
+ </BaseNavMenu.Popup>
42
+ </BaseNavMenu.Positioner>
43
+ </BaseNavMenu.Portal>
44
+ </BaseNavMenu.Item>
45
+ )
46
+ }
47
+ return (
48
+ <BaseNavMenu.Item key={i}>
49
+ <BaseNavMenu.Link
50
+ className="bento-nav-menu-trigger"
51
+ href={item.href}
52
+ render={<a href={item.href} />}
53
+ >
54
+ {item.label}
55
+ </BaseNavMenu.Link>
56
+ </BaseNavMenu.Item>
57
+ )
58
+ })}
59
+ </BaseNavMenu.List>
60
+ </BaseNavMenu.Root>
61
+ )
62
+ }
63
+
64
+ export type { NavigationMenuProps, NavMenuItem }
@@ -0,0 +1,38 @@
1
+ .bento-number-field {
2
+ display: grid;
3
+ gap: 8px;
4
+ width: 220px;
5
+ }
6
+
7
+ .bento-number-field-group {
8
+ display: grid;
9
+ grid-template-columns: 36px 1fr 36px;
10
+ height: 40px;
11
+ overflow: hidden;
12
+ border: 1px solid var(--bt-border);
13
+ border-radius: var(--bt-radius-sm);
14
+ background: var(--bt-surface);
15
+ }
16
+
17
+ .bento-number-field-button,
18
+ .bento-number-field-input {
19
+ border: 0;
20
+ color: var(--bt-fg);
21
+ background: transparent;
22
+ }
23
+
24
+ .bento-number-field-button {
25
+ display: grid;
26
+ place-items: center;
27
+ cursor: pointer;
28
+ }
29
+
30
+ .bento-number-field-button:hover {
31
+ background: var(--bt-surface-muted);
32
+ }
33
+
34
+ .bento-number-field-input {
35
+ min-width: 0;
36
+ text-align: center;
37
+ outline: 0;
38
+ }
@@ -0,0 +1,28 @@
1
+ import { NumberField as BaseNumberField } from '@base-ui/react/number-field'
2
+ import { type ComponentPropsWithoutRef } from 'react'
3
+ import { Minus, Plus } from 'lucide-react'
4
+ import clsx from 'clsx'
5
+ import './NumberField.css'
6
+
7
+ export type NumberFieldProps = ComponentPropsWithoutRef<typeof BaseNumberField.Root> & {
8
+ label?: string
9
+ }
10
+
11
+ export function NumberField({ label, className, ...rest }: NumberFieldProps) {
12
+ return (
13
+ <BaseNumberField.Root className={clsx('bento-number-field', className)} {...rest}>
14
+ {label && <label className="field-label">{label}</label>}
15
+ <BaseNumberField.Group className="bento-number-field-group">
16
+ <BaseNumberField.Decrement className="bento-number-field-button" aria-label="Decrease">
17
+ <Minus size={14} />
18
+ </BaseNumberField.Decrement>
19
+ <BaseNumberField.Input className="bento-number-field-input" />
20
+ <BaseNumberField.Increment className="bento-number-field-button" aria-label="Increase">
21
+ <Plus size={14} />
22
+ </BaseNumberField.Increment>
23
+ </BaseNumberField.Group>
24
+ </BaseNumberField.Root>
25
+ )
26
+ }
27
+
28
+ export { BaseNumberField }
@@ -0,0 +1,28 @@
1
+ .bento-otp-field {
2
+ display: grid;
3
+ gap: 8px;
4
+ }
5
+
6
+ .bento-otp-inputs {
7
+ display: flex;
8
+ flex-wrap: wrap;
9
+ gap: 8px;
10
+ }
11
+
12
+ .bento-otp-input {
13
+ width: 40px;
14
+ height: 44px;
15
+ border: 1px solid var(--bt-border);
16
+ border-radius: var(--bt-radius-sm);
17
+ color: var(--bt-fg);
18
+ background: var(--bt-surface);
19
+ text-align: center;
20
+ font-size: 18px;
21
+ font-weight: 760;
22
+ outline: 0;
23
+ }
24
+
25
+ .bento-otp-input:focus-visible {
26
+ border-color: var(--bt-info);
27
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--bt-primary), transparent 82%);
28
+ }
@@ -0,0 +1,24 @@
1
+ import { OTPFieldPreview as BaseOtpField } from '@base-ui/react/otp-field'
2
+ import { type ComponentPropsWithoutRef } from 'react'
3
+ import clsx from 'clsx'
4
+ import './OtpField.css'
5
+
6
+ export type OtpFieldProps = ComponentPropsWithoutRef<typeof BaseOtpField.Root> & {
7
+ length?: number
8
+ label?: string
9
+ }
10
+
11
+ export function OtpField({ length = 6, label, className, ...rest }: OtpFieldProps) {
12
+ return (
13
+ <BaseOtpField.Root className={clsx('bento-otp-field', className)} length={length} {...rest}>
14
+ {label && <label className="field-label">{label}</label>}
15
+ <div className="bento-otp-inputs">
16
+ {Array.from({ length }, (_, index) => (
17
+ <BaseOtpField.Input key={index} className="bento-otp-input" />
18
+ ))}
19
+ </div>
20
+ </BaseOtpField.Root>
21
+ )
22
+ }
23
+
24
+ export { BaseOtpField }
@@ -0,0 +1,25 @@
1
+ .bento-popover-popup {
2
+ width: 250px;
3
+ padding: 16px;
4
+ border: 1px solid var(--bt-border);
5
+ border-radius: var(--bt-radius);
6
+ background: var(--bt-surface);
7
+ box-shadow: var(--bt-shadow);
8
+ }
9
+
10
+ .bento-popover-arrow {
11
+ fill: var(--bt-select-popup-bg, var(--bt-surface));
12
+ }
13
+
14
+ .bento-popover-title {
15
+ color: var(--bt-fg);
16
+ font-size: 20px;
17
+ font-weight: 780;
18
+ margin: 0;
19
+ }
20
+
21
+ .bento-popover-description {
22
+ margin: 10px 0 0;
23
+ color: var(--bt-muted-fg);
24
+ line-height: 1.6;
25
+ }
@@ -0,0 +1,37 @@
1
+ import { Popover as BasePopover } from '@base-ui/react/popover'
2
+ import { type ReactElement, type ReactNode } from 'react'
3
+ import clsx from 'clsx'
4
+ import './Popover.css'
5
+
6
+ type PopoverProps = {
7
+ trigger: ReactElement
8
+ title?: string
9
+ description?: string
10
+ children?: ReactNode
11
+ className?: string
12
+ }
13
+
14
+ export function Popover({ trigger, title, description, children, className }: PopoverProps) {
15
+ return (
16
+ <BasePopover.Root>
17
+ <BasePopover.Trigger render={trigger} />
18
+ <BasePopover.Portal>
19
+ <BasePopover.Positioner sideOffset={10}>
20
+ <BasePopover.Popup className={clsx('bento-popover-popup', className)}>
21
+ <BasePopover.Arrow className="bento-popover-arrow" />
22
+ {title && <BasePopover.Title className="bento-popover-title">{title}</BasePopover.Title>}
23
+ {description && (
24
+ <BasePopover.Description className="bento-popover-description">
25
+ {description}
26
+ </BasePopover.Description>
27
+ )}
28
+ {children}
29
+ </BasePopover.Popup>
30
+ </BasePopover.Positioner>
31
+ </BasePopover.Portal>
32
+ </BasePopover.Root>
33
+ )
34
+ }
35
+
36
+ export { BasePopover }
37
+ export type { PopoverProps }
@@ -0,0 +1,33 @@
1
+ .bento-preview-card {
2
+ width: 280px;
3
+ padding: 16px;
4
+ border: 1px solid var(--bt-border);
5
+ border-radius: var(--bt-radius);
6
+ background: var(--bt-surface);
7
+ box-shadow: var(--bt-shadow);
8
+ }
9
+
10
+ .bento-preview-card img {
11
+ width: 100%;
12
+ height: 140px;
13
+ object-fit: cover;
14
+ border-radius: var(--bt-radius-sm);
15
+ margin-bottom: 12px;
16
+ }
17
+
18
+ .bento-preview-card-title {
19
+ color: var(--bt-fg);
20
+ font-size: 16px;
21
+ font-weight: 760;
22
+ margin: 0;
23
+ }
24
+
25
+ .bento-preview-card-description {
26
+ margin-top: 6px;
27
+ color: var(--bt-muted-fg);
28
+ font-size: 14px;
29
+ }
30
+
31
+ .bento-preview-card-arrow {
32
+ fill: var(--bt-select-popup-bg, var(--bt-surface));
33
+ }
@@ -0,0 +1,43 @@
1
+ import { PreviewCard as BasePreviewCard } from '@base-ui/react/preview-card'
2
+ import { type ReactElement } from 'react'
3
+ import clsx from 'clsx'
4
+ import './PreviewCard.css'
5
+
6
+ type PreviewCardProps = {
7
+ trigger: ReactElement
8
+ title: string
9
+ description?: string
10
+ imageUrl?: string
11
+ imageAlt?: string
12
+ className?: string
13
+ }
14
+
15
+ export function PreviewCard({
16
+ trigger,
17
+ title,
18
+ description,
19
+ imageUrl,
20
+ imageAlt,
21
+ className,
22
+ }: PreviewCardProps) {
23
+ return (
24
+ <BasePreviewCard.Root>
25
+ <BasePreviewCard.Trigger render={trigger} />
26
+ <BasePreviewCard.Portal>
27
+ <BasePreviewCard.Positioner sideOffset={10}>
28
+ <BasePreviewCard.Popup className={clsx('bento-preview-card', className)}>
29
+ <BasePreviewCard.Arrow className="bento-preview-card-arrow" />
30
+ {imageUrl && <img src={imageUrl} alt={imageAlt ?? ''} />}
31
+ <h3 className="bento-preview-card-title">{title}</h3>
32
+ {description && (
33
+ <p className="bento-preview-card-description">{description}</p>
34
+ )}
35
+ </BasePreviewCard.Popup>
36
+ </BasePreviewCard.Positioner>
37
+ </BasePreviewCard.Portal>
38
+ </BasePreviewCard.Root>
39
+ )
40
+ }
41
+
42
+ export { BasePreviewCard }
43
+ export type { PreviewCardProps }