Files
mypage/CLAUDE.md
2025-11-12 16:17:55 +02:00

11 KiB

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

# 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:

---
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

// 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:

// 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:

// 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:

/* 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:

// 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:

// 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:

// 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)