perf: eliminate Three.js/WebGL, fix render-blocking CSS, add dev team agents
- Replace ShaderGradientBackground WebGL shader (3 static spheres) with pure CSS radial-gradient divs — moves from ClientProviders (deferred JS) to app/layout.tsx as a server component rendered in initial HTML. Eliminates @shadergradient/react, three, @react-three/fiber from the JS bundle. Removes chunks/7001 (~20s CPU eval) and the 39s main thread block. - Remove optimizeCss/critters: it was converting <link rel="stylesheet"> to a JS-deferred preload, which PageSpeed read as a 410ms sequential CSS chain. Both CSS files now load as parallel <link> tags from initial HTML (~150ms). - Update browserslist safari >= 15 → 15.4 (Array.prototype.at, Object.hasOwn are native in 15.4+; eliminates unnecessary SWC compatibility transforms). - Delete orphaned app/styles/ghostContent.css (never imported anywhere, 3.7KB). - Add .claude/ dev team setup: 5 subagents (frontend-dev, backend-dev, tester, code-reviewer, debugger), 3 skills (/add-section, /review-changes, /check-quality), 3 path-scoped rules, settings.json with auto-lint hook. - Update CLAUDE.md with server/client orchestrator pattern, SSR animation safety rules, API route conventions, and improved command reference. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
35
.claude/rules/api-routes.md
Normal file
35
.claude/rules/api-routes.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
paths:
|
||||
- "app/api/**/*.ts"
|
||||
---
|
||||
|
||||
# API Route Rules
|
||||
|
||||
Every API route in this project must follow these conventions:
|
||||
|
||||
## Required exports
|
||||
```typescript
|
||||
export const runtime = 'nodejs'
|
||||
export const dynamic = 'force-dynamic'
|
||||
```
|
||||
|
||||
## Response format
|
||||
All responses must include a `source` field:
|
||||
```typescript
|
||||
return NextResponse.json({ data: ..., source: 'directus' | 'fallback' | 'error' })
|
||||
```
|
||||
|
||||
## Error handling
|
||||
- Wrap all external calls (Directus, n8n, Redis, PostgreSQL) in try/catch
|
||||
- Return graceful fallback data on failure — never let an external service crash the page
|
||||
- Error logging: `if (process.env.NODE_ENV === "development") console.error(...)`
|
||||
|
||||
## n8n proxies (app/api/n8n/)
|
||||
- Rate limiting required on all public endpoints (use `lib/auth.ts`)
|
||||
- 10 second timeout on upstream n8n calls
|
||||
- Auth via `N8N_SECRET_TOKEN` and/or `N8N_API_KEY` headers
|
||||
|
||||
## Directus queries
|
||||
- Use `directusRequest()` from `lib/directus.ts`
|
||||
- 2 second timeout is already set in `directusRequest()`
|
||||
- Always have a hardcoded fallback when Directus returns null
|
||||
37
.claude/rules/components.md
Normal file
37
.claude/rules/components.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
paths:
|
||||
- "app/components/**/*.tsx"
|
||||
- "app/_ui/**/*.tsx"
|
||||
---
|
||||
|
||||
# Component Rules
|
||||
|
||||
## SSR animation safety (critical)
|
||||
**Never** use `initial={{ opacity: 0 }}` on server-rendered elements.
|
||||
This bakes `style="opacity:0"` into HTML — content is invisible if hydration fails.
|
||||
|
||||
Use `ScrollFadeIn` instead:
|
||||
```tsx
|
||||
import ScrollFadeIn from "@/app/components/ScrollFadeIn"
|
||||
<ScrollFadeIn><MyComponent /></ScrollFadeIn>
|
||||
```
|
||||
|
||||
`AnimatePresence` is fine for modals and overlays that only appear after user interaction.
|
||||
|
||||
## Design system
|
||||
- Colors: only `liquid-*` tokens — no hardcoded hex or raw Tailwind palette colors
|
||||
- Cards: `bg-gradient-to-br from-liquid-*/15 via-liquid-*/10 to-liquid-*/15 backdrop-blur-sm border-2 rounded-xl`
|
||||
- Headlines: `uppercase tracking-tighter` with accent dot `<span className="text-emerald-600">.</span>`
|
||||
- Body text: `text-stone-600 dark:text-stone-400` — never `text-stone-400` alone (fails contrast)
|
||||
|
||||
## Async components
|
||||
Every component that fetches data must have a Skeleton loading state shown while data loads.
|
||||
|
||||
## i18n
|
||||
- Client: `useTranslations("namespace")` from `next-intl`
|
||||
- Server: `getTranslations("namespace")` from `next-intl/server`
|
||||
- New client sections need a wrapper in `ClientWrappers.tsx` with scoped `NextIntlClientProvider`
|
||||
|
||||
## TypeScript
|
||||
- No `any` — define interfaces in `lib/directus.ts` or `types/`
|
||||
- No emojis in code
|
||||
38
.claude/rules/testing.md
Normal file
38
.claude/rules/testing.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
paths:
|
||||
- "**/__tests__/**/*.ts"
|
||||
- "**/__tests__/**/*.tsx"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "e2e/**/*.spec.ts"
|
||||
---
|
||||
|
||||
# Testing Rules
|
||||
|
||||
## Jest environment
|
||||
- Global mocks are set up in `jest.setup.ts` — do NOT re-mock `matchMedia`, `IntersectionObserver`, or `NextResponse` in individual tests
|
||||
- Test env vars are pre-set: `DIRECTUS_URL`, `NEXT_PUBLIC_SITE_URL`
|
||||
- Always mock external API calls (Directus, n8n, PostgreSQL) — tests must work without running services
|
||||
|
||||
## ESM modules
|
||||
If a new import causes "Must use import to load ES Module" errors, add the package to `transformIgnorePatterns` in `jest.config.ts`.
|
||||
|
||||
## Server component tests
|
||||
```typescript
|
||||
// Server components return JSX, not a promise in React 19, but async ones need await
|
||||
const resolved = await MyServerComponent({ locale: 'en', ...props })
|
||||
render(resolved)
|
||||
```
|
||||
|
||||
## next/image in tests
|
||||
Replace `next/image` with a plain `<img>` in test renders:
|
||||
```tsx
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img src={src} alt={alt} />
|
||||
```
|
||||
|
||||
## Run commands
|
||||
- Single file: `npx jest path/to/test.tsx`
|
||||
- All unit tests: `npm run test`
|
||||
- Watch mode: `npm run test:watch`
|
||||
- Specific E2E: `npm run test:critical`, `npm run test:hydration`, `npm run test:accessibility`
|
||||
Reference in New Issue
Block a user