Added Cursor Rules
This commit is contained in:
parent
a2c67c3fb9
commit
a76e20e91f
159
.cursor/rules/component-patterns.mdc
Normal file
159
.cursor/rules/component-patterns.mdc
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
97
.cursor/rules/data-content.mdc
Normal file
97
.cursor/rules/data-content.mdc
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
globs: src/data/**/*.ts,src/data/**/*.json
|
||||||
|
---
|
||||||
|
|
||||||
|
# Data and Content Management
|
||||||
|
|
||||||
|
## Data Structure
|
||||||
|
|
||||||
|
Non-secret content belongs in `src/data/` as TypeScript modules or JSON files. Keep data presentation-agnostic.
|
||||||
|
|
||||||
|
## Current Data Files
|
||||||
|
|
||||||
|
- [src/data/projects.ts](mdc:src/data/projects.ts) - Project portfolio data
|
||||||
|
- [src/data/services.ts](mdc:src/data/services.ts) - Service offerings data
|
||||||
|
|
||||||
|
## Data Patterns
|
||||||
|
|
||||||
|
### TypeScript Data Modules
|
||||||
|
```typescript
|
||||||
|
// src/data/projects.ts
|
||||||
|
export interface Project {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
category: string
|
||||||
|
images: string[]
|
||||||
|
videoUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const projects: Project[] = [
|
||||||
|
{
|
||||||
|
id: 'project-1',
|
||||||
|
title: 'Project Title',
|
||||||
|
description: 'Project description',
|
||||||
|
category: 'commercial',
|
||||||
|
images: ['/image1.jpg', '/image2.jpg']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON Data Files
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"id": "vfx",
|
||||||
|
"name": "Visual Effects",
|
||||||
|
"description": "High-end VFX services"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Usage Rules
|
||||||
|
|
||||||
|
1. **Server Components**: Prefer server components for data fetching
|
||||||
|
2. **File Imports**: Use direct imports instead of client-side fetching for static data
|
||||||
|
3. **Type Safety**: Define TypeScript interfaces for all data structures
|
||||||
|
4. **Separation**: Keep data separate from presentation logic
|
||||||
|
|
||||||
|
## Content Guidelines
|
||||||
|
|
||||||
|
- Use descriptive, SEO-friendly content
|
||||||
|
- Include proper alt text for images
|
||||||
|
- Maintain consistent naming conventions
|
||||||
|
- Keep content up-to-date and accurate
|
||||||
|
|
||||||
|
## Data Fetching Patterns
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: Server component with direct import
|
||||||
|
import { projects } from '@/data/projects'
|
||||||
|
|
||||||
|
export default function PortfolioPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{projects.map(project => (
|
||||||
|
<ProjectCard key={project.id} project={project} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Avoid: Client-side fetching of static data
|
||||||
|
'use client'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
export function PortfolioPage() {
|
||||||
|
const [projects, setProjects] = useState([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/projects').then(res => res.json())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
73
.cursor/rules/deployment.mdc
Normal file
73
.cursor/rules/deployment.mdc
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Deployment and Build Process
|
||||||
|
|
||||||
|
## Cloudflare Workers with OpenNext
|
||||||
|
|
||||||
|
This project deploys to Cloudflare Workers using OpenNext for Next.js compatibility.
|
||||||
|
|
||||||
|
### Build Process
|
||||||
|
|
||||||
|
1. **Quality Gates** (run before build):
|
||||||
|
```bash
|
||||||
|
npm run type-check # TypeScript validation
|
||||||
|
npm run lint # ESLint validation
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Production Build**:
|
||||||
|
```bash
|
||||||
|
npm run build # Next.js build
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **OpenNext Build**:
|
||||||
|
```bash
|
||||||
|
npx open-next@latest build # Generate Cloudflare-compatible build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Deploy**:
|
||||||
|
```bash
|
||||||
|
npx wrangler deploy .open-next/worker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
|
||||||
|
- [wrangler.toml](mdc:wrangler.toml) - Cloudflare Workers configuration
|
||||||
|
- [open-next.config.ts](mdc:open-next.config.ts) - OpenNext build configuration
|
||||||
|
- [next.config.ts](mdc:next.config.ts) - Next.js configuration
|
||||||
|
|
||||||
|
### Required wrangler.toml Settings
|
||||||
|
|
||||||
|
```toml
|
||||||
|
name = "site-worker"
|
||||||
|
main = ".open-next/worker/index.mjs"
|
||||||
|
compatibility_date = "2024-09-23"
|
||||||
|
compatibility_flags = ["nodejs_compat"]
|
||||||
|
assets = { directory = ".open-next/assets" }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Create `.env.sample` and keep it synchronized. Typical keys:
|
||||||
|
```
|
||||||
|
NEXT_PUBLIC_SITE_URL=
|
||||||
|
RESEND_API_KEY=
|
||||||
|
CF_PAGES_URL=
|
||||||
|
```
|
||||||
|
|
||||||
|
**Security**: Never commit real secrets. Use `.env` locally and environment variables in production.
|
||||||
|
|
||||||
|
## Build Configuration
|
||||||
|
|
||||||
|
- ESLint and TypeScript errors are ignored during build for deployment speed
|
||||||
|
- CI still gates on `lint` and `type-check` before merge
|
||||||
|
- Always fix errors instead of bypassing checks
|
||||||
|
|
||||||
|
## Deployment Checklist
|
||||||
|
|
||||||
|
- [ ] Run `npm run type-check` and `npm run lint`
|
||||||
|
- [ ] Ensure `assets.directory` matches OpenNext output
|
||||||
|
- [ ] Keep compatibility date at or after 2024-09-23
|
||||||
|
- [ ] Test build locally with `npm run build`
|
||||||
|
- [ ] Verify OpenNext build artifacts in `.open-next/`
|
||||||
74
.cursor/rules/development-workflow.mdc
Normal file
74
.cursor/rules/development-workflow.mdc
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Development Workflow
|
||||||
|
|
||||||
|
## Git Workflow
|
||||||
|
|
||||||
|
- **Default Branch**: `main` is protected
|
||||||
|
- **Workflow**: feature branches → PR → required checks → squash merge
|
||||||
|
- **Commit Format**: Conventional Commits
|
||||||
|
- `feat: add contact form schema`
|
||||||
|
- `fix: correct Image remote pattern`
|
||||||
|
- `chore: bump dependencies`
|
||||||
|
|
||||||
|
## Required Checks
|
||||||
|
|
||||||
|
Before any merge:
|
||||||
|
- `npm run lint` - ESLint validation
|
||||||
|
- `npm run type-check` - TypeScript validation
|
||||||
|
- `npm run build` - Production build (optional locally, required in CI)
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Setup
|
||||||
|
npm ci # Install dependencies
|
||||||
|
|
||||||
|
# Development
|
||||||
|
npm run dev # Dev server with Turbopack
|
||||||
|
npm run type-check # TypeScript validation
|
||||||
|
npm run lint # ESLint validation
|
||||||
|
|
||||||
|
# Build & Deploy
|
||||||
|
npm run build # Production build
|
||||||
|
npm run start # Preview production build
|
||||||
|
npx open-next@latest build # OpenNext build
|
||||||
|
npx wrangler deploy .open-next/worker # Deploy to Cloudflare
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
### TypeScript
|
||||||
|
- Use strict mode (enabled in [tsconfig.json](mdc:tsconfig.json))
|
||||||
|
- Prefer type inference over explicit types
|
||||||
|
- Use absolute imports with `@` alias
|
||||||
|
|
||||||
|
### ESLint
|
||||||
|
- Follow Next.js ESLint config
|
||||||
|
- Fix all linting errors before committing
|
||||||
|
- Don't bypass checks in production builds
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
- **Unit Tests**: Place close to sources, name with `.test.ts` or `.test.tsx`
|
||||||
|
- **E2E Tests**: Optional Playwright setup
|
||||||
|
- **Manual Testing**: Test all user flows before deployment
|
||||||
|
|
||||||
|
## Pull Request Guidelines
|
||||||
|
|
||||||
|
- Keep PRs small and reviewable
|
||||||
|
- Include screenshots for UI changes
|
||||||
|
- Update [AGENTS.md](mdc:AGENTS.md) if conventions change
|
||||||
|
- Justify new dependencies in PR description
|
||||||
|
- Never commit secrets or sensitive data
|
||||||
|
|
||||||
|
## Common Pitfalls to Avoid
|
||||||
|
|
||||||
|
1. Adding remote image domains without updating [next.config.ts](mdc:next.config.ts)
|
||||||
|
2. Introducing client components unnecessarily
|
||||||
|
3. Duplicating navigation in nested layouts
|
||||||
|
4. Bypassing Tailwind utilities for custom CSS
|
||||||
|
5. Forgetting to update middleware whitelist for new static assets
|
||||||
|
6. Committing secrets instead of using environment variables
|
||||||
87
.cursor/rules/forms-validation.mdc
Normal file
87
.cursor/rules/forms-validation.mdc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
globs: **/*form*.tsx,**/*Form*.tsx
|
||||||
|
---
|
||||||
|
|
||||||
|
# Forms and Validation
|
||||||
|
|
||||||
|
## Form Library Stack
|
||||||
|
|
||||||
|
- **Forms**: react-hook-form for form state management
|
||||||
|
- **Validation**: Zod schemas for type-safe validation
|
||||||
|
- **Resolvers**: @hookform/resolvers for integration
|
||||||
|
|
||||||
|
## Form Structure
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useForm } from 'react-hook-form'
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
// Define schema
|
||||||
|
const formSchema = z.object({
|
||||||
|
name: z.string().min(1, 'Name is required'),
|
||||||
|
email: z.string().email('Invalid email address'),
|
||||||
|
message: z.string().min(10, 'Message must be at least 10 characters')
|
||||||
|
})
|
||||||
|
|
||||||
|
type FormData = z.infer<typeof formSchema>
|
||||||
|
|
||||||
|
export function ContactForm() {
|
||||||
|
const form = useForm<FormData>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = (data: FormData) => {
|
||||||
|
// Handle form submission
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
|
{/* Form fields with error handling */}
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling Rules
|
||||||
|
|
||||||
|
1. **Field-level errors**: Show validation errors under each field
|
||||||
|
2. **Generic submit error**: Display general submission errors
|
||||||
|
3. **Never swallow errors**: Always surface validation and submission errors
|
||||||
|
4. **Loading states**: Show loading indicators during submission
|
||||||
|
|
||||||
|
## Form Components
|
||||||
|
|
||||||
|
Use shadcn/ui form components from [src/components/ui/form.tsx](mdc:src/components/ui/form.tsx):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="your@email.com" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Patterns
|
||||||
|
|
||||||
|
- Use Zod for all form validation
|
||||||
|
- Provide clear, user-friendly error messages
|
||||||
|
- Validate on both client and server side
|
||||||
|
- Handle async validation (e.g., email uniqueness)
|
||||||
75
.cursor/rules/images-assets.mdc
Normal file
75
.cursor/rules/images-assets.mdc
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
globs: **/*.tsx,**/*.ts
|
||||||
|
---
|
||||||
|
|
||||||
|
# Images and Assets
|
||||||
|
|
||||||
|
## Image Handling
|
||||||
|
|
||||||
|
### Next.js Image Component
|
||||||
|
Always use Next.js Image component for remote images:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
<Image
|
||||||
|
src="https://images.unsplash.com/photo-123"
|
||||||
|
alt="Descriptive alt text"
|
||||||
|
width={800}
|
||||||
|
height={600}
|
||||||
|
className="rounded-lg"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remote Image Domains
|
||||||
|
Current allowed domains in [next.config.ts](mdc:next.config.ts):
|
||||||
|
- `images.unsplash.com`
|
||||||
|
|
||||||
|
**When adding new external domains:**
|
||||||
|
1. Add to `remotePatterns` in [next.config.ts](mdc:next.config.ts)
|
||||||
|
2. Document the change in [AGENTS.md](mdc:AGENTS.md)
|
||||||
|
|
||||||
|
## Static Assets
|
||||||
|
|
||||||
|
### Public Directory
|
||||||
|
- Keep `public/` for truly static assets only
|
||||||
|
- Current assets: favicon files, images (OLIVER.jpeg, etc.), GIFs
|
||||||
|
|
||||||
|
### Middleware Whitelist
|
||||||
|
**CRITICAL**: When adding new static assets to `public/`, update the middleware allowlist in [src/middleware.ts](mdc:src/middleware.ts) line 8:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Add new asset paths here
|
||||||
|
if (pathname === '/' ||
|
||||||
|
pathname.startsWith('/_next') ||
|
||||||
|
pathname.startsWith('/favicon.') ||
|
||||||
|
pathname === '/OLIVER.jpeg' ||
|
||||||
|
pathname === '/new-asset.jpg' || // Add new assets here
|
||||||
|
pathname === '/reel.mp4') {
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common symptom**: If assets return "text/html" Content-Type error, the path isn't whitelisted.
|
||||||
|
|
||||||
|
## Asset Optimization
|
||||||
|
|
||||||
|
- Use appropriate image formats (WebP when possible)
|
||||||
|
- Provide proper alt text for accessibility
|
||||||
|
- Use responsive images with `sizes` prop
|
||||||
|
- Optimize file sizes for web delivery
|
||||||
|
|
||||||
|
## Video Files
|
||||||
|
|
||||||
|
Custom headers are configured in [next.config.ts](mdc:next.config.ts) for `.mp4` files:
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
source: "/:path*.mp4",
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
key: "Content-Type",
|
||||||
|
value: "video/mp4",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
60
.cursor/rules/project-structure.mdc
Normal file
60
.cursor/rules/project-structure.mdc
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Biohazard VFX Website - Project Structure
|
||||||
|
|
||||||
|
This is a Next.js 15.5.4 VFX studio website built with React 19, TypeScript, Tailwind CSS 4, and shadcn/ui components.
|
||||||
|
|
||||||
|
## Core Architecture
|
||||||
|
|
||||||
|
- **Framework**: Next.js 15.5.4 with App Router
|
||||||
|
- **Styling**: Tailwind CSS 4 + shadcn/ui components
|
||||||
|
- **Animation**: Framer Motion for subtle transitions
|
||||||
|
- **Forms**: react-hook-form + Zod validation
|
||||||
|
- **Deployment**: Cloudflare Workers via OpenNext
|
||||||
|
- **Package Manager**: npm
|
||||||
|
|
||||||
|
## Project Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├─ app/ # App Router pages and layouts
|
||||||
|
│ ├─ (marketing)/ # Route groups
|
||||||
|
│ ├─ api/ # Route handlers
|
||||||
|
│ └─ layout.tsx # Root layout with global providers
|
||||||
|
├─ components/ # Reusable UI components
|
||||||
|
│ └─ ui/ # shadcn/ui primitives
|
||||||
|
├─ data/ # JSON/TS data objects
|
||||||
|
├─ lib/ # Utilities, hooks, server actions
|
||||||
|
├─ styles/ # globals.css, Tailwind utilities
|
||||||
|
└─ types/ # Shared TypeScript types
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
- [AGENTS.md](mdc:AGENTS.md) - Single source of truth for development guidelines
|
||||||
|
- [package.json](mdc:package.json) - Dependencies and scripts
|
||||||
|
- [next.config.ts](mdc:next.config.ts) - Next.js configuration
|
||||||
|
- [tsconfig.json](mdc:tsconfig.json) - TypeScript configuration with @ alias
|
||||||
|
- [src/middleware.ts](mdc:src/middleware.ts) - Route protection and redirects
|
||||||
|
- [src/app/layout.tsx](mdc:src/app/layout.tsx) - Root layout with fonts and metadata
|
||||||
|
|
||||||
|
## Import Aliases
|
||||||
|
|
||||||
|
Always use absolute imports with `@` mapped to `src/`:
|
||||||
|
```typescript
|
||||||
|
import { Component } from '@/components/Component'
|
||||||
|
import { data } from '@/data/projects'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci # Install dependencies
|
||||||
|
npm run dev # Development server with Turbopack
|
||||||
|
npm run type-check # TypeScript validation
|
||||||
|
npm run lint # ESLint validation
|
||||||
|
npm run build # Production build
|
||||||
|
npx open-next@latest build # OpenNext build for Cloudflare
|
||||||
|
```
|
||||||
67
.cursor/rules/routing-layout.mdc
Normal file
67
.cursor/rules/routing-layout.mdc
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
globs: src/app/**/*.tsx
|
||||||
|
---
|
||||||
|
|
||||||
|
# Routing and Layout Rules
|
||||||
|
|
||||||
|
## App Router Structure
|
||||||
|
|
||||||
|
- **Pages**: Live in `src/app/` directory
|
||||||
|
- **Server Components**: Default to server components, promote to client only when needed
|
||||||
|
- **Layout Hierarchy**: Root layout owns global providers, navigation, and footer
|
||||||
|
- **Route Groups**: Use `(marketing)` for grouped routes without affecting URL structure
|
||||||
|
|
||||||
|
## Layout Rules
|
||||||
|
|
||||||
|
### Root Layout ([src/app/layout.tsx](mdc:src/app/layout.tsx))
|
||||||
|
- Owns global providers and theme class
|
||||||
|
- Contains `<Navigation />` and `<Footer />` components
|
||||||
|
- Sets up font variables and metadata
|
||||||
|
- **DO NOT** duplicate these in child layouts
|
||||||
|
|
||||||
|
### Page Layouts
|
||||||
|
- Keep server components as default
|
||||||
|
- Add `"use client"` only when necessary for interactivity
|
||||||
|
- Define unique metadata for each route
|
||||||
|
|
||||||
|
## Metadata Requirements
|
||||||
|
|
||||||
|
Every page must have:
|
||||||
|
```typescript
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Unique Page Title",
|
||||||
|
description: "Unique description for SEO",
|
||||||
|
// Include Open Graph and Twitter cards
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Route Protection
|
||||||
|
|
||||||
|
The [src/middleware.ts](mdc:src/middleware.ts) currently redirects all routes to `/` except:
|
||||||
|
- Home page (`/`)
|
||||||
|
- Next.js internal routes (`/_next/*`)
|
||||||
|
- Favicon files
|
||||||
|
- Specific static assets (OLIVER.jpeg, OLIVER_depth.jpeg, etc.)
|
||||||
|
|
||||||
|
## Static Assets
|
||||||
|
|
||||||
|
When adding new files to `public/`, update the middleware allowlist in [src/middleware.ts](mdc:src/middleware.ts) line 8 to prevent 307 redirects.
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Page component
|
||||||
|
export default function PageName() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
{/* Page content */}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With metadata
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Page Title",
|
||||||
|
description: "Page description"
|
||||||
|
}
|
||||||
|
```
|
||||||
78
.cursor/rules/seo-metadata.mdc
Normal file
78
.cursor/rules/seo-metadata.mdc
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
globs: src/app/**/page.tsx,src/app/**/layout.tsx
|
||||||
|
---
|
||||||
|
|
||||||
|
# SEO and Metadata
|
||||||
|
|
||||||
|
## Metadata API Requirements
|
||||||
|
|
||||||
|
Every page must define unique metadata using the Next.js Metadata API:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { Metadata } from "next"
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Unique Page Title | Biohazard VFX",
|
||||||
|
description: "Unique, descriptive page description for SEO",
|
||||||
|
metadataBase: new URL("https://biohazardvfx.com"),
|
||||||
|
openGraph: {
|
||||||
|
title: "Page Title",
|
||||||
|
description: "Page description",
|
||||||
|
type: "website",
|
||||||
|
locale: "en_US",
|
||||||
|
siteName: "Biohazard VFX",
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title: "Page Title",
|
||||||
|
description: "Page description",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Root Layout Metadata
|
||||||
|
|
||||||
|
The root layout in [src/app/layout.tsx](mdc:src/app/layout.tsx) includes:
|
||||||
|
- Global site metadata
|
||||||
|
- Open Graph configuration
|
||||||
|
- Twitter card setup
|
||||||
|
- JSON-LD structured data
|
||||||
|
- Canonical URLs
|
||||||
|
|
||||||
|
## SEO Best Practices
|
||||||
|
|
||||||
|
1. **Unique Titles**: Each page must have a unique, descriptive title
|
||||||
|
2. **Descriptions**: Write compelling meta descriptions (150-160 characters)
|
||||||
|
3. **Structured Data**: Use JSON-LD for rich snippets
|
||||||
|
4. **Canonical URLs**: Set canonical URLs to prevent duplicate content
|
||||||
|
5. **Open Graph**: Include OG tags for social media sharing
|
||||||
|
6. **Twitter Cards**: Configure Twitter card metadata
|
||||||
|
|
||||||
|
## Structured Data Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const jsonLd = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
name: "Biohazard VFX",
|
||||||
|
description: "Visual effects studio",
|
||||||
|
url: "https://biohazardvfx.com",
|
||||||
|
logo: "https://biohazardvfx.com/logo.png",
|
||||||
|
sameAs: ["https://instagram.com/biohazardvfx"],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Page-Specific Metadata
|
||||||
|
|
||||||
|
- **Home**: Focus on main services and value proposition
|
||||||
|
- **Portfolio**: Highlight featured projects and capabilities
|
||||||
|
- **Services**: Target specific service keywords
|
||||||
|
- **About**: Include company information and team details
|
||||||
|
- **Contact**: Include location and contact information
|
||||||
|
|
||||||
|
## Image SEO
|
||||||
|
|
||||||
|
- Use descriptive alt text for all images
|
||||||
|
- Optimize image file names
|
||||||
|
- Include image dimensions
|
||||||
|
- Use appropriate image formats (WebP when possible)
|
||||||
60
.cursor/rules/ui-system.mdc
Normal file
60
.cursor/rules/ui-system.mdc
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
globs: *.tsx,*.ts,*.css
|
||||||
|
---
|
||||||
|
|
||||||
|
# UI System Guidelines
|
||||||
|
|
||||||
|
## Theme & Design System
|
||||||
|
|
||||||
|
- **Default Theme**: Dark mode only - do not introduce light-first designs
|
||||||
|
- **Typography**: Use CSS variables for fonts (Geist, Geist Mono, Bebas Neue, Orbitron, etc.)
|
||||||
|
- **Components**: Use shadcn/ui primitives from [src/components/ui/](mdc:src/components/ui/)
|
||||||
|
- **Spacing**: Follow Tailwind 4 defaults, prefer utility classes over custom CSS
|
||||||
|
- **Animation**: Keep Framer Motion subtle and meaningful only
|
||||||
|
|
||||||
|
## Component Structure
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Use shadcn/ui primitives as base
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Card } from '@/components/ui/card'
|
||||||
|
|
||||||
|
// Extend with local wrappers when needed
|
||||||
|
export function CustomComponent() {
|
||||||
|
return (
|
||||||
|
<Card className="bg-black border-gray-800">
|
||||||
|
<Button variant="outline" className="text-white">
|
||||||
|
Action
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styling Rules
|
||||||
|
|
||||||
|
1. **Dark Theme**: All components must work in dark mode
|
||||||
|
2. **Tailwind First**: Use utility classes before custom CSS
|
||||||
|
3. **Component Variants**: Use class-variance-authority for component variants
|
||||||
|
4. **Responsive**: Mobile-first responsive design
|
||||||
|
5. **Accessibility**: Include proper ARIA labels and semantic HTML
|
||||||
|
|
||||||
|
## Font Usage
|
||||||
|
|
||||||
|
Available font variables from [src/app/layout.tsx](mdc:src/app/layout.tsx):
|
||||||
|
- `--font-geist-sans` (default)
|
||||||
|
- `--font-geist-mono`
|
||||||
|
- `--font-bebas`
|
||||||
|
- `--font-orbitron`
|
||||||
|
- `--font-inter`
|
||||||
|
- `--font-jetbrains-mono`
|
||||||
|
- `--font-space-mono`
|
||||||
|
- `--font-rajdhani`
|
||||||
|
- `--font-exo-2`
|
||||||
|
|
||||||
|
## Animation Guidelines
|
||||||
|
|
||||||
|
- Use Framer Motion sparingly for meaningful transitions
|
||||||
|
- Prefer CSS transitions for simple hover effects
|
||||||
|
- Keep animations under 300ms for UI feedback
|
||||||
|
- Respect `prefers-reduced-motion` for accessibility
|
||||||
99
.cursor/rules/vfx-specific.mdc
Normal file
99
.cursor/rules/vfx-specific.mdc
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
description: VFX studio specific patterns and requirements
|
||||||
|
---
|
||||||
|
|
||||||
|
# VFX Studio Specific Guidelines
|
||||||
|
|
||||||
|
## Media Handling
|
||||||
|
|
||||||
|
### Video Components
|
||||||
|
- Use [src/components/VideoPlayer.tsx](mdc:src/components/VideoPlayer.tsx) for video playback
|
||||||
|
- Use [src/components/ReelPlayer.tsx](mdc:src/components/ReelPlayer.tsx) for demo reels
|
||||||
|
- Support multiple video formats (MP4, WebM)
|
||||||
|
- Include proper video metadata and thumbnails
|
||||||
|
|
||||||
|
### Image Components
|
||||||
|
- Use [src/components/DepthMap.tsx](mdc:src/components/DepthMap.tsx) for depth map visualizations
|
||||||
|
- Implement lazy loading for portfolio images
|
||||||
|
- Use Next.js Image optimization for all media
|
||||||
|
|
||||||
|
## Portfolio Patterns
|
||||||
|
|
||||||
|
### Project Showcase
|
||||||
|
```typescript
|
||||||
|
// Use ProjectCard component for consistent project display
|
||||||
|
import { ProjectCard } from '@/components/ProjectCard'
|
||||||
|
import { ProjectShowcase } from '@/components/ProjectShowcase'
|
||||||
|
|
||||||
|
// Project data structure from src/data/projects.ts
|
||||||
|
interface Project {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
category: 'commercial' | 'music-video' | 'film' | 'animation'
|
||||||
|
client?: string
|
||||||
|
year: number
|
||||||
|
images: string[]
|
||||||
|
videoUrl?: string
|
||||||
|
tags: string[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Categories
|
||||||
|
- Visual Effects (VFX)
|
||||||
|
- Motion Graphics
|
||||||
|
- 3D Animation
|
||||||
|
- Compositing
|
||||||
|
- Color Grading
|
||||||
|
- Post-Production
|
||||||
|
|
||||||
|
## Client Work Patterns
|
||||||
|
|
||||||
|
### Client Logo Grid
|
||||||
|
- Use [src/components/ClientLogoGrid.tsx](mdc:src/components/ClientLogoGrid.tsx)
|
||||||
|
- Display client logos with proper attribution
|
||||||
|
- Ensure logos are high-quality and properly sized
|
||||||
|
|
||||||
|
### Project Filtering
|
||||||
|
- Implement category-based filtering
|
||||||
|
- Support tag-based search
|
||||||
|
- Include year-based sorting
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Media Optimization
|
||||||
|
- Compress images and videos for web delivery
|
||||||
|
- Use appropriate formats (WebP for images, MP4 for videos)
|
||||||
|
- Implement progressive loading for large media files
|
||||||
|
- Use CDN for media delivery
|
||||||
|
|
||||||
|
### Loading States
|
||||||
|
- Show skeleton loaders for media content
|
||||||
|
- Implement progressive image loading
|
||||||
|
- Use intersection observer for lazy loading
|
||||||
|
|
||||||
|
## VFX-Specific UI Elements
|
||||||
|
|
||||||
|
### Before/After Comparisons
|
||||||
|
- Implement split-screen comparisons
|
||||||
|
- Use slider controls for reveal effects
|
||||||
|
- Include toggle for before/after views
|
||||||
|
|
||||||
|
### Process Showcases
|
||||||
|
- Show breakdowns of VFX work
|
||||||
|
- Include wireframe and final render comparisons
|
||||||
|
- Display technical specifications
|
||||||
|
|
||||||
|
## Content Guidelines
|
||||||
|
|
||||||
|
### Project Descriptions
|
||||||
|
- Include technical details (software used, techniques)
|
||||||
|
- Mention client and project scope
|
||||||
|
- Highlight challenges and solutions
|
||||||
|
- Use industry-standard terminology
|
||||||
|
|
||||||
|
### Service Descriptions
|
||||||
|
- Be specific about capabilities
|
||||||
|
- Include typical project timelines
|
||||||
|
- Mention software and hardware capabilities
|
||||||
|
- Provide clear pricing structure (if applicable)
|
||||||
Loading…
x
Reference in New Issue
Block a user