A blog by Linghao Zhang, built with Next.js 15 and modern web technologies.
- Framework: Next.js 15 (App Directory, Static Export)
- Language: TypeScript
- Styling: Tailwind CSS 4.0 beta
- Content: MDX for rich content
- Syntax Highlighting: Shiki
- Math: KaTeX
- Fonts: Inter Variable, Lora Italic Variable, Iosevka Fixed Curly
- Deployment: Cloudflare Pages
- CI/CD: GitHub Actions
- โจ Modern, clean design with smooth view transitions
- ๐ฑ Fully responsive layout
- ๐จ Custom typography with variable fonts
- ๐ Elegant color scheme (rurikon palette)
- ๐ MDX support for rich content
- ๐ Syntax highlighting for code blocks
- ๐งฎ Math rendering support
- ๐ท๏ธ Tag system with filtering
- ๐งฉ Projects page with SVG logos
- ๐ธ Photography gallery with lightbox
- ๐ก Auto-generated RSS feed
- โก Optimized static site with CDN delivery
- ๐ Automated deployments
- Node.js 16 or later
- pnpm, yarn, or npm
# Install dependencies
pnpm install
# or
yarn install
# or
npm install# Run development server
pnpm dev
# or
yarn dev
# or
npm run devOpen http://localhost:3000 to view the blog.
# Build for production (includes RSS generation)
npm run buildThe build outputs to the dist/ directory and includes:
- Static HTML/CSS/JS files
- Auto-generated RSS feed at
/feed.xml - Optimized images and assets
# Generate RSS feed only
npm run rss
# Deploy to Cloudflare Pages (preview)
npm run deploy
# Deploy to Cloudflare Pages (production)
npm run deploy:prod
# Lint code
npm run lint.
โโโ app/ # Next.js app directory
โ โโโ _fonts/ # Custom font files
โ โโโ globals.css # Global styles
โ โโโ layout.tsx # Root layout
โ โโโ page.mdx # Home page
โ โโโ posts/ # Blog posts
โ โ โโโ page.tsx # Posts index
โ โ โโโ [slug]/ # Dynamic post pages
โ โ โโโ _articles/ # Post content (MDX)
โ โโโ notes/ # Legacy URL compatibility routes (redirect/alias to posts)
โ โ โโโ page.tsx # Legacy notes index
โ โ โโโ [slug]/ # Legacy note URLs
โ โโโ misc/ # Miscellaneous articles
โ โ โโโ page.tsx # Misc index
โ โ โโโ [slug]/ # Dynamic misc pages
โ โ โโโ _articles/ # Misc content (MDX)
โ โโโ gallery/ # Photography gallery
โ โ โโโ page.tsx # Gallery page
โ โ โโโ gallery-grid.tsx # Lightbox component
โ โ โโโ data.ts # Photo data
โ โโโ projects/ # Projects page
โ โ โโโ page.tsx # Projects page
โ โ โโโ data.tsx # Project metadata
โ โ โโโ logos.tsx # Project SVG logos
โ โโโ tags/ # Tag system
โ โโโ all/ # Tag filtering page
โโโ components/ # React components
โ โโโ navbar.tsx # Navigation
โ โโโ tag.tsx # Tag component
โ โโโ ... # Other components
โโโ lib/ # Utilities
โ โโโ tags.ts # Tag management
โโโ scripts/ # Build scripts
โ โโโ generate-rss.mjs # RSS generation
โโโ public/ # Static assets
โโโ dist/ # Build output
โโโ mdx-components.tsx # MDX component config
โโโ next.config.ts # Next.js config
โโโ wrangler.toml # Cloudflare config
All content is written in MDX format, combining Markdown with React components.
- Create a new
.mdxfile inapp/posts/_articles/ - Add metadata at the top:
export const metadata = {
title: 'Your Post Title',
description: 'A brief description',
date: '2025.01.01',
tags: ['Tag1', 'Tag2'],
}
Your content here...- The post will automatically:
- Appear in the posts index
- Be included in RSS feed
- Be filterable by tags
- Get a URL like
/posts/your-post-title
- Posts (
app/posts/_articles/) - Blog posts and reading notes - Misc (
app/misc/_articles/) - Miscellaneous content
Legacy /notes/* URLs are still supported for backward compatibility, but new content should be added under posts.
The gallery feature displays a collection of photography. Photos are managed in app/gallery/data.ts.
To add a new photo:
- Upload the image to a hosting service (e.g., Cloudflare R2).
- Add a new object to the
photosarray inapp/gallery/data.ts:
{
id: 'unique-id',
src: 'https://your-image-url.jpg',
alt: 'Description for accessibility',
caption: 'Optional caption text',
metadata: {
'Location': 'Kyoto, Japan',
'Date': '2024-10-28',
'Camera': 'Sony ฮฑ7C II',
}
}MDX files can use React components:
<Card
image="https://example.com/image.jpg"
title="Card Title"
desc="Description"
link="https://example.com"
/>
<BlockSideTitle title="Side note text">
Main content here
</BlockSideTitle>The blog uses the "rurikon" color palette defined in app/globals.css. Customize colors by modifying the @theme section.
Fonts are loaded locally via next/font/local in app/layout.tsx and exposed as CSS variables:
--sans: Inter Variable (default body text)--serif: Lora Italic Variable (used for blockquote/nav/sidenote styling)--mono: Iosevka Fixed Curly (code blocks and inline code)
Current typography strategy in app/globals.css:
- Body text uses sans variable font with custom OpenType features and
font-variation-settings. - Emphasis (
em,i) uses sans italic withfont-synthesis: style(so italic renders even when no dedicated italic face is available). - Blockquote/nav/sidenote use serif styling.
- Code uses mono styling and Shiki token variables.
Edit components/navbar.tsx to modify navigation links.
The blog uses automated CI/CD with GitHub Actions and Cloudflare Pages.
# Deploy to preview
npm run deploy
# Deploy to production
npm run deploy:prodDeployment is currently script-driven (npm run deploy, npm run deploy:prod).
If CI branch automation is added, document branch mappings here.
See DEPLOYMENT.md for detailed setup instructions.
Add tags to any article's metadata:
export const metadata = {
title: 'My Post',
tags: ['JavaScript', 'React', 'Web Development'],
}- Browse all tags:
/tags/all - Filter by tags:
/tags/all?tag=JavaScript - Click tags on articles to filter
The RSS feed is automatically generated on every build:
- Feed URL:
https://linghao.io/feed.xml - Includes: All posts and misc articles
- Updates: Automatic on deployment
- Manual generation:
npm run rss
MIT
This blog is inspired by Shu Ding's blog.