UI Package
The UI package (@sustentus/ui) is a shared component library built with React, TypeScript, and Tailwind CSS, providing 45+ production-ready components.
Overview
Package Name: @sustentus/ui
Path: packages/ui/
Build Tool: TSUP
Version: 1.0.0
Purpose
Provides reusable UI components for all Sustentus applications, ensuring:
- Consistency: Unified design language across apps
- Reusability: Write once, use everywhere
- Maintainability: Single source of truth for components
- Accessibility: ARIA-compliant components
- Type Safety: Full TypeScript support
Technology Stack
Core
- React 19: UI library
- TypeScript 5: Type safety
- Radix UI: Accessible component primitives
- Tailwind CSS v4: Utility-first styling
Build
- TSUP: TypeScript bundler powered by esbuild
- Tailwind CLI: CSS compilation
- Concurrently: Run multiple build processes
UI Libraries
- class-variance-authority: Variant-based component styling
- tailwind-merge: Intelligent class merging
- Lucide React: Icon library
Form Libraries
- React Hook Form: Form state management
- Zod: Schema validation
- @hookform/resolvers: Integration layer
Additional
- Sonner: Toast notifications
- Recharts: Chart components
- Embla Carousel: Carousel implementation
- date-fns: Date utilities
- React Day Picker: Date picker
Directory Structure
ui/
├── src/
│ ├── components/
│ │ ├── accordion.tsx
│ │ ├── alert.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ └── ... (45+ components)
│ ├── lib/
│ │ └── utils.ts # Utility functions
│ ├── globals.css # Global styles & Tailwind
│ └── index.ts # Package entry point
├── dist/
│ ├── index.js # Bundled JavaScript
│ ├── index.d.ts # TypeScript declarations
│ ├── index.js.map # Source map
│ └── globals.css # Compiled CSS
├── components.json # shadcn/ui config
├── package.json
├── tsconfig.json
└── tsup.config.tsComponents
The package includes 45+ components organized by category:
Form Components
- Button: Clickable button with variants
- Input: Text input field
- Textarea: Multi-line text input
- Checkbox: Boolean selection
- Radio Group: Single selection from options
- Select: Dropdown selection
- Switch: Toggle switch
- Label: Form field label
- Form: Form wrapper with validation
Layout Components
- Card: Content container with header/footer
- Separator: Visual divider
- Tabs: Tabbed interface
- Accordion: Collapsible sections
- Collapsible: Expandable content
- Resizable: Resizable panels
- Aspect Ratio: Maintain aspect ratio
- Scroll Area: Custom scrollbars
Overlay Components
- Dialog: Modal dialog
- Drawer: Slide-out drawer (mobile)
- Sheet: Slide-out panel
- Popover: Floating content
- Tooltip: Hover hint
- Hover Card: Rich hover content
- Dropdown Menu: Contextual menu
- Context Menu: Right-click menu
- Alert Dialog: Confirmation dialog
Feedback Components
- Alert: Inline message
- Toast: Notification (via Sonner)
- Progress: Progress indicator
- Skeleton: Loading placeholder
Navigation Components
- Navigation Menu: Main navigation
- Menubar: Menu bar
- Breadcrumb: Breadcrumb trail
- Pagination: Page navigation
Data Display
- Table: Data table
- Badge: Status indicator
- Avatar: User avatar
- Calendar: Date picker calendar
- Carousel: Image/content carousel
- Chart: Data visualization
Input Components
- Input OTP: One-time password input
- Slider: Range slider
- Command: Command palette
- Toggle: Toggle button
- Toggle Group: Toggle button group
Utilities
- use-mobile: Mobile detection hook
AI Components
Compound components for building AI chat interfaces — used by the BRD Agent UI:
- PromptInput: Controlled text input for submitting messages to the AI agent
- Attachments: File attachment management within the chat interface
- Confirmation: Approval prompt for AI-generated outputs (e.g. BRD approval)
- Image: Image rendering within chat messages
- Shimmer: Skeleton loading state for streaming AI responses
- Message: Chat message bubble with role-aware styling (user vs. assistant)
Usage
Installation
In an application:
pnpm add @sustentus/ui@workspace:*Importing Components
import { Button, Card, Input } from "@sustentus/ui";
const MyComponent = () => (
<Card>
<Input placeholder="Enter text" />
<Button>Submit</Button>
</Card>
);Importing Styles
Import global CSS in your app layout:
import "@sustentus/ui/globals.css";Using the cn Utility
import { cn } from "@sustentus/ui";
const className = cn(
"base-class",
condition && "conditional-class",
anotherClass,
);Component Examples
Button
import { Button } from "@sustentus/ui";
// Default button
<Button>Click me</Button>
// Variant buttons
<Button variant="destructive">Delete</Button>
<Button variant="outline">Cancel</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon"><Icon /></Button>Form
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage, Input, Button } from "@sustentus/ui";
const schema = z.object({
email: z.string().email(),
});
const MyForm = () => {
const form = useForm({
resolver: zodResolver(schema),
});
return (
<Form {...form}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</Form>
);
};Development
Running in Watch Mode
# From root
pnpm ui:dev
# From ui directory
cd packages/ui
pnpm devThis runs TSUP in watch mode (rebuilds on code changes) and Tailwind in watch mode (recompiles CSS).
Building
# From root
pnpm ui:build
# From ui directory
cd packages/ui
pnpm buildThe build bundles TypeScript to JavaScript, generates declarations, and compiles CSS. pnpm clean removes the dist/ folder.
Configuration
TSUP Config
tsup.config.ts:
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm"], // ESM output
dts: true, // Generate .d.ts
clean: true, // Clean dist/
sourcemap: true, // Source maps
minify: false, // No minification
target: "es2022", // Modern JS
outDir: "dist",
external: ["react", "react-dom"],
banner: {
js: '"use client";', // Mark as client component
},
});Package Exports
package.json:
{
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./globals.css": "./dist/globals.css"
}
}React and React DOM are peer dependencies (^19); all other Radix UI, validation, and utility libraries are bundled.
Styling
Components use Tailwind utility classes and class-variance-authority for variants:
import { cva } from "class-variance-authority";
const buttonVariants = cva("inline-flex items-center justify-center", {
variants: {
variant: {
default: "bg-primary text-primary-foreground",
destructive: "bg-destructive text-destructive-foreground",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3",
lg: "h-11 px-8",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});The cn utility merges classes intelligently:
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));Accessibility
All components built on Radix UI primitives are keyboard navigable, screen-reader friendly (proper ARIA attributes), focus-managed, and WCAG compliant.
Adding New Components
Using shadcn/ui CLI
If the component is in shadcn/ui:
cd packages/ui
npx shadcn@latest add [component-name]Manual Addition
- Create the component file in
src/components/ - Export it from
src/index.ts - Build the package
- Add a story to Storybook
// src/components/new-component.tsx
import { type ComponentProps } from "react";
import { cn } from "../lib/utils.js";
export const NewComponent = ({ className, ...props }: ComponentProps<"div">) => (
<div className={cn("base-styles", className)} {...props} />
);// src/index.ts
export * from "./components/new-component.js";Learn More
- Storybook - Interactive component documentation
- Radix UI Documentation
- Tailwind CSS Documentation
- shadcn/ui Documentation