From 31b53e1afd86e17b435bedb4d04e77f0b3cbe9a5 Mon Sep 17 00:00:00 2001 From: Arjun Mahanti Date: Wed, 9 Sep 2026 08:24:52 -0700 Subject: [PATCH] feat(design-system): add portable open-source component library Signed-off-by: Arjun Mahanti Co-authored-by: Codex --- desktop-next/package.json | 10 +- .../features/design-system/ui/MotionPage.tsx | 3 +- .../features/design-system/ui/RadiusPage.tsx | 3 +- .../features/design-system/ui/SpacingPage.tsx | 3 +- packages/design-tokens/src/foundations.css | 9 + packages/design-tokens/src/foundations.ts | 15 + packages/ui/.gitignore | 1 + packages/ui/README.md | 68 ++++ packages/ui/THIRD_PARTY_NOTICES.md | 332 ++++++++++++++++++ packages/ui/package.json | 46 +++ packages/ui/postcss.config.js | 1 + packages/ui/src/assets.d.ts | 1 + packages/ui/src/button.tsx | 42 +++ packages/ui/src/calendar.tsx | 21 ++ packages/ui/src/carousel.tsx | 56 +++ packages/ui/src/chart.tsx | 53 +++ packages/ui/src/classes.ts | 12 + packages/ui/src/command.tsx | 51 +++ packages/ui/src/index.ts | 47 +++ packages/ui/src/input-group.tsx | 6 + packages/ui/src/input.tsx | 24 ++ packages/ui/src/primitives/accordion.tsx | 64 ++++ packages/ui/src/primitives/alert-dialog.tsx | 57 +++ packages/ui/src/primitives/autocomplete.tsx | 85 +++++ packages/ui/src/primitives/avatar.tsx | 43 +++ packages/ui/src/primitives/checkbox-group.tsx | 10 + packages/ui/src/primitives/checkbox.tsx | 27 ++ packages/ui/src/primitives/collapsible.tsx | 41 +++ packages/ui/src/primitives/combobox.tsx | 179 ++++++++++ packages/ui/src/primitives/context-menu.tsx | 121 +++++++ packages/ui/src/primitives/dialog.tsx | 70 ++++ packages/ui/src/primitives/drawer.tsx | 80 +++++ packages/ui/src/primitives/field.tsx | 61 ++++ packages/ui/src/primitives/fieldset.tsx | 24 ++ packages/ui/src/primitives/form.tsx | 10 + packages/ui/src/primitives/menu.tsx | 152 ++++++++ packages/ui/src/primitives/menubar.tsx | 10 + packages/ui/src/primitives/meter.tsx | 64 ++++ .../ui/src/primitives/navigation-menu.tsx | 105 ++++++ packages/ui/src/primitives/number-field.tsx | 77 ++++ packages/ui/src/primitives/otp-field.tsx | 22 ++ packages/ui/src/primitives/popover.tsx | 57 +++ packages/ui/src/primitives/preview-card.tsx | 45 +++ packages/ui/src/primitives/progress.tsx | 64 ++++ packages/ui/src/primitives/radio-group.tsx | 10 + packages/ui/src/primitives/radio.tsx | 25 ++ packages/ui/src/primitives/scroll-area.tsx | 66 ++++ packages/ui/src/primitives/select.tsx | 124 +++++++ packages/ui/src/primitives/slider.tsx | 90 +++++ packages/ui/src/primitives/switch.tsx | 27 ++ packages/ui/src/primitives/tabs.tsx | 56 +++ packages/ui/src/primitives/toast.tsx | 91 +++++ packages/ui/src/primitives/toggle-group.tsx | 15 + packages/ui/src/primitives/toggle.tsx | 13 + packages/ui/src/primitives/toolbar.tsx | 63 ++++ packages/ui/src/primitives/tooltip.tsx | 34 ++ packages/ui/src/resizable.tsx | 13 + packages/ui/src/sheet.tsx | 8 + packages/ui/src/styles.css | 160 +++++++++ packages/ui/src/styles/actions.css | 148 ++++++++ packages/ui/src/styles/controls.css | 176 ++++++++++ packages/ui/src/styles/layout.css | 150 ++++++++ packages/ui/src/styles/overlays.css | 152 ++++++++ packages/ui/src/styles/surfaces.css | 105 ++++++ packages/ui/src/surfaces.tsx | 114 ++++++ packages/ui/tsconfig.json | 18 + packages/ui/vite.config.ts | 21 ++ pnpm-lock.yaml | 114 +++++- 68 files changed, 4039 insertions(+), 26 deletions(-) create mode 100644 packages/ui/.gitignore create mode 100644 packages/ui/README.md create mode 100644 packages/ui/THIRD_PARTY_NOTICES.md create mode 100644 packages/ui/package.json create mode 100644 packages/ui/postcss.config.js create mode 100644 packages/ui/src/assets.d.ts create mode 100644 packages/ui/src/button.tsx create mode 100644 packages/ui/src/calendar.tsx create mode 100644 packages/ui/src/carousel.tsx create mode 100644 packages/ui/src/chart.tsx create mode 100644 packages/ui/src/classes.ts create mode 100644 packages/ui/src/command.tsx create mode 100644 packages/ui/src/index.ts create mode 100644 packages/ui/src/input-group.tsx create mode 100644 packages/ui/src/input.tsx create mode 100644 packages/ui/src/primitives/accordion.tsx create mode 100644 packages/ui/src/primitives/alert-dialog.tsx create mode 100644 packages/ui/src/primitives/autocomplete.tsx create mode 100644 packages/ui/src/primitives/avatar.tsx create mode 100644 packages/ui/src/primitives/checkbox-group.tsx create mode 100644 packages/ui/src/primitives/checkbox.tsx create mode 100644 packages/ui/src/primitives/collapsible.tsx create mode 100644 packages/ui/src/primitives/combobox.tsx create mode 100644 packages/ui/src/primitives/context-menu.tsx create mode 100644 packages/ui/src/primitives/dialog.tsx create mode 100644 packages/ui/src/primitives/drawer.tsx create mode 100644 packages/ui/src/primitives/field.tsx create mode 100644 packages/ui/src/primitives/fieldset.tsx create mode 100644 packages/ui/src/primitives/form.tsx create mode 100644 packages/ui/src/primitives/menu.tsx create mode 100644 packages/ui/src/primitives/menubar.tsx create mode 100644 packages/ui/src/primitives/meter.tsx create mode 100644 packages/ui/src/primitives/navigation-menu.tsx create mode 100644 packages/ui/src/primitives/number-field.tsx create mode 100644 packages/ui/src/primitives/otp-field.tsx create mode 100644 packages/ui/src/primitives/popover.tsx create mode 100644 packages/ui/src/primitives/preview-card.tsx create mode 100644 packages/ui/src/primitives/progress.tsx create mode 100644 packages/ui/src/primitives/radio-group.tsx create mode 100644 packages/ui/src/primitives/radio.tsx create mode 100644 packages/ui/src/primitives/scroll-area.tsx create mode 100644 packages/ui/src/primitives/select.tsx create mode 100644 packages/ui/src/primitives/slider.tsx create mode 100644 packages/ui/src/primitives/switch.tsx create mode 100644 packages/ui/src/primitives/tabs.tsx create mode 100644 packages/ui/src/primitives/toast.tsx create mode 100644 packages/ui/src/primitives/toggle-group.tsx create mode 100644 packages/ui/src/primitives/toggle.tsx create mode 100644 packages/ui/src/primitives/toolbar.tsx create mode 100644 packages/ui/src/primitives/tooltip.tsx create mode 100644 packages/ui/src/resizable.tsx create mode 100644 packages/ui/src/sheet.tsx create mode 100644 packages/ui/src/styles.css create mode 100644 packages/ui/src/styles/actions.css create mode 100644 packages/ui/src/styles/controls.css create mode 100644 packages/ui/src/styles/layout.css create mode 100644 packages/ui/src/styles/overlays.css create mode 100644 packages/ui/src/styles/surfaces.css create mode 100644 packages/ui/src/surfaces.tsx create mode 100644 packages/ui/tsconfig.json create mode 100644 packages/ui/vite.config.ts diff --git a/desktop-next/package.json b/desktop-next/package.json index f2e4abbb72..220447a251 100644 --- a/desktop-next/package.json +++ b/desktop-next/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build && tsc --noEmit", + "build": "pnpm --filter @buzz/ui build && vite build && tsc --noEmit", "typecheck": "tsc --noEmit", "preview": "vite preview", "lint": "biome lint .", @@ -18,13 +18,14 @@ "test": "pnpm --filter @buzz/design-tokens test" }, "dependencies": { - "@base-ui-components/react": "1.0.0-rc.0", "@fontsource-variable/inter": "^5.2.8", "@fontsource/jetbrains-mono": "^5.3.0", "@tanstack/react-router": "^1.168.10", "react": "^19.1.0", "react-dom": "^19.1.0", - "@buzz/design-tokens": "workspace:*" + "@buzz/design-tokens": "workspace:*", + "@buzz/ui": "workspace:*", + "lucide-react": "^1.0.0" }, "devDependencies": { "@tailwindcss/postcss": "^4.3.0", @@ -34,6 +35,7 @@ "@vitejs/plugin-react": "^6.0.0", "tailwindcss": "^4.3.0", "typescript": "~6.0.0", - "vite": "^8.0.0" + "vite": "^8.0.0", + "@playwright/test": "^1.58.2" } } diff --git a/desktop-next/src/features/design-system/ui/MotionPage.tsx b/desktop-next/src/features/design-system/ui/MotionPage.tsx index a60df6a034..21c1d0f6d9 100644 --- a/desktop-next/src/features/design-system/ui/MotionPage.tsx +++ b/desktop-next/src/features/design-system/ui/MotionPage.tsx @@ -1,7 +1,8 @@ import { FOUNDATION_GROUPS } from "@buzz/design-tokens/foundations"; import { PageHeader, Section } from "./primitives"; export function MotionPage() { - const group = FOUNDATION_GROUPS[2]; + const group = FOUNDATION_GROUPS.find((group) => group.name === "Motion"); + if (!group) throw new Error("Missing foundation group"); return ( <> diff --git a/desktop-next/src/features/design-system/ui/RadiusPage.tsx b/desktop-next/src/features/design-system/ui/RadiusPage.tsx index 416761bc44..693fa659b9 100644 --- a/desktop-next/src/features/design-system/ui/RadiusPage.tsx +++ b/desktop-next/src/features/design-system/ui/RadiusPage.tsx @@ -1,7 +1,8 @@ import { FOUNDATION_GROUPS } from "@buzz/design-tokens/foundations"; import { PageHeader, Section } from "./primitives"; export function RadiusPage() { - const group = FOUNDATION_GROUPS[1]; + const group = FOUNDATION_GROUPS.find((group) => group.name === "Radius"); + if (!group) throw new Error("Missing foundation group"); return ( <> diff --git a/desktop-next/src/features/design-system/ui/SpacingPage.tsx b/desktop-next/src/features/design-system/ui/SpacingPage.tsx index 9c88d77f47..564858e8b0 100644 --- a/desktop-next/src/features/design-system/ui/SpacingPage.tsx +++ b/desktop-next/src/features/design-system/ui/SpacingPage.tsx @@ -1,7 +1,8 @@ import { FOUNDATION_GROUPS } from "@buzz/design-tokens/foundations"; import { PageHeader, Section } from "./primitives"; export function SpacingPage() { - const group = FOUNDATION_GROUPS[0]; + const group = FOUNDATION_GROUPS.find((group) => group.name === "Spacing"); + if (!group) throw new Error("Missing foundation group"); return ( <> diff --git a/packages/design-tokens/src/foundations.css b/packages/design-tokens/src/foundations.css index 62936a5087..d46f5e7499 100644 --- a/packages/design-tokens/src/foundations.css +++ b/packages/design-tokens/src/foundations.css @@ -1,5 +1,14 @@ /* Generated by packages/design-tokens/build.mjs. */ @theme { + --control-sm: 2rem; + --control-md: 2.5rem; + --control-lg: 3.25rem; + --space-action-inset: 1.25rem; + --space-tooltip-block: 0.125rem; + --layer-dialog: 80; + --layer-floating: 100; + --layer-toast: 120; + --overlay-scrim: rgb(0 0 0 / 0.5); --space-control-gap: 0.5rem; --space-control-inset: 0.75rem; --space-section-gap: 1.5rem; diff --git a/packages/design-tokens/src/foundations.ts b/packages/design-tokens/src/foundations.ts index 3ad3a7bf90..fce9e0fc4e 100644 --- a/packages/design-tokens/src/foundations.ts +++ b/packages/design-tokens/src/foundations.ts @@ -1,5 +1,20 @@ /** Role-based geometry and motion shared by components and documentation. */ export const FOUNDATION_GROUPS = [ + { + name: "Controls", + description: "Shared control dimensions and overlay stacking.", + roles: [ + ["control-sm", "2rem", "Small actions."], + ["control-md", "2.5rem", "Standard actions and inputs."], + ["control-lg", "3.25rem", "Prominent actions."], + ["space-action-inset", "1.25rem", "Standard button horizontal padding."], + ["space-tooltip-block", "0.125rem", "Compact tooltip vertical padding."], + ["layer-dialog", "80", "Modal surfaces."], + ["layer-floating", "100", "Menus and tooltips above their trigger."], + ["layer-toast", "120", "Transient status announcements."], + ["overlay-scrim", "rgb(0 0 0 / 0.5)", "A modal backdrop."], + ], + }, { name: "Spacing", description: diff --git a/packages/ui/.gitignore b/packages/ui/.gitignore new file mode 100644 index 0000000000..849ddff3b7 --- /dev/null +++ b/packages/ui/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/packages/ui/README.md b/packages/ui/README.md new file mode 100644 index 0000000000..08c102a5e3 --- /dev/null +++ b/packages/ui/README.md @@ -0,0 +1,68 @@ +# Buzz UI + +Open-source React components with Buzz's semantic roles, Inter typography, and +Lucide icons. Behavior comes from public Base UI primitives; calendar and resize +behavior come from DayPicker and react-resizable-panels. No internal package, +font CDN, Storybook server, or native Buzz bridge is required. + +From the repository root: + +```sh +. ./bin/activate-hermit +pnpm install --frozen-lockfile +pnpm --filter buzz-desktop-next dev +# Open http://localhost:5173/design +pnpm --filter @buzz/ui build +``` + +## Consume + +```tsx +import '@fontsource-variable/inter/index.css'; +import '@fontsource/jetbrains-mono/index.css'; +import { Button, Field, Input } from '@buzz/ui'; + + + Name + + + + +``` + +The workspace export includes the source CSS; use a Tailwind 4 PostCSS pipeline. +For a consumer without Tailwind, build the package and use `@buzz/ui/dist` plus +`@buzz/ui/dist/styles.css`. Build output keeps React and behavior libraries external. +The package is intentionally private until package naming and release automation +are agreed; it is fully usable from this open-source workspace. + +The stylesheet includes the shared theme and Tailwind reset. Load it once at the +application boundary. Set `class="dark"` on the document root for dark mode. +Font packages belong to the host, allowing self-hosted subsets without remote requests. + +## Component contract + +Compound families retain Base UI's API: `Dialog.Root`, `Dialog.Trigger`, +`Dialog.Portal`, `Dialog.Backdrop`, `Dialog.Popup`, `Dialog.Title`, and +`Dialog.Close`. Use `render={