333 lines
11 KiB
Markdown
333 lines
11 KiB
Markdown
# 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 (
|
|
<html suppressHydrationWarning>
|
|
<body>
|
|
<ThemeProvider
|
|
attribute="class"
|
|
defaultTheme="dark"
|
|
enableSystem={false}
|
|
storageKey="blog-theme"
|
|
>
|
|
{children}
|
|
</ThemeProvider>
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|
|
```
|
|
|
|
**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 <div>...</div>
|
|
|
|
return <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
|
|
Toggle
|
|
</button>
|
|
}
|
|
```
|
|
|
|
**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 `<html>` 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 <div>{posts.map(...)}</div>
|
|
}
|
|
```
|
|
|
|
**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 `<html>` 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 `<Image>` 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)
|