- 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>
1.3 KiB
1.3 KiB
paths
| paths | ||
|---|---|---|
|
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:
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-tighterwith accent dot<span className="text-emerald-600">.</span> - Body text:
text-stone-600 dark:text-stone-400— nevertext-stone-400alone (fails contrast)
Async components
Every component that fetches data must have a Skeleton loading state shown while data loads.
i18n
- Client:
useTranslations("namespace")fromnext-intl - Server:
getTranslations("namespace")fromnext-intl/server - New client sections need a wrapper in
ClientWrappers.tsxwith scopedNextIntlClientProvider
TypeScript
- No
any— define interfaces inlib/directus.tsortypes/ - No emojis in code