Component Specification โ
This document defines the UI component library specification for the HaloLight project, based on the shadcn/ui design system.
Component Library Overview โ
Based on shadcn/ui โ
All framework versions use the corresponding shadcn/ui implementation:
| Framework | Component Library | Repository |
|---|---|---|
| React/Next.js | shadcn/ui | shadcn/ui |
| Vue 3 | shadcn-vue | shadcn-vue |
| Svelte | shadcn-svelte | shadcn-svelte |
| Angular | spartan/ui | spartan |
| Solid.js | solid-ui | solid-ui |
Required Component Checklist โ
Basic UI Components (30+) โ
ui/
โโโ accordion.tsx # Accordion
โโโ alert-dialog.tsx # Alert dialog
โโโ alert.tsx # Alert
โโโ avatar.tsx # Avatar
โโโ badge.tsx # Badge
โโโ breadcrumb.tsx # Breadcrumb
โโโ button.tsx # Button
โโโ calendar.tsx # Calendar
โโโ card.tsx # Card
โโโ checkbox.tsx # Checkbox
โโโ collapsible.tsx # Collapsible
โโโ command.tsx # Command palette
โโโ data-table.tsx # Data table
โโโ date-picker.tsx # Date picker
โโโ dialog.tsx # Dialog
โโโ dropdown-menu.tsx # Dropdown menu
โโโ form.tsx # Form
โโโ input.tsx # Input
โโโ label.tsx # Label
โโโ pagination.tsx # Pagination
โโโ popover.tsx # Popover
โโโ progress.tsx # Progress
โโโ radio-group.tsx # Radio group
โโโ scroll-area.tsx # Scroll area
โโโ select.tsx # Select
โโโ separator.tsx # Separator
โโโ sheet.tsx # Sheet
โโโ skeleton.tsx # Skeleton
โโโ slider.tsx # Slider
โโโ switch.tsx # Switch
โโโ table.tsx # Table
โโโ tabs.tsx # Tabs
โโโ textarea.tsx # Textarea
โโโ toast.tsx # Toast
โโโ tooltip.tsx # Tooltip
โโโ sonner.tsx # Toast notificationLayout Components โ
layout/
โโโ AdminLayout.tsx # Admin main layout
โโโ AuthLayout.tsx # Auth page layout
โโโ Sidebar.tsx # Sidebar
โโโ Header.tsx # Header
โโโ Footer.tsx # Footer
โโโ Breadcrumb.tsx # Breadcrumb navigation
โโโ TabsNav.tsx # Tabs navigation
โโโ PageContainer.tsx # Page containerDashboard Components โ
dashboard/
โโโ DashboardGrid.tsx # Draggable grid container
โโโ WidgetWrapper.tsx # Widget wrapper
โโโ StatsWidget.tsx # Stats card
โโโ ChartWidget.tsx # Chart widget
โโโ TableWidget.tsx # Table widget
โโโ CalendarWidget.tsx # Calendar widget
โโโ TasksWidget.tsx # Task list
โโโ QuickActionsWidget.tsx # Quick actionsChart Components โ
charts/
โโโ LineChart.tsx # Line chart
โโโ BarChart.tsx # Bar chart
โโโ PieChart.tsx # Pie chart
โโโ AreaChart.tsx # Area chart
โโโ RadarChart.tsx # Radar chart
โโโ GaugeChart.tsx # GaugeComponent Design Specification โ
1. Props Interface Design โ
tsx
// Basic Props structure
interface ComponentProps {
// Required props first
children: React.ReactNode
// Optional props in alphabetical order
className?: string
disabled?: boolean
loading?: boolean
size?: 'sm' | 'md' | 'lg'
variant?: 'default' | 'outline' | 'ghost'
// Event handlers
onChange?: (value: T) => void
onClick?: () => void
}2. Style Variants โ
Use cva (class-variance-authority) to define variants:
tsx
import { cva, type VariantProps } from 'class-variance-authority'
const buttonVariants = cva(
// Base styles
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
outline: 'border border-input bg-background hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
},
size: {
sm: 'h-8 px-3 text-xs',
md: 'h-9 px-4 text-sm',
lg: 'h-10 px-6 text-base',
},
},
defaultVariants: {
variant: 'default',
size: 'md',
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
loading?: boolean
}3. Accessibility (a11y) โ
All components must support:
tsx
// ARIA attributes
<button
role="button"
aria-label={ariaLabel}
aria-disabled={disabled}
aria-busy={loading}
>
// Keyboard navigation
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
onClick?.()
}
if (e.key === 'Escape') {
onClose?.()
}
}
// Focus management
const focusRef = useRef<HTMLElement>(null)
useEffect(() => {
if (open) {
focusRef.current?.focus()
}
}, [open])4. Responsive Design โ
tsx
// Tailwind breakpoints
const breakpoints = {
sm: '640px', // Mobile landscape
md: '768px', // Tablet
lg: '1024px', // Small desktop
xl: '1280px', // Desktop
'2xl': '1536px' // Large screen
}
// Responsive class example
<div className="
grid
grid-cols-1
sm:grid-cols-2
md:grid-cols-3
lg:grid-cols-4
gap-4
">Layout Component Specification โ
AdminLayout โ
tsx
interface AdminLayoutProps {
children: React.ReactNode
}
// Layout structure
<div className="min-h-screen bg-background">
<Sidebar />
<div className="flex flex-col lg:ml-64">
<Header />
<main className="flex-1 p-6">
<PageContainer>
{children}
</PageContainer>
</main>
<Footer />
</div>
</div>Sidebar State โ
tsx
interface SidebarState {
collapsed: boolean // Is collapsed
mobileOpen: boolean // Mobile open state
activeMenu: string // Current active menu
openMenus: string[] // Expanded submenus
}
// Collapse widths
const SIDEBAR_WIDTH = 256 // Expanded 16rem
const SIDEBAR_COLLAPSED = 64 // Collapsed 4remHeader Component โ
tsx
interface HeaderProps {
showBreadcrumb?: boolean
showSearch?: boolean
showNotifications?: boolean
showUserMenu?: boolean
}
// Component parts
<header className="h-16 border-b bg-background/95 backdrop-blur">
<div className="flex items-center justify-between px-4 h-full">
{/* Left side */}
<div className="flex items-center gap-4">
<SidebarTrigger />
<Breadcrumb />
</div>
{/* Right side */}
<div className="flex items-center gap-2">
<GlobalSearch />
<ThemeToggle />
<NotificationDropdown />
<UserDropdown />
</div>
</div>
</header>Form Component Specification โ
Form Field Structure โ
tsx
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="Enter email" {...field} />
</FormControl>
<FormDescription>
We won't share your email
</FormDescription>
<FormMessage />
</FormItem>
)}
/>Form Validation โ
tsx
// Zod Schema
const formSchema = z.object({
username: z.string().min(2, 'Username must be at least 2 characters').max(50),
email: z.string().email('Please enter a valid email'),
password: z.string().min(8, 'Password must be at least 8 characters'),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: 'Passwords do not match',
path: ['confirmPassword'],
})Data Table Specification โ
DataTable Features โ
tsx
interface DataTableProps<T> {
columns: ColumnDef<T>[]
data: T[]
// Pagination
pagination?: boolean
pageSize?: number
// Sorting
sorting?: boolean
defaultSort?: { id: string; desc: boolean }
// Filtering
filtering?: boolean
globalFilter?: boolean
// Selection
selection?: boolean
onSelectionChange?: (rows: T[]) => void
// Actions
actions?: (row: T) => React.ReactNode
}Column Definition Example โ
tsx
const columns: ColumnDef<User>[] = [
{
id: 'select',
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected()}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
/>
),
},
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => <span className="font-medium">{row.getValue('name')}</span>,
},
{
accessorKey: 'email',
header: ({ column }) => (
<Button variant="ghost" onClick={() => column.toggleSorting()}>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
},
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => (
<Badge variant={row.getValue('status') === 'active' ? 'default' : 'secondary'}>
{row.getValue('status')}
</Badge>
),
filterFn: (row, id, value) => value.includes(row.getValue(id)),
},
{
id: 'actions',
cell: ({ row }) => <RowActions row={row.original} />,
},
]