This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Lemon Blog is a modern, static blog platform built with Next.js App Router and Tailwind CSS. It features:
- Static Site Generation: All blog posts are generated as static pages at build time
- Markdown Content: Articles are stored as Markdown files in
/content/articles - Syntax Highlighting: Code blocks with Prism.js syntax highlighting (rehype-prism-plus)
- Dark Mode: Full dark/light mode support with system preference detection
- Responsive Design: Mobile-first design with Tailwind CSS
- SEO Optimized: Built-in metadata and OpenGraph support
- Image Service: Unified image service supporting Unsplash, GIPHY, and Storyset
- Cover Images: Automatic cover image extraction from article content
# Install dependencies
npm install
# Start development server (also copies article images)
npm run dev
# Build for production (also copies article images)
npm run build
# Start production server
npm run start
# Lint code
npm run lint
# Fix linting issues
npm run lint:fix
# Type checking
npm run type-check
# Bundle analysis
npm run analyzeImportant: The dev and build commands automatically run scripts/copy-images.js before starting. This script copies images from content/articles/{slug}/img/ and content/articles/img/ to public/articles/ for static serving.
Image Path Resolution: The blog supports two image storage patterns:
- Per-article:
content/articles/{slug}/img/*→/articles/{slug}/img/* - Shared:
content/articles/img/*→/articles/img/*
Relative image paths in markdown (e.g., img/photo.jpg) are automatically converted to absolute paths during build time via fixImagePaths() in src/lib/posts.ts.
Image Service Architecture (src/lib/images/):
index.ts: UnifiedimageServiceclass with cachingunsplash.ts: Unsplash photo integrationgiphy.ts: GIPHY GIF integrationstoryset.ts: Storyset illustration integration- All services follow a consistent pattern: search, random, and optimized URL generation
Cover Image Extraction: The first image in markdown content is automatically extracted as the cover image. This supports both markdown syntax  and HTML <img> tags. The extracted image is removed from the content to avoid duplication.
src/
├── app/ # Next.js App Router pages
│ ├── blog/ # Blog-related pages
│ │ ├── [slug]/ # Dynamic blog post pages
│ │ └── page.tsx # Blog listing page
│ ├── layout.tsx # Root layout with Navbar and Footer
│ └── page.tsx # Homepage
├── components/ # Reusable React components
│ ├── Navbar.tsx # Navigation with dark mode toggle
│ ├── Footer.tsx # Site footer
│ ├── BannerImage.tsx # Smart image component with Unsplash/GIPHY/Storyset support
│ ├── ArticleCard.tsx # Blog post card component
│ └── ImageOptimizer.tsx # Image optimization utilities
├── lib/ # Utility functions and configurations
│ ├── posts.ts # Markdown processing, image path fixing, cover extraction
│ ├── images/ # Image service (Unsplash, GIPHY, Storyset)
│ └── styles.css # Custom styles for syntax highlighting
└── types/
└── post.ts # Post type definition
content/articles/ # Markdown blog posts
├── {slug}.md # Article markdown files
├── {slug}/img/ # Per-article images (copied to public/articles/{slug}/img/)
└── img/ # Shared article images (copied to public/articles/img/)
scripts/
└── copy-images.js # Copies article images to public directory
public/articles/ # Generated by copy-images.js (do not edit manually)
src/lib/posts.ts: Core utilities includingfixImagePaths(),extractCoverImageSync(),getAllPosts(),getPostBySlug()src/lib/images/index.ts: Unified image service withgetArticleImage(),searchImages(),getRandomImage()src/app/blog/[slug]/page.tsx: Dynamic route withgenerateStaticParams()for static generationsrc/components/BannerImage.tsx: Smart image component supporting multiple sources with fallbackscripts/copy-images.js: Build-time script that copies images from content to publicnext.config.ts: Static export configuration withoutput: 'export'
- Create a new
.mdfile in/content/articles/ - Include required frontmatter:
--- title: "Article Title" date: "YYYY-MM-DD" description: "Brief description" author: "Author Name" tags: ["tag1", "tag2"] ---
- Add images to either
content/articles/{slug}/img/(per-article) orcontent/articles/img/(shared) - Reference images in markdown as
img/filename.jpg- paths will be auto-converted - The first image in content becomes the cover image automatically
- title: Required. The article title
- date: Required. Publication date (YYYY-MM-DD format)
- description: Required. Brief description for SEO and previews
- author: Required. Author name (defaults to "Anonymous")
- tags: Optional. Array of string tags for categorization (also used for image matching)
For article images (displayed in content):
- Place in
content/articles/{slug}/img/orcontent/articles/img/ - Reference as
in markdown - Paths are automatically converted to
/articles/...at build time
For cover images:
- First image in markdown is auto-extracted as cover
- Supports
and<img src="url">syntax - Removed from content after extraction to avoid duplication
- Can be overridden via
coverImageproperty in Post type
For BannerImage component:
- Accepts
customImageprop to override automatic selection - Falls back to Unsplash/GIPHY/Storyset based on tags if no custom image
- Supports
preferredSourceprop ('unsplash', 'giphy', 'storyset')
- Tailwind CSS v4: Utility-first CSS framework with new PostCSS-based architecture
- Dark Mode: Class-based dark mode with system preference detection
- Typography: Custom prose styles using @tailwindcss/typography plugin
- Responsive: Mobile-first responsive design
- Components: Consistent design system with reusable components
The project is configured for one-click deployment to Vercel:
- Push to GitHub
- Connect repository to Vercel
- Vercel automatically builds and deploys
- Static Export:
output: 'export'in next.config.ts - Image Optimization:
unoptimized: truerequired for static export - Turbopack: Enabled for faster builds
- Trailing Slash: Enabled for consistent URL structure
- Remote Patterns: Configured for Unsplash, via.placeholder, and wildcard HTTPS
- Uses
gray-matterfor frontmatter parsing - Uses
remarkandrehypefor Markdown processing pipeline rehype-prism-plusprovides syntax highlighting with line numbers- Image paths are fixed before markdown processing via
fixImagePaths() - First image is extracted as cover, then removed from content
- Dark mode preference stored in localStorage
- Theme state managed in Navbar component (client component)
- System preference detection on first load
- Image service uses in-memory Map for caching
- All types defined in
src/types/post.ts - Client-safe type exports (no server-only types in client components)
- TypeScript strict mode enabled
- Next.js 16: React framework with App Router
- React 19: Latest React with concurrent features
- Tailwind CSS v4: Latest utility-first CSS with PostCSS integration
- gray-matter: Frontmatter parser for Markdown
- remark/rehype: Markdown processor ecosystem
- rehype-prism-plus: Syntax highlighting with Prism.js
- date-fns: Date formatting utilities
- @tailwindcss/typography: Prose styles for article content
- @tailwindcss/aspect-ratio: Aspect ratio utilities