constructive-ui
Constructive UI
Build UIs with @constructive-io/ui — 50+ components on Base UI + Tailwind CSS v4.
When to Apply
- Building UIs with
@constructive-io/uicomponents - Creating custom components with cva/cn/data-slot
- Setting up theming, dark mode, OKLCH tokens
- Building forms, overlays, layouts, advanced inputs
- Using the shadcn registry
- Adding animations with motion/react
Quick Start
Deep Import Convention
// Correct — tree-shakeable
import { Button } from '@constructive-io/ui/button';
import { Dialog, DialogTrigger, DialogPopup } from '@constructive-io/ui/dialog';
import { cn } from '@constructive-io/ui/lib/utils';
// Avoid — barrel import pulls entire library
import { Button } from '@constructive-io/ui';
PortalRoot Setup (Required)
All overlay components (dialogs, popovers, tooltips, sheets) require PortalRoot in your root layout:
import { PortalRoot } from '@constructive-io/ui/portal';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<PortalRoot />
</body>
</html>
);
}
Toaster Setup
Add Toaster to your root layout for toast notifications:
import { Toaster } from '@constructive-io/ui/sonner';
// Place <Toaster /> alongside <PortalRoot /> in your body
Component Architecture
Every component follows: cva for variant definitions, cn() for class merging, data-slot on root, named exports.
'use client';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@constructive-io/ui/lib/utils';
const myVariants = cva('base-classes', {
variants: { variant: { default: '...', secondary: '...' } },
defaultVariants: { variant: 'default' },
});
type MyProps = React.ComponentProps<'div'> & VariantProps<typeof myVariants>;
function MyComponent({ className, variant, ...props }: MyProps) {
return <div data-slot="my-component" className={cn(myVariants({ variant }), className)} {...props} />;
}
export { MyComponent, myVariants, type MyProps };
Key rules:
React.ComponentProps<'element'>overReact.HTMLAttributes- Always set
data-sloton root element cn()to merge className — never concatenate- Named exports only,
'use client'when using hooks/events Slot+Slottablefrom@constructive-io/ui/lib/utilsfor polymorphicasChild
See references/foundations.md for full patterns, Base UI mapping, TypeScript conventions.
Theming & Tokens
OKLCH token system in globals.css with @theme inline for Tailwind v4.
Key Tokens
| Category | Tokens |
|---|---|
| Surface | background, foreground, card, popover |
| Interactive | primary, secondary, accent, muted, destructive |
| Status | info, success, warning |
| Input | border, input, ring |
| Sidebar | sidebar, sidebar-primary, sidebar-accent, sidebar-border |
| Chart | chart-1 through chart-5 |
Dark Mode
Class-based via .dark on <html>. Tailwind v4 directive: @custom-variant dark (&:is(.dark *));
// Toggle implementation
document.documentElement.classList.toggle('dark', isDark);
localStorage.setItem('theme', isDark ? 'dark' : 'light');
Tailwind v4 Migration
| v3 | v4 |
|---|---|
shadow-sm |
shadow-xs |
shadow |
shadow-sm |
rounded-sm |
rounded-xs |
rounded |
rounded-sm |
outline-none |
outline-hidden |
bg-opacity-* |
bg-black/50 |
bg-[--brand] |
bg-(--brand) |
See references/theming.md for complete globals.css, z-index layers, shadow utilities. See references/token-values.md for all light/dark OKLCH values.
Registry Installation
Install components via shadcn registry or npm.
// components.json — add constructive registry
{
"registries": {
"@constructive": "https://constructive-io.github.io/dashboard/r/{name}.json"
}
}
npx shadcn@latest add @constructive/button
npx shadcn@latest add @constructive/form-kit # all form components
npx shadcn@latest add @constructive/overlay-kit # all overlay components
npx shadcn@latest add @constructive/layout-kit # all layout components
npx shadcn@latest add @constructive/constructive-theme # full token system
npm = centralized updates, version-locked. Registry = source ownership, deep customization.
See references/registry.md for full component list, bundles, build pipeline.
Animation System
Import from motion/react (NOT framer-motion). Use presets from @constructive-io/ui/lib/motion/motion-config.
| Preset | Use For |
|---|---|
variants.fadeScale |
Modal/dialog enter |
variants.fadeSlideUp |
List items with stagger |
variants.fadeSlideDown |
Toast/notification |
variants.fade |
Subtle presence change |
variants.floatUp |
Hero entrance |
transitions.panel |
Sheet/drawer slide |
springs.snappy |
Button press (whileTap) |
transitions.enterExit |
Tab/route transitions |
'use client';
import { motion, AnimatePresence } from 'motion/react';
import { variants } from '@constructive-io/ui/lib/motion/motion-config';
<AnimatePresence mode="wait">
{isOpen && (
<motion.div key="panel" variants={variants.fadeScale} initial="initial" animate="animate" exit="exit">
{children}
</motion.div>
)}
</AnimatePresence>
See references/motion.md for all presets, stagger patterns, reduced motion, performance rules.
Forms
Three layers: Field (standalone labels), FormControl (floating labels), Form (react-hook-form).
// Layer 1: Field — simple label + input
import { Field } from '@constructive-io/ui/field';
import { Input } from '@constructive-io/ui/input';
<Field label="Email" error={errors.email} required>
<Input type="email" placeholder="name@example.com" />
</Field>
// Layer 3: Form — react-hook-form integration
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@constructive-io/ui/form';
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField control={form.control} name="email" render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl><Input {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
</form>
</Form>
InputGroup for addons: InputGroupAddon with position="inline-start|inline-end|block-start|block-end".
See references/forms.md for all form patterns, Zod validation, composition examples. See references/input-components.md for Input, Textarea, Checkbox, RadioGroup, Switch, Select API.
Overlays
| Component | Use When |
|---|---|
| Dialog | Modal form, confirmation, focused content |
| AlertDialog | Destructive confirmation (blocks interaction) |
| Sheet | Side panel for details, editing |
| Popover | Contextual info/controls, filter panels |
| Tooltip | Brief hints on hover/focus |
| DropdownMenu | Action menus, context menus |
| Command | Command palette (Cmd+K), search-driven command execution |
// Dialog
import { Dialog, DialogTrigger, DialogPopup, DialogHeader, DialogTitle, DialogFooter } from '@constructive-io/ui/dialog';
<Dialog>
<DialogTrigger asChild><Button>Open</Button></DialogTrigger>
<DialogPopup>
<DialogHeader><DialogTitle>Title</DialogTitle></DialogHeader>
{/* content */}
<DialogFooter><Button>Save</Button></DialogFooter>
</DialogPopup>
</Dialog>
// Sheet
import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle } from '@constructive-io/ui/sheet';
<Sheet>
<SheetTrigger asChild><Button>Open Panel</Button></SheetTrigger>
<SheetContent side="right">{/* content */}</SheetContent>
</Sheet>
Floating elements inside modals auto-elevate z-index via useFloatingOverlayPortalProps().
See references/overlays.md for all overlay components, nesting patterns. See references/sheet-stacking.md for SheetStackProvider deep dive. See references/dropdown-menu-api.md for DropdownMenu sub-component API. See references/command-palette.md for full command palette system — registry, hooks, multi-step wizards, background tasks, keyboard shortcuts.
Layout & Navigation
// Sidebar — full app shell
import { SidebarProvider, Sidebar, SidebarContent, SidebarMenu, SidebarMenuItem,
SidebarMenuButton, SidebarInset, SidebarTrigger } from '@constructive-io/ui/sidebar';
<SidebarProvider>
<Sidebar>
<SidebarContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive tooltip="Home"><Home className="size-4" /><span>Home</span></SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarContent>
</Sidebar>
<SidebarInset>{/* main content */}</SidebarInset>
</SidebarProvider>
// Tabs
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@constructive-io/ui/tabs';
<Tabs defaultValue="general">
<TabsList>
<TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="security">Security</TabsTrigger>
</TabsList>
<TabsContent value="general">...</TabsContent>
<TabsContent value="security">...</TabsContent>
</Tabs>
Also: Breadcrumb, Pagination, Stepper, Collapsible, Resizable, ScrollArea, PageHeader, Dock.
Stack Navigation (iOS-Style Card Navigation)
The primary navigation pattern in the Constructive admin app. Cards push/pop from the right with peek interactions, gestures, and responsive layout.
import { CardStackProvider, useCardStack, CardStackViewport } from '@constructive-io/ui/stack';
// Root layout — wraps entire app
<CardStackProvider layoutMode="side-by-side" defaultPeekOffset={48}>
{children}
<ClientOnlyStackViewport />
</CardStackProvider>
// Push cards imperatively
const stack = useCardStack();
stack.push({ title: 'Profile', Component: ProfileCard, props: { userId } });
Layout modes: cascade (overlapping peek) and side-by-side (master-detail). Cards support useCardReady() to defer queries until slide animation completes.
See references/stack-navigation.md for full Stack API — CardSpec, CardStackApi, route registry, peek gestures, mobile behavior.
See references/layout.md for all layout components. See references/sidebar-api.md for Sidebar sub-component props, CSS variables.
Data Display & Feedback
// Badge variants
import { Badge } from '@constructive-io/ui/badge';
<Badge variant="success">Active</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="warning">Pending</Badge>
// Table
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@constructive-io/ui/table';
// Toast
import { showSuccessToast, showErrorToast } from '@constructive-io/ui/toast';
showSuccessToast('Saved');
showErrorToast('Failed', 'Please try again');
Also: Alert, Avatar, Skeleton, Progress, FlickeringGrid, MotionGrid, ProgressiveBlur.
See references/data-display.md for all data display components.
Advanced Inputs
| Need | Component |
|---|---|
| Search + select one | Autocomplete (simple) or Combobox (richer) |
| Search + select multiple | Combobox multiple or MultiSelect |
| Tag-style multi-picker | Tags (create-on-enter) |
| Link/unlink records | RecordPicker (fuzzy search, checkboxes) |
| Date selection | Calendar / RangeCalendar |
| JSON editing | JsonInput (with validation) |
// Combobox
import { Combobox, ComboboxInput, ComboboxTrigger, ComboboxContent, ComboboxItem, ComboboxList } from '@constructive-io/ui/combobox';
<Combobox value={value} onValueChange={setValue}>
<ComboboxTrigger><ComboboxInput placeholder="Select..." /></ComboboxTrigger>
<ComboboxContent>
<ComboboxList>
{items.map((item) => <ComboboxItem key={item.value} value={item.value}>{item.label}</ComboboxItem>)}
</ComboboxList>
</ComboboxContent>
</Combobox>
See references/advanced-inputs.md for all advanced input components. See references/combobox-api.md for Combobox sub-component props, multiple mode, useComboboxFilter.
Component Catalog
Primitives
button, badge, label, skeleton, card (patterns), separator, alert
Form
input, textarea, checkbox, checkbox-group, radio-group, switch, select, progress, form, form-control, input-group, field
Overlay
dialog, alert-dialog, sheet, drawer, popover, tooltip, dropdown-menu, command
Layout
tabs, collapsible, scroll-area, resizable, sidebar, breadcrumb, pagination, stepper, page-header, dock
Data
table, avatar
Advanced Inputs
autocomplete, combobox, multi-select, tags, record-picker, calendar-rac, json-input
Notifications
sonner, toast
Navigation
stack (full reference)
Utilities
portal, lib/utils (cn, Slot, Slottable, composeRefs, mergeProps, useControllableState), lib/motion/motion-config, globals.css
Effects
flickering-grid, motion-grid, progressive-blur, progressive-blur-scroll-container, responsive-diagram
All imported via @constructive-io/ui/{name}.
Best Practices
- Imports: Always use deep imports (
@constructive-io/ui/button), never barrel imports - Components: Named exports only,
data-sloton root,cn()for class merging,...propsspread - Types:
React.ComponentProps<'element'>overReact.HTMLAttributes - Client:
'use client'on any component using hooks, events, or browser APIs - Tailwind v4:
shadow-xsnotshadow-sm,rounded-xsnotrounded-sm,bg-black/50notbg-opacity-* - Tokens: Use semantic tokens (
bg-primary) not raw colors (bg-blue-500), define light+dark for custom tokens - Z-index: Use layer variables (
--z-layer-floating), never hardcode values - Animation: Use
motion-configpresets, respectprefers-reduced-motion - Forms:
Fieldfor simple forms,Form+FormFieldfor validation,zodResolverover inline rules - Overlays:
asChildon triggers,PortalRootrequired,AlertDialogfor destructive confirms - Icons:
size-4shorthand overw-4 h-4
References
- references/foundations.md — Component architecture: cva, cn, Slot, data-slot, Base UI mapping
- references/theming.md — OKLCH tokens, globals.css, dark mode, z-index layers, Tailwind v4 migration
- references/token-values.md — Complete light/dark OKLCH token value table
- references/registry.md — shadcn registry setup, npm vs registry, bundles, troubleshooting
- references/motion.md — motion/react presets, AnimatePresence, springs, reduced motion
- references/forms.md — Field, FormControl, Form (react-hook-form), InputGroup patterns
- references/input-components.md — Input, Textarea, Checkbox, RadioGroup, Switch, Select API
- references/overlays.md — Dialog, AlertDialog, Sheet, Popover, Tooltip, DropdownMenu
- references/sheet-stacking.md — SheetStackProvider modes, useSheetStack, nested sheets
- references/dropdown-menu-api.md — DropdownMenu sub-component props reference
- references/layout.md — Sidebar, Tabs, Breadcrumb, Pagination, Stepper, Collapsible, Resizable
- references/sidebar-api.md — Sidebar sub-component props, CSS variables, cookie persistence
- references/data-display.md — Table, Badge, Alert, Avatar, Skeleton, Progress, Toast, effects
- references/advanced-inputs.md — Autocomplete, Combobox, MultiSelect, Tags, RecordPicker, Calendar, JsonInput
- references/combobox-api.md — Combobox sub-component props, multiple mode, useComboboxFilter
- references/command-palette.md — Full command palette system: registry, hooks, multi-step wizards, background tasks, keyboard shortcuts
- references/card-patterns.md — Card variants (default/elevated/flat/ghost/interactive), usage patterns, grid layouts
- references/stack-navigation.md — iOS-style card navigation: CardStackApi, route registry, peek gestures, mobile behavior
More from constructive-io/constructive-skills
drizzle-orm
Drizzle ORM patterns for PostgreSQL schema design and queries. Use when asked to "design Drizzle schema", "write Drizzle queries", "set up Drizzle ORM", or when building type-safe database layers.
21planning-blueprinting
In-repo planning and specification system for software projects. Use when asked to "create a plan", "write a spec", "document a proposal", "blueprint a feature", or when doing architectural planning work.
20pgsql-parser-testing
Test the pgsql-parser repository (SQL parser/deparser). Use when working in the pgsql-parser repo, fixing deparser issues, running parser tests, or validating SQL round-trips. Scoped specifically to the constructive-io/pgsql-parser repository.
18constructive-graphql-codegen
Generate type-safe React Query hooks, Prisma-like ORM client, or inquirerer-based CLI from GraphQL endpoints, schema files/directories, databases, or PGPM modules using @constructive-io/graphql-codegen. Also generates documentation (README, AGENTS.md, skills/, mcp.json). Use when asked to "generate GraphQL hooks", "generate ORM", "generate CLI", "set up codegen", "generate docs", "generate skills", "export schema", or when implementing data fetching for a PostGraphile backend.
17constructive-server-config
Configure and run the Constructive GraphQL server (cnc server), GraphiQL explorer (cnc explorer), and code generation (cnc codegen). Use when asked to "start the server", "run cnc server", "start GraphQL API", "run GraphiQL", "configure API routing", "generate types", or when working with the Constructive CLI and PostGraphile.
17constructive-boilerplate-nextjs-app
Set up and develop with the Constructive App frontend boilerplate — a Next.js application with authentication, organization management, invites, members, and a GraphQL SDK. Use when scaffolding a new Constructive frontend application from the boilerplate.
17