# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a **Next.js 16** blog/portfolio application built with **TypeScript**, **Tailwind CSS**, and **React 19**. The project uses **App Router** with Static Site Generation (SSG) for blog posts stored as Markdown files. **Design Philosophy:** Industrial/SCP-inspired aesthetic with terminal/cyberpunk elements. Sharp edges, thick borders, monospace fonts, darker color palettes (slate/zinc/cyan/emerald tones). No modern Material UI feel - think government documents, classified files, brutal utilitarian design. ## Development Commands ```bash # Development server (runs on port 3030) npm run dev # Production build npm run build # Start production server npm run start # Lint code npm run lint # Validate all markdown posts (frontmatter, format, tags) npm run validate-posts ``` ## Architecture Overview ### Next.js 16 App Router Structure ``` app/ ├── @breadcrumbs/ # Parallel route for breadcrumb navigation │ ├── default.tsx # Auto-generated breadcrumbs │ ├── blog/[...slug]/ # Post-specific breadcrumbs with titles │ ├── tags/[tag]/ # Tag-specific breadcrumbs │ └── about/ # Static breadcrumbs ├── blog/ │ ├── page.tsx # Blog listing with all posts │ └── [...slug]/ # Dynamic post routes (supports nested paths) │ ├── page.tsx # Post rendering with SSG │ └── not-found.tsx # Custom 404 for missing posts ├── about/page.tsx ├── layout.tsx # Root layout with metadata, fonts, breadcrumbs slot └── page.tsx # Landing page (hero + featured posts) ``` **Key Architectural Patterns:** 1. **Parallel Routes:** `@breadcrumbs` slot renders dynamic navigation based on current route without prop drilling 2. **Catch-all Routes:** `[...slug]` supports nested blog posts (e.g., `/blog/tech/article-name`) 3. **Static Generation:** `generateStaticParams()` pre-renders all blog posts at build time 4. **Server Components by Default:** All components are RSC unless marked with `'use client'` ### Markdown System ``` content/blog/ # Markdown files (supports nested directories) ├── example.md └── tech/ └── article.md lib/ ├── markdown.ts # Core markdown utilities │ ├── getPostBySlug() # Read single post with path sanitization │ ├── getAllPosts() # Get all posts, sorted by date, recursive │ ├── getRelatedPosts() # Find similar posts by tags │ └── validateFrontmatter() ├── types/frontmatter.ts # TypeScript interfaces for Post, FrontMatter └── utils.ts # formatDate(), formatRelativeDate(), generateExcerpt() ``` **Frontmatter Schema:** ```yaml --- title: string # Required description: string # Required date: "YYYY-MM-DD" # Required, ISO format author: string # Required tags: [string, string?, string?] # Max 3 tags image?: string # Optional hero image draft?: boolean # Exclude from listings if true --- ``` **Security:** Path sanitization prevents directory traversal attacks. All file reads use `path.resolve()` and validate paths stay within `content/blog/`. ### Components Organization ``` components/ ├── blog/ │ └── MarkdownRenderer.tsx # Client component for rendering markdown │ # Custom components: images (Next Image), │ # links (external vs internal), code blocks ├── layout/ │ ├── Breadcrumbs.tsx # Client component, uses usePathname() │ └── BreadcrumbsSchema.tsx # Schema.org structured data for SEO └── [future components] ``` ## Coding Standards for Next.js 16 ### File Naming Conventions **Files and Directories:** - Use **kebab-case** for all file names: `user-profile.tsx`, `blog-post.tsx` - Special Next.js files: `page.tsx`, `layout.tsx`, `not-found.tsx`, `loading.tsx` **Component Names (inside files):** - Use **PascalCase**: `export function UserProfile()`, `export default BlogPost` **Variables, Functions, Props:** - Use **camelCase**: `const userSettings = {}`, `function handleSubmit() {}` - Hooks: `useTheme`, `useMarkdown` **Constants:** - Use **SCREAMING_SNAKE_CASE**: `const API_BASE_URL = "..."` **Why kebab-case for files?** - Cross-platform compatibility (Windows vs Unix) - URL-friendly (file names often map to routes) - Easier to parse and read ### Theme Management & Reusability **Recommended Pattern:** Use `next-themes` library for dark/light mode ```typescript // Root layout.tsx import { ThemeProvider } from 'next-themes' export default function RootLayout({ children }) { return ( {children} ) } ``` **Client Component for Toggle:** ```typescript // components/theme-toggle.tsx 'use client' import { useTheme } from 'next-themes' import { useEffect, useState } from 'react' export function ThemeToggle() { const { theme, setTheme } = useTheme() const [mounted, setMounted] = useState(false) // Prevent hydration mismatch useEffect(() => setMounted(true), []) if (!mounted) return
...
return } ``` **Tailwind Configuration:** ```javascript // tailwind.config.js module.exports = { darkMode: 'class', // Use 'class' strategy for next-themes theme: { extend: { colors: { // Define custom colors for consistency 'dark-primary': '#18181b', 'accent': { DEFAULT: '#164e63', hover: '#155e75' } } } } } ``` **CSS Variables Pattern:** ```css /* globals.css */ :root { --bg-primary: 255 255 255; --text-primary: 15 23 42; } .dark { --bg-primary: 24 24 27; --text-primary: 241 245 249; } /* Use in components */ .card { @apply bg-[rgb(var(--bg-primary))] text-[rgb(var(--text-primary))]; } ``` ### Provider Pattern Best Practices 1. **Server Component Boundary:** Keep `layout.tsx` as Server Component, wrap only `children` with Client Provider 2. **Avoid Hydration Mismatches:** Always use `suppressHydrationWarning` on `` tag 3. **Client-Only Rendering:** Use `useEffect` + `mounted` state for theme-dependent UI 4. **Context Consumption:** Only components using `useTheme()` need `'use client'` directive 5. **No Prop Drilling:** Context makes theme accessible anywhere without passing props ### Next.js 16 Specific Patterns **Async Server Components:** ```typescript // app/blog/page.tsx export default async function BlogPage() { const posts = await getAllPosts() // Server-side data fetching return
{posts.map(...)}
} ``` **Static Generation with Dynamic Routes:** ```typescript // app/blog/[...slug]/page.tsx export async function generateStaticParams() { const posts = await getAllPosts() return posts.map(post => ({ slug: post.slug.split('/') })) } export async function generateMetadata({ params }) { const post = getPostBySlug(params.slug.join('/')) return { title: post.frontmatter.title, ... } } ``` **Parallel Routes for Layout Composition:** ```typescript // app/layout.tsx export default function RootLayout({ children, breadcrumbs, // From @breadcrumbs parallel route }: { children: React.ReactNode breadcrumbs: React.ReactNode }) { return <> {breadcrumbs} {children} } ``` ## Project-Specific Patterns ### Markdown Processing - **Always validate paths:** Use `sanitizePath()` to prevent directory traversal - **Draft support:** Posts with `draft: true` are excluded from `getAllPosts()` - **Recursive directories:** Blog posts can be organized in subdirectories (`content/blog/tech/post.md`) - **Reading time:** Auto-calculated at 200 words/minute - **Date handling:** Use Romanian locale (`ro-RO`) for date formatting ### SEO & Metadata - **Every page exports `metadata`:** Use Next.js 16's `Metadata` type - **Dynamic metadata:** Use `generateMetadata()` for blog posts - **Structured data:** Include Schema.org `BreadcrumbList` and `BlogPosting` - **OpenGraph images:** Reference `post.frontmatter.image` for social sharing ### Styling Guidelines **Color Palette:** - Backgrounds: `zinc-900`, `slate-900`, `slate-800` - Accents: `cyan-900`, `emerald-900`, `teal-900` - Text: `slate-100`, `slate-300`, `slate-500` - Borders: `border-2`, `border-4` (thick, sharp) **Design Tokens:** - **NO rounded corners:** Use `rounded-none` or omit (default is sharp) - **Monospace fonts:** Apply `font-mono` for terminal aesthetic - **Uppercase labels:** Use `uppercase tracking-wider` for headers - **Border-heavy design:** Thick borders (`border-4`) over shadows - **Classification labels:** Add metadata like "FILE#001", "DOCUMENT LEVEL-1" **Typography:** - Primary font: `JetBrains Mono` (monospace) - Headings: `font-mono font-bold uppercase` - Body: `font-mono text-sm` - Code blocks: Sharp borders, dark background, no syntax highlighting (for terminal feel) ## Available Subagents Use these specialized agents via `/spec-implementation-and-review` command: - `nextjs-specialist` - Next.js 15/16, App Router, SSG, API routes - `ui-implementer` - UI implementation with shadcn/ui, Tailwind - `ui-css-specialist` - CSS layouts, styling, responsive design - `react-frontend-expert` - React components, hooks, state management - `nodejs-typescript-engineer` - TypeScript, Node.js backend ## Common Pitfalls 1. **Hydration Mismatches with Themes:** Always use `suppressHydrationWarning` on `` and check `mounted` state before rendering theme-dependent UI 2. **Image Paths:** Use `/` prefix for public assets (`/blog/image.jpg` not `blog/image.jpg`) 3. **Dynamic Routes:** Remember to return `slug` as array in `generateStaticParams()` for catch-all routes 4. **Client Components:** Minimize `'use client'` usage - only add when using hooks, event handlers, or browser APIs 5. **Path Security:** Always use `sanitizePath()` when reading markdown files 6. **Date Formats:** Use `YYYY-MM-DD` in frontmatter, convert to Romanian locale for display 7. **Port Configuration:** Dev server runs on port **3030** (not default 3000) ## Type Safety - All utilities in `lib/` are fully typed - Frontmatter structure enforced via `FrontMatter` interface - Use `Post` type for blog post objects - Avoid `any` - use `unknown` if type is truly unknown, then narrow ## Performance Notes - **SSG by default:** All blog posts pre-rendered at build time - **Image optimization:** Use Next.js `` component - **Font optimization:** Google Fonts loaded via `next/font` - **No client-side data fetching:** Markdown loaded server-side only - **Static exports:** Pages are fully static HTML (no server required for hosting)