14 KiB
AI Code Generation Guidelines
For AI Assistants: This document contains strict rules for generating code in the PragmaStack project. Follow these rules to ensure generated code matches the design system perfectly.
🎯 Core Rules
ALWAYS Do
-
✅ Import from
@/components/ui/*import { Button } from '@/components/ui/button'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; -
✅ Use semantic color tokens
className = 'bg-primary text-primary-foreground'; className = 'text-destructive'; className = 'bg-muted text-muted-foreground'; -
✅ Use
cn()utility for className mergingimport { cn } from '@/lib/utils'; className={cn("base-classes", conditional && "conditional-classes", className)} -
✅ Follow spacing scale (multiples of 4: 0, 1, 2, 3, 4, 6, 8, 12, 16)
className = 'p-4 space-y-6 mb-8'; -
✅ Add accessibility attributes
<Label htmlFor="email">Email</Label> <Input id="email" aria-invalid={!!errors.email} aria-describedby={errors.email ? 'email-error' : undefined} /> -
✅ Use component variants
<Button variant="destructive">Delete</Button> <Alert variant="destructive">Error message</Alert> -
✅ Compose from shadcn/ui primitives
// Don't create custom card components // Use Card + CardHeader + CardTitle + CardContent -
✅ Use mobile-first responsive design
className = 'text-2xl sm:text-3xl lg:text-4xl'; className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3';
NEVER Do
-
❌ NO arbitrary colors
// ❌ WRONG className = 'bg-blue-500 text-white'; // ✅ CORRECT className = 'bg-primary text-primary-foreground'; -
❌ NO arbitrary spacing values
// ❌ WRONG className = 'p-[13px] mb-[17px]'; // ✅ CORRECT className = 'p-4 mb-4'; -
❌ NO inline styles
// ❌ WRONG style={{ margin: '10px', color: '#3b82f6' }} // ✅ CORRECT className="m-4 text-primary" -
❌ NO custom CSS classes (use Tailwind utilities)
// ❌ WRONG <div className="my-custom-class"> // ✅ CORRECT <div className="flex items-center justify-between p-4"> -
❌ NO mixing component libraries
// ❌ WRONG - Don't use Material-UI, Ant Design, etc. import { Button } from '@mui/material'; // ✅ CORRECT - Only shadcn/ui import { Button } from '@/components/ui/button'; -
❌ NO skipping accessibility
// ❌ WRONG <button><X /></button> // ✅ CORRECT <Button size="icon" aria-label="Close"> <X className="h-4 w-4" /> </Button> -
❌ NO creating custom variants without CVA
// ❌ WRONG <Button className={type === 'danger' ? 'bg-red-500' : 'bg-blue-500'}> // ✅ CORRECT <Button variant="destructive">Delete</Button>
📐 Layout Patterns
Page Container
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-6">{/* Content */}</div>
</div>
Dashboard Grid
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{items.map((item) => (
<Card key={item.id}>...</Card>
))}
</div>
Form Layout
<Card className="max-w-md mx-auto">
<CardHeader>
<CardTitle>Form Title</CardTitle>
</CardHeader>
<CardContent>
<form className="space-y-4">{/* Form fields */}</form>
</CardContent>
</Card>
Centered Content
<div className="max-w-2xl mx-auto px-4">{/* Readable content width */}</div>
🧩 Component Templates
Custom Component Template
import { cn } from '@/lib/utils';
import { Card } from '@/components/ui/card';
interface MyComponentProps {
variant?: 'default' | 'compact';
className?: string;
children: React.ReactNode;
}
export function MyComponent({ variant = 'default', className, children }: MyComponentProps) {
return (
<Card
className={cn(
'p-4', // base styles
variant === 'compact' && 'p-2',
className // allow overrides
)}
>
{children}
</Card>
);
}
Component with CVA (class-variance-authority)
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const componentVariants = cva(
'base-classes-here', // base
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground',
destructive: 'bg-destructive text-destructive-foreground',
},
size: {
sm: 'h-8 px-3 text-xs',
default: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
interface ComponentProps
extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof componentVariants> {}
export function Component({ variant, size, className, ...props }: ComponentProps) {
return <div className={cn(componentVariants({ variant, size, className }))} {...props} />;
}
📝 Form Pattern Template
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Alert } from '@/components/ui/alert';
const formSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
type FormData = z.infer<typeof formSchema>;
export function MyForm() {
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
email: '',
password: '',
},
});
const onSubmit = async (data: FormData) => {
// Handle submission
};
return (
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
{/* Email Field */}
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
{...form.register('email')}
aria-invalid={!!form.formState.errors.email}
aria-describedby={form.formState.errors.email ? 'email-error' : undefined}
/>
{form.formState.errors.email && (
<p id="email-error" className="text-sm text-destructive">
{form.formState.errors.email.message}
</p>
)}
</div>
{/* Submit Button */}
<Button type="submit" disabled={form.formState.isSubmitting}>
{form.formState.isSubmitting ? 'Submitting...' : 'Submit'}
</Button>
</form>
);
}
🎨 Color Token Reference
Always use these semantic tokens:
| Token | Usage |
|---|---|
bg-primary text-primary-foreground |
Primary buttons, CTAs |
bg-secondary text-secondary-foreground |
Secondary actions |
bg-destructive text-destructive-foreground |
Delete, errors |
bg-muted text-muted-foreground |
Disabled states |
bg-accent text-accent-foreground |
Hover states |
bg-card text-card-foreground |
Card backgrounds |
text-foreground |
Body text |
text-muted-foreground |
Secondary text |
border-border |
Borders |
ring-ring |
Focus rings |
📏 Spacing Reference
Use these spacing values (multiples of 4px):
| Class | Value | Pixels | Usage |
|---|---|---|---|
2 |
0.5rem | 8px | Tight spacing |
4 |
1rem | 16px | Standard spacing |
6 |
1.5rem | 24px | Section spacing |
8 |
2rem | 32px | Large gaps |
12 |
3rem | 48px | Section dividers |
16 |
4rem | 64px | Page sections |
🔑 Decision Trees
When to use Grid vs Flex?
Need equal-width columns? → Use Grid
className="grid grid-cols-3 gap-6"
Need flexible item sizes? → Use Flex
className="flex gap-4"
Need 2D layout (rows + columns)? → Use Grid
className="grid grid-cols-2 grid-rows-3 gap-4"
Need 1D layout (single row OR column)? → Use Flex
className="flex flex-col gap-4"
When to use Margin vs Padding?
Spacing between sibling elements? → Use gap or space-y
className="flex gap-4"
className="space-y-4"
Internal element spacing? → Use padding
className="p-4"
External element spacing? → Avoid margins, use parent gap
// ❌ Child with margin
<div className="mb-4">
// ✅ Parent with gap
<div className="space-y-4">
🚨 Common Mistakes to Avoid
❌ Mistake 1: Hardcoding colors
// ❌ WRONG
<div className="bg-red-500 text-white">Error</div>
// ✅ CORRECT
<Alert variant="destructive">Error message</Alert>
❌ Mistake 2: Arbitrary spacing
// ❌ WRONG
<div className="p-[15px] mb-[23px]">
// ✅ CORRECT
<div className="p-4 mb-6">
❌ Mistake 3: Missing accessibility
// ❌ WRONG
<input type="email" />
// ✅ CORRECT
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" />
❌ Mistake 4: Creating custom components unnecessarily
// ❌ WRONG - Custom component for simple composition
function MyCard({ title, children }) {
return <div className="card">{children}</div>;
}
// ✅ CORRECT - Use shadcn/ui primitives
<Card>
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent>{children}</CardContent>
</Card>;
❌ Mistake 5: Not using cn() utility
// ❌ WRONG
<div className={`base-class ${isActive ? 'active-class' : ''} ${className}`}>
// ✅ CORRECT
<div className={cn("base-class", isActive && "active-class", className)}>
📚 Reference Documentation
Before generating code, check these resources:
- Quick Start - Essential patterns
- Components - All shadcn/ui components
- Layouts - Layout patterns
- Spacing - Spacing rules
- Forms - Form patterns
- Reference - Quick lookup tables
✅ Code Generation Checklist
Before outputting code, verify:
- All imports from
@/components/ui/* - Using semantic color tokens (no
bg-blue-500) - Using spacing scale (multiples of 4)
- Using
cn()for className merging - Accessibility attributes included
- Mobile-first responsive design
- Composing from shadcn/ui primitives
- Following established patterns from docs
- No inline styles
- No arbitrary values
🤖 AI Assistant Configuration
For Claude Code / Cursor
Add this to your project context:
When generating React/Next.js components:
1. Always import from @/components/ui/*
2. Use semantic tokens (bg-primary, text-destructive)
3. Use cn() utility for classNames
4. Follow spacing scale (4, 8, 12, 16, 24, 32)
5. Add accessibility (labels, ARIA)
6. Use component variants (variant="destructive")
7. Reference: /docs/design-system/08-ai-guidelines.md
For GitHub Copilot
Add to .github/copilot-instructions.md:
# Component Guidelines
- Import from @/components/ui/\*
- Use semantic colors: bg-primary, text-destructive
- Spacing: multiples of 4 (p-4, mb-6, gap-8)
- Use cn() for className merging
- Add accessibility attributes
- See /docs/design-system/08-ai-guidelines.md
📊 Examples
✅ Good Component (AI Generated)
import { cn } from '@/lib/utils';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
interface DashboardCardProps {
title: string;
value: string;
trend?: 'up' | 'down';
className?: string;
}
export function DashboardCard({ title, value, trend, className }: DashboardCardProps) {
return (
<Card className={cn('p-6', className)}>
<CardHeader>
<CardTitle className="text-sm font-medium text-muted-foreground">{title}</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{value}</div>
{trend && (
<p
className={cn(
'text-xs',
trend === 'up' && 'text-green-600',
trend === 'down' && 'text-destructive'
)}
>
{trend === 'up' ? '↑' : '↓'} Trend
</p>
)}
</CardContent>
</Card>
);
}
Why it's good:
- ✅ Imports from
@/components/ui/* - ✅ Uses semantic tokens
- ✅ Uses
cn()utility - ✅ Follows spacing scale
- ✅ Composes from shadcn/ui primitives
- ✅ TypeScript interfaces
- ✅ Allows className override
🎓 Learning Path for AI
- Read Quick Start - Essential patterns
- Read this document - Rules and templates
- Reference Component Guide - All components
- Check Reference Tables - Token lookups
With these guidelines, you can generate code that perfectly matches the design system. Always prioritize consistency over creativity.
Last Updated: November 2, 2025 For AI Assistants: Follow these rules strictly for optimal code generation.