--- 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" ``` `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 `.` - 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