159 lines
3.7 KiB
Plaintext
159 lines
3.7 KiB
Plaintext
---
|
|
globs: src/components/**/*.tsx
|
|
---
|
|
|
|
# Component Patterns
|
|
|
|
## Component Structure
|
|
|
|
### File Organization
|
|
- Place reusable components in `src/components/`
|
|
- Use PascalCase for component files: `ComponentName.tsx`
|
|
- Group related components in subdirectories when needed
|
|
|
|
### Component Template
|
|
```typescript
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface ComponentProps {
|
|
className?: string
|
|
children?: React.ReactNode
|
|
// Other props
|
|
}
|
|
|
|
export function ComponentName({
|
|
className,
|
|
children,
|
|
...props
|
|
}: ComponentProps) {
|
|
return (
|
|
<div className={cn("base-styles", className)} {...props}>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## shadcn/ui Integration
|
|
|
|
### Using shadcn/ui Components
|
|
```typescript
|
|
import { Button } from '@/components/ui/button'
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
export function ProjectCard({ project }: { project: Project }) {
|
|
return (
|
|
<Card className="bg-black border-gray-800">
|
|
<CardHeader>
|
|
<CardTitle className="text-white">{project.title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Badge variant="outline">{project.category}</Badge>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Extending shadcn/ui Components
|
|
```typescript
|
|
// Create wrapper components for common patterns
|
|
import { Button } from '@/components/ui/button'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface PrimaryButtonProps extends React.ComponentProps<typeof Button> {
|
|
loading?: boolean
|
|
}
|
|
|
|
export function PrimaryButton({
|
|
loading,
|
|
className,
|
|
children,
|
|
...props
|
|
}: PrimaryButtonProps) {
|
|
return (
|
|
<Button
|
|
className={cn("bg-primary hover:bg-primary/90", className)}
|
|
disabled={loading}
|
|
{...props}
|
|
>
|
|
{loading ? "Loading..." : children}
|
|
</Button>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Animation Patterns
|
|
|
|
### Framer Motion Usage
|
|
```typescript
|
|
import { motion } from 'framer-motion'
|
|
|
|
export function AnimatedCard({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
className="card-styles"
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Component Composition
|
|
|
|
### Compound Components
|
|
```typescript
|
|
// Parent component
|
|
export function Accordion({ children }: { children: React.ReactNode }) {
|
|
return <div className="accordion-container">{children}</div>
|
|
}
|
|
|
|
// Child components
|
|
export function AccordionItem({ children }: { children: React.ReactNode }) {
|
|
return <div className="accordion-item">{children}</div>
|
|
}
|
|
|
|
export function AccordionTrigger({ children }: { children: React.ReactNode }) {
|
|
return <button className="accordion-trigger">{children}</button>
|
|
}
|
|
|
|
// Usage
|
|
<Accordion>
|
|
<AccordionItem>
|
|
<AccordionTrigger>Title</AccordionTrigger>
|
|
<AccordionContent>Content</AccordionContent>
|
|
</AccordionItem>
|
|
</Accordion>
|
|
```
|
|
|
|
## Props and TypeScript
|
|
|
|
### Interface Patterns
|
|
```typescript
|
|
// Use descriptive interface names
|
|
interface ProjectCardProps {
|
|
project: Project
|
|
variant?: 'default' | 'featured'
|
|
showDescription?: boolean
|
|
onSelect?: (project: Project) => void
|
|
}
|
|
|
|
// Use React.ComponentProps for extending HTML elements
|
|
interface CustomButtonProps extends React.ComponentProps<'button'> {
|
|
variant?: 'primary' | 'secondary'
|
|
size?: 'sm' | 'md' | 'lg'
|
|
}
|
|
```
|
|
|
|
## Styling Guidelines
|
|
|
|
1. **Tailwind First**: Use utility classes before custom CSS
|
|
2. **Conditional Classes**: Use `cn()` utility for conditional styling
|
|
3. **Responsive Design**: Mobile-first approach with responsive utilities
|
|
4. **Dark Theme**: Ensure all components work in dark mode
|
|
5. **Accessibility**: Include proper ARIA labels and semantic HTML |