UI components & utilities
The app's design system is a set of shadcn/ui-style primitives built on Radix UI and Tailwind CSS. Application chrome (the header) is composed from these primitives.
Design system primitives
Exported from src/components/ui/index.ts:
| Group | Exports |
|---|---|
| Button | Button, buttonVariants, ButtonProps |
| Card | Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent |
| Badge | Badge, badgeVariants, BadgeProps |
| Table | Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption |
| Form | Input, Label, Checkbox |
| Scroll | ScrollArea, ScrollBar |
| Avatar | Avatar, AvatarImage, AvatarFallback |
| Layout | Separator, Tabs, TabsList, TabsTrigger, TabsContent |
| Tooltip | Tooltip, TooltipTrigger, TooltipContent, TooltipProvider |
| Dropdown | DropdownMenu + Trigger, Content, Item, Label, Separator, Group, Portal, Sub, RadioGroup |
| Dialog | Dialog + Trigger, Portal, Close, Overlay, Content, Header, Footer, Title, Description |
| Feedback | Progress, StatusDot (StatusDotProps) |
| Utility | cn |
Import from the barrel:
import {Button, Card, CardHeader, cn} from '@/components/ui';
cn(...classes)
The className helper combines clsx with
tailwind-merge, so conflicting
Tailwind utilities resolve to the last one:
<div className={cn('px-2 py-1', isActive && 'bg-primary', className)} />
Header components
The application header lives in src/components/header/:
| Component | Role |
|---|---|
AppHeader | Top bar: brand, mode controls, view-tabs slot, save indicator, profile menu. |
ViewTabs | The horizontal strip of view tabs with add/rename/duplicate/delete. |
UserMenu | Profile dropdown: workspace switcher, settings, connection switcher, sign-out. |
AppHeader is presentational — it takes the current user, mode, and
saveStatus plus callbacks (onGoLive, onEdit, onSave, onNewWorkspace,
onWorkspaceSettings, onSignOut) and a tabsSlot.
Shared utilities
src/lib/utils.ts holds the tiny cross-cutting helpers:
genId(prefix = 'id'): string; // unique id (crypto.randomUUID with fallback)
nowIso(): string; // current ISO-8601 timestamp
These back the workspace/view/widget factories that need fresh ids and timestamps.
cn is exported from @/components/ui (the design system), while genId and
nowIso come from @/lib/utils. They live in different places on purpose: one
is a styling concern, the others are domain helpers.
Styling
- Tailwind is configured at the repo root
(tailwind.config.cjs)
and the design system ships a preset under
src/components/ui/. - Global styles enter through src/index.css.
- Icons come from
lucide-react.