Ui
UI Package Overview
Shared component library for BISO Sites based on shadcn/ui.
UI Package Overview
The @repo/ui package is BISO Sites' shared component library, providing consistent UI components across all applications. Built on shadcn/ui with Tailwind CSS.
When to use this package
- Building any UI in the web, admin, or docs apps so everything stays on-brand.
- Sharing complex patterns (tables, forms, nav) without duplicating logic.
- Creating new design tokens or layout primitives that should be reused everywhere.
Where it's used
| Surface | Usage |
|---|---|
| Web App components | Marketing pages, forms, cards, layout |
| Admin App dashboards | Tables, filters, navigation, dialogs |
| Docs architecture | Callouts, tabs, and MDX UI primitives |
What's Included
- 📦 37+ shadcn/ui components - Fully styled and accessible
- 🎨 Theme system - Light/dark mode with customization
- 🎯 Utility functions -
cn()and helper utilities - 🪝 Custom hooks -
useMobile()and more - 📱 Responsive patterns - Mobile-first composite components
- 🌈 Design tokens - Consistent colors, spacing, typography
Package Structure
packages/ui/
├── components/
│ ├── ui/ # shadcn components (37+ components)
│ │ ├── button.tsx
│ │ ├── input.tsx
│ │ ├── card.tsx
│ │ └── ... (34 more)
│ ├── patterns/ # Composite components
│ └── theme-provider.tsx
├── hooks/
│ └── use-mobile.ts
├── lib/
│ ├── utils.ts # cn() and utilities
│ ├── fonts.ts # Font definitions
│ └── tokens.ts # Design tokens
├── styles/
│ └── globals.css # Global styles
└── package.jsonQuick Start
Installation
The package is automatically available in all workspace apps:
import { Button } from '@repo/ui/components/ui/button';
import { Card } from '@repo/ui/components/ui/card';
import { ThemeProvider } from '@repo/ui/components/theme-provider';Basic Usage
import { Button } from '@repo/ui/components/ui/button';
import { Card, CardHeader, CardTitle, CardContent } from '@repo/ui/components/ui/card';
export function MyComponent() {
return (
<Card>
<CardHeader>
<CardTitle>Hello World</CardTitle>
</CardHeader>
<CardContent>
<Button>Click me</Button>
</CardContent>
</Card>
);
}Available Components
Form Components
Button- Buttons with variants (default, destructive, outline, ghost)Input- Text inputs with validation statesTextarea- Multi-line text inputsCheckbox- Checkboxes with labelsRadio- Radio buttons and groupsSelect- Dropdowns and select menusSwitch- Toggle switchesSlider- Range slidersLabel- Form labels
Example:
import { Button } from '@repo/ui/components/ui/button';
import { Input } from '@repo/ui/components/ui/input';
import { Label } from '@repo/ui/components/ui/label';
<div className="space-y-4">
<div>
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="you@example.com" />
</div>
<Button>Submit</Button>
</div>Layout Components
Card- Content containers with header/footerSeparator- DividersTabs- Tabbed interfacesAccordion- Collapsible sectionsSheet- Slide-out panelsDialog- Modal dialogsDrawer- Bottom drawers
Example:
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@repo/ui/components/ui/card';
<Card>
<CardHeader>
<CardTitle>Product</CardTitle>
</CardHeader>
<CardContent>
<p>Product details here...</p>
</CardContent>
<CardFooter>
<Button>Add to Cart</Button>
</CardFooter>
</Card>Feedback Components
Alert- Inline alerts and notificationsToast- Toast notifications (Sonner)Badge- Status badgesProgress- Progress barsSkeleton- Loading skeletonsSpinner- Loading spinners
Example:
import { Alert, AlertTitle, AlertDescription } from '@repo/ui/components/ui/alert';
import { Badge } from '@repo/ui/components/ui/badge';
<Alert>
<AlertTitle>Success!</AlertTitle>
<AlertDescription>
Your changes have been saved.
</AlertDescription>
</Alert>
<Badge variant="success">Active</Badge>Navigation Components
NavigationMenu- Main navigation menusBreadcrumb- Breadcrumb navigationPagination- Page paginationCommand- Command palette (⌘K menu)ContextMenu- Right-click menusDropdownMenu- Dropdown menusMenubar- Menu bars
Data Display
Table- Data tablesAvatar- User avatarsTooltip- Hover tooltipsPopover- Pop-up contentHoverCard- Rich hover cards
Utilities
ScrollArea- Custom scrollbarsResizable- Resizable panelsCollapsible- Collapsible content
Theme System
Theme Provider
Wrap your app with ThemeProvider:
// app/layout.tsx
import { ThemeProvider } from '@repo/ui/components/theme-provider';
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
);
}Theme Toggle
'use client';
import { useTheme } from 'next-themes';
import { Button } from '@repo/ui/components/ui/button';
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<Button
variant="ghost"
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
Toggle Theme
</Button>
);
}Utility Functions
cn() - Class Name Utility
Merge Tailwind classes intelligently:
import { cn } from '@repo/ui/lib/utils';
// Merge classes
const className = cn('px-4 py-2', 'bg-blue-500', 'hover:bg-blue-600');
// Conditional classes
const buttonClass = cn(
'px-4 py-2 rounded',
isActive && 'bg-blue-500',
isDisabled && 'opacity-50 cursor-not-allowed'
);
// In components
<button className={cn('base-class', className, props.className)}>
Click me
</button>Custom Hooks
useMobile()
Detect mobile screens:
'use client';
import { useMobile } from '@repo/ui/hooks/use-mobile';
export function MyComponent() {
const isMobile = useMobile();
return (
<div>
{isMobile ? (
<MobileView />
) : (
<DesktopView />
)}
</div>
);
}Styling Guidelines
Tailwind CSS
All components use Tailwind CSS:
<Button className="w-full md:w-auto">
Responsive Button
</Button>
<div className="flex items-center gap-4 p-4">
<Avatar />
<div>
<p className="font-semibold">John Doe</p>
<p className="text-sm text-muted-foreground">john@example.com</p>
</div>
</div>Component Variants
Most components support variants:
// Button variants
<Button variant="default">Default</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
// Button sizes
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon"><Icon /></Button>
// Badge variants
<Badge variant="default">Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="outline">Outline</Badge>Toast Notifications
Using Sonner for toasts:
'use client';
import { toast } from 'sonner';
import { Button } from '@repo/ui/components/ui/button';
export function ToastDemo() {
return (
<div className="space-x-2">
<Button onClick={() => toast.success('Success!')}>
Success
</Button>
<Button onClick={() => toast.error('Error occurred')}>
Error
</Button>
<Button onClick={() => toast.info('Information')}>
Info
</Button>
<Button onClick={() => toast.loading('Loading...')}>
Loading
</Button>
</div>
);
}
// In layout, add Toaster
import { Toaster } from 'sonner';
<body>
<ThemeProvider>
{children}
<Toaster />
</ThemeProvider>
</body>Form Patterns
Common form pattern with shadcn components:
'use client';
import { Button } from '@repo/ui/components/ui/button';
import { Input } from '@repo/ui/components/ui/input';
import { Label } from '@repo/ui/components/ui/label';
import { Card } from '@repo/ui/components/ui/card';
import { toast } from 'sonner';
export function ContactForm() {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
try {
// Submit form
toast.success('Message sent!');
} catch (error) {
toast.error('Failed to send message');
}
};
return (
<Card className="p-6">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<Label htmlFor="name">Name</Label>
<Input id="name" name="name" required />
</div>
<div>
<Label htmlFor="email">Email</Label>
<Input id="email" name="email" type="email" required />
</div>
<div>
<Label htmlFor="message">Message</Label>
<Textarea id="message" name="message" required />
</div>
<Button type="submit">Send Message</Button>
</form>
</Card>
);
}Customization
Custom Component Variants
Extend component variants:
// components/custom-button.tsx
import { Button } from '@repo/ui/components/ui/button';
import { cn } from '@repo/ui/lib/utils';
export function CustomButton({ className, ...props }) {
return (
<Button
className={cn('custom-styling', className)}
{...props}
/>
);
}Theme Colors
Customize theme in your app's globals.css:
@layer base {
:root {
--primary: 221 83% 53%;
--secondary: 210 40% 96%;
--accent: 210 40% 96%;
--destructive: 0 84% 60%;
/* ... more variables */
}
.dark {
--primary: 217 91% 60%;
--secondary: 217 33% 17%;
/* ... dark mode colors */
}
}Best Practices
- Use semantic variants -
variant="destructive"notclassName="bg-red-500" - Combine with Tailwind - Use
classNamefor additional styles - Accessibility first - Components are accessible by default
- Responsive design - Use Tailwind responsive prefixes (
md:,lg:) - Consistent spacing - Use Tailwind spacing scale
- Theme-aware - Use theme colors, not hardcoded values
ℹ️
For complete component examples and API references, visit shadcn/ui documentation.
Next Steps
- Explore all components at shadcn/ui
- Styling Guide - BISO Sites styling patterns
- Forms Guide - Building forms with these components
