constructive-nextjs-ui
SKILL.md
Build modern Next.js applications using the @constructive-io/ui component library.
When to Apply
Use this skill when:
- Creating a new Next.js application with Constructive UI components
- Building dashboards, admin panels, or web applications
- Setting up a project with @constructive-io/ui
- Looking for component usage examples and patterns
Overview
The @constructive-io/ui library is a modern React component library built on Base UI primitives and Tailwind CSS v4. It provides 50+ production-ready components with consistent styling, dark mode support, and full TypeScript types.
Project Setup
1. Create a New Next.js Project
npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir
cd my-app
2. Install Dependencies
# Core package
pnpm add @constructive-io/ui
# Required peer dependencies
pnpm add @base-ui/react lucide-react
# Optional peer dependencies (install as needed)
pnpm add motion @use-gesture/react # For Stack component and animations
pnpm add sonner # For Toast notifications
pnpm add vaul # For Drawer component
pnpm add react-hook-form # For Form utilities
pnpm add react-aria-components @internationalized/date # For Calendar
pnpm add react-resizable-panels # For Resizable panels
3. Configure Tailwind CSS v4
Update your src/app/globals.css:
@import "tailwindcss";
@import "tw-animate-css";
/* Required: Tell Tailwind to scan the UI package */
@source "../node_modules/@constructive-io";
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.3211 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.3211 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.3211 0 0);
--primary: oklch(0.688 0.1754 245.6151);
--primary-foreground: oklch(0.979 0.021 166.113);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.967 0.001 286.375);
--muted-foreground: oklch(0.552 0.016 285.938);
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.55 0.2 25);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.871 0.006 286.286);
--ring: oklch(0.871 0.006 286.286);
--radius: 0.5rem;
}
.dark {
--background: oklch(0.21 0.006 285.885);
--foreground: oklch(0.985 0 0);
--card: oklch(0.21 0.006 285.885);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.21 0.006 285.885);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.688 0.1754 245.6151);
--primary-foreground: oklch(0.979 0.021 166.113);
--secondary: oklch(0.274 0.006 286.033);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.244 0.006 285.97);
--muted-foreground: oklch(0.705 0.015 286.067);
--accent: oklch(0.244 0.006 285.97);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.55 0.2 25);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.29 0.009 285.83);
--input: oklch(0.29 0.009 285.83);
--ring: oklch(0.442 0.017 285.786);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: var(--radius);
--radius-lg: calc(var(--radius) + 2px);
--radius-xl: calc(var(--radius) + 6px);
}
@layer base {
* {
@apply border-border/60 outline-ring/50;
}
body {
@apply bg-background text-foreground;
position: relative;
}
#__next,
[data-nextjs-root-layout] {
isolation: isolate;
}
}
Component Reference
Importing Components
Import components individually for tree-shaking:
import { Button } from '@constructive-io/ui/button';
import { Card, CardHeader, CardTitle, CardContent } from '@constructive-io/ui/card';
import { Input } from '@constructive-io/ui/input';
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle } from '@constructive-io/ui/dialog';
Available Components
Layout Components
Card- Container with header, content, footer sectionsSeparator- Visual dividerTabs- Tabbed interfaceCollapsible- Expandable/collapsible sectionsScrollArea- Custom scrollable areaResizable- Resizable panel layouts
Form Components
Button- Buttons with variants (default, secondary, outline, ghost, destructive)Input- Text inputTextarea- Multiline text inputCheckbox- Checkbox with labelSwitch- Toggle switchSelect- Dropdown selectRadioGroup- Radio button groupLabel- Form labelsProgress- Progress bar
Feedback Components
Alert- Alert messagesBadge- Status indicatorsSkeleton- Loading placeholdersToast- Toast notifications (requires sonner)
Overlay Components
Dialog- Modal dialogsAlertDialog- Confirmation dialogsSheet- Slide-out panelsDrawer- Bottom drawer (requires vaul)Popover- Floating contentTooltip- Hover tooltipsDropdownMenu- Context menus
Data Components
Table- Data tablesPagination- Page navigationAvatar- User avatarsBreadcrumb- Navigation breadcrumbs
Advanced Components
Command- Command paletteCombobox- Searchable selectAutocomplete- Auto-complete inputMultiSelect- Multi-value selectStack- Navigation card stack (requires motion)Calendar- Date picker (requires react-aria-components)
Usage Examples
Basic Card with Button
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@constructive-io/ui/card';
import { Button } from '@constructive-io/ui/button';
export function MyCard() {
return (
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
</CardHeader>
<CardContent>
<p>This is a card with some content.</p>
</CardContent>
<CardFooter>
<Button>Get Started</Button>
</CardFooter>
</Card>
);
}
Form with Input and Button
import { Input } from '@constructive-io/ui/input';
import { Button } from '@constructive-io/ui/button';
import { Label } from '@constructive-io/ui/label';
export function LoginForm() {
return (
<form className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="you@example.com" />
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" />
</div>
<Button type="submit" className="w-full">Sign In</Button>
</form>
);
}
Dialog Modal
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose,
} from '@constructive-io/ui/dialog';
import { Button } from '@constructive-io/ui/button';
export function ConfirmDialog() {
return (
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>
This action cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Tabs Interface
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@constructive-io/ui/tabs';
export function SettingsTabs() {
return (
<Tabs defaultValue="general">
<TabsList>
<TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="security">Security</TabsTrigger>
<TabsTrigger value="notifications">Notifications</TabsTrigger>
</TabsList>
<TabsContent value="general">
<p>General settings content</p>
</TabsContent>
<TabsContent value="security">
<p>Security settings content</p>
</TabsContent>
<TabsContent value="notifications">
<p>Notification settings content</p>
</TabsContent>
</Tabs>
);
}
Select Dropdown
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
} from '@constructive-io/ui/select';
export function StatusSelect() {
return (
<Select>
<SelectTrigger className="w-48">
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="active">Active</SelectItem>
<SelectItem value="pending">Pending</SelectItem>
<SelectItem value="inactive">Inactive</SelectItem>
</SelectContent>
</Select>
);
}
Data Table
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
} from '@constructive-io/ui/table';
import { Badge } from '@constructive-io/ui/badge';
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com', status: 'active' },
{ id: 2, name: 'Bob', email: 'bob@example.com', status: 'pending' },
];
export function UsersTable() {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map((user) => (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>
<Badge variant={user.status === 'active' ? 'default' : 'secondary'}>
{user.status}
</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}
Alert Messages
import { Alert, AlertTitle, AlertDescription } from '@constructive-io/ui/alert';
import { AlertCircle, CheckCircle } from 'lucide-react';
export function Alerts() {
return (
<div className="space-y-4">
<Alert>
<AlertCircle className="h-4 w-4" />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components to your app using the cli.
</AlertDescription>
</Alert>
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>
Something went wrong. Please try again.
</AlertDescription>
</Alert>
</div>
);
}
Button Variants
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon"><PlusIcon /></Button>
Card Variants
<Card variant="default">Default shadow</Card>
<Card variant="elevated">Prominent shadow</Card>
<Card variant="flat">No shadow</Card>
<Card variant="interactive">Hover lift effect</Card>
<Card variant="ghost">Transparent background</Card>
Dark Mode
Add the dark class to enable dark mode:
// In your layout or root component
<html className="dark">
{/* Components automatically use dark theme */}
</html>
Or toggle dynamically:
function ThemeToggle() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
document.documentElement.classList.toggle('dark', isDark);
}, [isDark]);
return (
<Button onClick={() => setIsDark(!isDark)}>
Toggle Theme
</Button>
);
}
Styling with data-slot
Components use data-slot attributes for styling hooks:
/* Target specific component parts */
[data-slot="card-header"] {
/* custom styles */
}
[data-slot="button"] {
/* custom styles */
}
TypeScript Support
Full TypeScript support with exported types:
import type { ButtonProps } from '@constructive-io/ui/button';
import type { CardProps } from '@constructive-io/ui/card';
const MyButton = (props: ButtonProps) => <Button {...props} />;
Tailwind CSS v4 Notes
Remember these Tailwind v4 changes:
shadow-smis nowshadow-xs,shadowis nowshadow-smrounded-smis nowrounded-xs,roundedis nowrounded-smoutline-noneis nowoutline-hidden- Use
bg-black/50instead ofbg-opacity-* - CSS variables: use
bg-(--brand)notbg-[--brand]
Best Practices
- Import individually - Always import components from their specific paths for tree-shaking
- Use semantic variants - Choose button/card variants that match the action's importance
- Consistent spacing - Use Tailwind's spacing utilities (space-y-4, gap-4, etc.)
- Accessible labels - Always provide labels for form inputs
- Loading states - Use Skeleton components for loading placeholders
- Error handling - Use Alert components for error messages
- Responsive design - Components are mobile-friendly by default
References
Weekly Installs
1
Repository
constructive-io…e-skillsFirst Seen
Feb 27, 2026
Security Audits
Installed on
mcpjam1
claude-code1
replit1
junie1
windsurf1
zencoder1