apps/ui/src/components/ui/ and are mostly stock shadcn/ui (New York style, neutral base, CSS variables enabled — see components.json) with a few HQ-specific additions called out below.
Don’t reach past primitives. If something can be composed from these, compose it. If it can’t, build it as a composite in components/shared/, never as another primitive.
Layout and containers
Structural pieces. They organize space; they don’t carry state of their own.| Component | File | Purpose |
|---|---|---|
Card | card.tsx | Bordered surface with semantic slots: CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter. |
Tabs | tabs.tsx | Two CVA variants: default (pill) and line (underline). Horizontal and vertical. |
Accordion | accordion.tsx | Stacked collapsible sections. |
Collapsible | collapsible.tsx | Single collapsible region (no accordion grouping). |
Separator | separator.tsx | 1px divider, horizontal or vertical. |
ScrollArea | scroll-area.tsx | Custom-styled scrollable container. Thin scrollbars match the global token. |
Resizable | resizable.tsx | Drag-to-resize panels (react-resizable-panels). |
AspectRatio | aspect-ratio.tsx | Fixed-ratio container for media. |
Sidebar | sidebar.tsx | Low-level sidebar primitive. The dashboard shell uses its own composition; reach for this only if you need a second sidebar surface. |
Forms and input
Reach forForm first. It wires React Hook Form + Zod and handles labels, descriptions, and errors consistently. Only use bare inputs when there is no form (search boxes, ad-hoc filters, inline-edit cells).
| Component | File | Purpose |
|---|---|---|
Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage | form.tsx | The full RHF + Zod stack. Auto-IDs, aria-describedby, aria-invalid wired up. |
Field | field.tsx | HQ extension. Labeled field wrapper for form-less surfaces (filters, settings panels). |
Label | label.tsx | Radix label primitive used inside Form. |
Input | input.tsx | Single-line text. Supports type="file". |
Textarea | textarea.tsx | Multi-line plaintext. For rich text use the Novel editor instead. |
InputGroup | input-group.tsx | HQ extension. Input with prefix or suffix slot. |
InputOtp | input-otp.tsx | Segmented one-time-password input. |
Checkbox | checkbox.tsx | Indeterminate-aware checkbox. |
RadioGroup | radio-group.tsx | Single-select radio set. |
Switch | switch.tsx | Boolean toggle. Use for “is enabled” semantics; use Checkbox for selection. |
Select | select.tsx | Custom-styled select with searchable items. |
NativeSelect | native-select.tsx | Real <select>. Use on mobile or when full keyboard control matters more than visual polish. |
Combobox | combobox.tsx | Search-as-you-type select with multi-select support. |
TagInput | tag-input.tsx | HQ extension. Multi-value chip input — Enter or comma to commit. |
Slider | slider.tsx | Range slider, single or dual handle. |
Toggle | toggle.tsx | Binary on/off button. |
ToggleGroup | toggle-group.tsx | Single or multiple-select toggle row (view switchers, formatting bars). |
Actions and controls
| Component | File | Purpose |
|---|---|---|
Button | button.tsx | Variants: default, destructive, outline, secondary, ghost, link. Sizes: default, xs, sm, lg, icon, icon-xs, icon-sm, icon-lg. CVA-driven; do not subclass. |
ButtonGroup | button-group.tsx | Adjacent buttons treated as a single segmented control (split actions). |
Item | item.tsx | Generic clickable row primitive — base for sidebar entries, list rows, palette results. |
Overlays
Pick the overlay by intent, not by visual shape:Dialog— focused decision the user must respond to (confirm, quick-create with ≤3 fields).AlertDialog— destructive or otherwise irreversible decisions; cannot be dismissed by clicking outside. Composed viaConfirmDialog.Sheet— multi-field forms or detail surfaces that benefit from staying alongside the page; preferred overDialogfor create flows. Composed viaSidePanel.Drawer— bottom-up mobile pattern (Vaul). Used sparingly; preferSheetfor desktop+mobile parity.Popover/HoverCard/Tooltip— non-modal supplementary content, in increasing weight (tooltip = label, hover-card = preview, popover = interactive).DropdownMenu/ContextMenu— action lists triggered by click vs. right-click respectively.NavigationMenu/Menubar— top-of-app menus. Rarely used at HQ; the command palette covers most navigation.Command— cmdk primitive used inside the command palette composite.
| Component | File |
|---|---|
Dialog | dialog.tsx |
AlertDialog | alert-dialog.tsx |
Sheet | sheet.tsx |
Drawer | drawer.tsx |
Popover | popover.tsx |
HoverCard | hover-card.tsx |
Tooltip | tooltip.tsx |
DropdownMenu | dropdown-menu.tsx |
ContextMenu | context-menu.tsx |
NavigationMenu | navigation-menu.tsx |
Menubar | menubar.tsx |
Command | command.tsx |
Display and data
| Component | File | Purpose |
|---|---|---|
Table | table.tsx | Semantic <table> with sticky-header support. Most lists use the DataTable composite on top. |
Badge | badge.tsx | Variants: default, secondary, destructive, outline, ghost, link. |
Avatar | avatar.tsx | Image with text fallback. |
StatusDot | status-dot.tsx | HQ extension. Small colored dot for status indicators on rows. Pair with text-status-* tokens. |
Progress | progress.tsx | Determinate progress bar. |
Chart | chart.tsx | Recharts wrapper. Consumes --chart-1..5 tokens. |
Calendar | calendar.tsx | Month-grid date picker. |
DatePickerButton | date-picker-button.tsx | HQ extension. Popover-button date picker for inline use. |
Pagination | pagination.tsx | Page-number navigation. |
Carousel | carousel.tsx | Horizontal-scroll carousel (Embla). |
Breadcrumb | breadcrumb.tsx | Path-style navigation; used inside the header bar. |
Feedback
| Component | File | Purpose |
|---|---|---|
Alert | alert.tsx | Persistent inline message. Use for unrecoverable warnings; transient feedback should use a toast. |
Skeleton | skeleton.tsx | animate-pulse placeholder block. Compose with LoadingSkeleton for full-list shapes. |
Spinner | spinner.tsx | Animated Loader2 icon. Use only when a skeleton would be misleading. |
Sonner (Toaster) | sonner.tsx | Toast outlet. Mounted once in the root layout. Call via toast(), toast.success(), toast.error(), toast.loading(). |
Empty | empty.tsx | Semantic empty-state primitives (icon, title, description). The EmptyState composite is preferred for full screens. |
Utilities
| Component | File | Purpose |
|---|---|---|
Kbd | kbd.tsx | HQ extension. Keyboard-key chip. Used in shortcut hints and the help sheet. |
InlineEdit | inline-edit.tsx | HQ extension. Click-to-edit text and textarea with Enter to commit, Escape to cancel. |
Direction | direction.tsx | LTR/RTL provider. Currently unused; reserved for internationalization. |
HQ extensions called out
These are the primitives we added on top of stock shadcn/ui. New extensions should be rare — most needs are better served by composing inshared/. If you do add one, follow the existing files for style: CVA for variants, data-slot attributes on composed pieces, no business logic, no module imports.

