- Remove external next-view-transitions dependency - Use Next.js 16 native navigation and Safari 18+ native View Transition API - Add ViewTransitionProvider for minimal wrapping with Safari 18+ detection - Updated all Link imports from external package to next/link - Removed link-wrapper.tsx and view-transitions-wrapper.tsx This resolves Safari compatibility issues while maintaining transitions on modern browsers.
7.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
npm run dev- Start dev server (runs Contentlayer2 + Next.js with Turbopack concurrently)npm run build- Full production build: sync-assets → contentlayer2 build → next build → pagefind indexing → copy pagefind to publicnpm run lint- ESLint vianext lintnpm run sync-assets- Copycontent/assets/topublic/assets/(also runs automatically before build)
No test framework is configured.
Architecture
Content pipeline: content/ git submodule (MDX/Markdown) → Contentlayer2 (contentlayer.config.ts) → typed Post/Page objects imported from contentlayer2/generated → consumed by pages and lib/posts.ts helpers.
Routing (App Router):
/— Home page with latest posts/blog— Blog index with search, sort, pagination/blog/[slug]— Single post with TOC, reading progress, prev/next navigation/pages/[slug]— Static content pages (fromcontent/pages/)/tags,/tags/[tag]— Tag index and per-tag post lists/api/og— Dynamic OG image generation (@vercel/og)/feed.xml— RSS feed (route handler)
Key data flow:
lib/config.ts—siteConfigobject built fromNEXT_PUBLIC_*env vars (all site metadata, social links, accent colors, pagination)lib/posts.ts— Query helpers:getAllPostsSorted(),getPostBySlug(),getPageBySlug(),getAllTagsWithCount(),getRelatedPosts(),getPostNeighbors()lib/mastodon.ts— Mastodon API client for sidebar feed widgetlib/rehype-callouts.ts— Custom rehype plugin for GitHub-style[!NOTE]callout blocks
Layout hierarchy: app/layout.tsx (fonts, theme CSS vars, ThemeProvider, JSON-LD) → components/layout-shell.tsx (header, sidebar, footer, back-to-top) → page content.
Markdown processing (configured in contentlayer.config.ts):
- Remark: GFM
- Rehype: callouts → pretty-code (shiki, dual theme) → slug → autolink-headings → image path rewriter (
../assets/→/assets/) - Image paths in markdown are relative (
../assets/foo.jpg); a rehype plugin rewrites them to/assets/foo.jpgat build time
Styling
- Tailwind CSS v4 with CSS-first configuration (no
tailwind.config.cjs) - Dark mode via
@custom-variant darkinstyles/globals.css(class-based, toggled bynext-themes) - Theme customization via
@themeblock instyles/globals.css: colors, fonts, easing, durations, shadows, keyframes, animations - Accent color system via CSS variables set in
app/layout.tsxfrom env vars:--color-accent,--color-accent-soft,--color-accent-text-light,--color-accent-text-dark - Typography plugin (
@tailwindcss/typography) loaded via@plugindirective; prose dark mode handled by custom.dark .proseCSS overrides - English headings use Playfair Display serif (
--font-serif-eng); body uses Inter + CJK fallback stack - PostCSS config:
postcss.config.mjsusing@tailwindcss/postcss
Content Submodule
The content/ directory is a git submodule pointing to a separate personal-blog repository. It contains posts/, pages/, and assets/. After pulling new content, run npm run sync-assets to update public/assets/. The build script does this automatically.
Path Aliases
@/* maps to project root (configured in tsconfig.json). Contentlayer generated types at .contentlayer/generated are aliased as contentlayer2/generated.
Deployment
Two Git remotes are involved: git.gbanyan.net (SSH, primary push target) and gitea.gbanyan.net (HTTPS, Gitea web UI). A crontab on the server automatically mirrors git.gbanyan.net → gitea.gbanyan.net. Push to main on git.gbanyan.net triggers CI/CD automatically (server-side hook). No Dockerfile or workflow file in this repo.
Content-only update (new/edited posts) — both steps are required to trigger deploy:
- Commit and push inside
content/submodule:git -C content add . && git -C content commit -m "..." && git -C content push - Update main repo submodule pointer and push:
git add content && git commit -m "Update content submodule" && git push
Pushing only to content/ (personal-blog) does NOT trigger deployment. The main repo must also be pushed because CI/CD is bound to blog-nextjs, not personal-blog.
Code changes: Commit and push in the main repo as usual — git push to main triggers the pipeline.
Language
The site's default locale is zh-TW. UI text, labels, and timestamps are in Traditional Chinese.
Design Context
Users
- Medical professionals & students: Seek clinical insights, case studies, and medical education content
- General public: Interested inpersonal reflections, medicine explainedaccessibly, and lifestyle content
- Tech enthusiasts & developers: Drawn to HomeLab, technical tutorials, and developer environment content
- Patients & advocates: Those with similar conditions (Usher syndrome, hearing/vision impairments) seeking understanding and community
Context: Readers visit for deep, reflective content—often in quiet environments, seeking to learn, reflect, or connect with personal experiences. They value clarity, authenticity, and quality over speed.
Job to be done: Gain meaningful knowledge, find resonance with personal experiences, understand complex topics (medical/technical) in approachable terms.
Brand Personality
- Voice: Reflective, professional, and thoughtful—like a trusted physician who also happens to be a developer
- 3-word personality: Professional & refined, Thoughtful & reflective, Technical & practical, Approachable & human
- Emotional goals: Calm & contemplative, Inspired & curious
Not: Corporate, salesy, alarmist (like news sites), or overly technical/clinical.
Aesthetic Direction
Visual tone: Warm & organic with academic & scholarly sensibility, combined with modern technical clarity
References:
- Medium (medium.com): Readability-focused, minimal distractions, clean typography
- Personal tech blogs: Individual personality, character, and hands-on authenticity
- Library aesthetic: Quiet, thoughtful, knowledge-rich environment
Anti-references (explicitly avoid):
- News sites: Cluttered, headline-focused, clickbait design
- Social media feeds: Infinite scroll, attention-grabbing tactics, dopamine-driven design
- Corporate/SaaS: Too polished, salesy, or uniform corporate branding
- Dry technical docs: Lacking personality, purely functional
Theme: Both light and dark modes equally important—light for daytime readability, dark for late-night focused reading. Accent colors should be warm (avoid reds/yellows which feel urgent/alerting).
Design Principles
-
Calm-first design: Space, breathing room, and typography hierarchy should prioritize relaxed reading over visual stimulation. Avoid jarring transitions or animation that distracts from content.
-
Warm technicality: Blend technical precision with human warmth—clean, efficient interfaces that don't feel cold or sterile. The HomeLab/developer content should feel hands-on, not just theoretical.
-
Academic elegance: Typography and layout should honor the scholarly nature of medical writing and technical explanations—clear hierarchy, proper spacing, and readability first.
-
Inclusive accessibility: Consider hearing/vision impairments (user has Usher syndrome): high contrast, readable text, motion sensitivity support, clear navigation, and no time-based content hiding.
-
Consistent rhythm: Maintain consistent spacing, sizing, and interaction patterns across pages to create a predictable, trustworthy experience. Subtle interactions > flashy animations.