Skip to main content
Primitives are the lowest layer of the component system. They live in 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.

Forms and input

Reach for Form 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).

Actions and controls

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 via ConfirmDialog.
  • Sheet — multi-field forms or detail surfaces that benefit from staying alongside the page; preferred over Dialog for create flows. Composed via SidePanel.
  • Drawer — bottom-up mobile pattern (Vaul). Used sparingly; prefer Sheet for 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.

Display and data

Feedback

Utilities

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 in shared/. 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.