- **Component Creation Guide:** Document best practices for creating reusable, accessible components using CVA patterns. Includes guidance on when to compose vs create, decision trees, templates, prop design, testing checklists, and real-world examples. - **Design System README:** Introduce an organized structure for the design system documentation with quick navigation, learning paths, and reference links to key topics. Includes paths for quick starts, layouts, components, forms, and AI setup.
13 KiB
AI Code Generation Guidelines
For AI Assistants: This document contains strict rules for generating code in the FastNext Template 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.