Blogcraft

Astro Built with

A minimalist bilingual blog built with Astro. Write in Markdown, statically generated, ready to go. Features built-in i18n (zh/en), dark/light theme, full-text search, and GitHub Discussions comments.


Features

  • Bilingual — Chinese by default, with English at /en/, automatic hreflang SEO, language switcher, and translation linking
  • Dark/Light mode — respects prefers-color-scheme with manual toggle, persisted in localStorage, zero flash
  • Full-text search — powered by Pagefind, build-time indexing, zero external dependencies, works offline
  • GitHub comments — Giscus integration using GitHub Discussions, no separate auth system needed
  • SEO out of the box — auto-generated sitemap, Open Graph tags, robots.txt, semantic HTML
  • Responsive — mobile-first, 900px max content width
  • Fast by default — fully static generation, zero JS runtime

Tech Stack

LayerChoice
FrameworkAstro v5 (static site generation)
ContentMarkdown via Content Collections
SearchPagefind (build-time indexing)
CommentsGiscus (GitHub Discussions)
Package managerpnpm
StylingPure CSS with Custom Properties

Prerequisites

# Install pnpm
npm install -g pnpm

Quick Start

# Clone the repository
git clone https://github.com/Alistairccc/BlogCraft.git
cd BlogCraft

# Install dependencies
pnpm install

# Start dev server (defaults to http://localhost:4321)
pnpm dev

Build for production

pnpm build

Output goes to dist/. Preview locally:

pnpm preview

Writing Blog Posts

Posts live in src/content/blog/ organized by language:

src/content/blog/
├── zh/              # Chinese posts (default language)
│   ├── hello-world.md
│   └── ...
└── en/              # English posts
    ├── hello-world.md
    └── ...

Each post requires frontmatter:

---
title: "My First Post"
description: "A brief description for cards and SEO"
lang: zh
translationKey: "hello-world"  # links zh/en versions of the same post
pubDate: 2026-05-27
draft: false
---

Linking translations: Posts with the same translationKey across languages are automatically linked — readers see a toggle button to switch language on the same article.

To draft a post: Set draft: true — it won’t appear on the homepage or in the sitemap.

Project Structure

BlogCraft/
├── public/
│   ├── favicon.ico
│   └── robots.txt
├── src/
│   ├── components/           # Reusable Astro components
│   │   ├── BlogCard.astro    # Blog post card
│   │   ├── Footer.astro
│   │   ├── Giscus.astro      # Giscus comments
│   │   ├── Header.astro
│   │   ├── LanguageSwitcher.astro
│   │   ├── SearchBar.astro
│   │   └── ThemeToggle.astro
│   ├── content/
│   │   ├── blog/en/          # English posts (Markdown)
│   │   ├── blog/zh/          # Chinese posts (Markdown)
│   │   └── config.ts         # Content Collection schema
│   ├── i18n/
│   │   ├── ui.ts             # Translation key-value pairs
│   │   └── utils.ts          # i18n helper functions
│   ├── layouts/
│   │   └── BaseLayout.astro  # HTML shell for all pages
│   ├── pages/
│   │   ├── index.astro       # Homepage (zh)
│   │   ├── about.astro       # About page (zh)
│   │   ├── blog/             # Blog-related pages
│   │   │   ├── index.astro       # Blog listing (zh)
│   │   │   └── [...slug].astro   # Blog post detail (zh)
│   │   ├── search.astro      # Search page (zh)
│   │   └── en/               # English versions
│   │       ├── index.astro
│   │       ├── about.astro
│   │       ├── blog/
│   │       │   ├── index.astro
│   │       │   └── [...slug].astro
│   │       └── search.astro
│   └── styles/
│       ├── global.css        # Design tokens, reset, layout
│       └── blog.css          # Blog post typography
├── astro.config.mjs
├── package.json
└── README.md

Configuration

Site URL

Set your domain in astro.config.mjs:

export default defineConfig({
  site: 'https://your-domain.com',
});

Also update public/robots.txt with the correct sitemap URL.

Giscus (Comments)

The blog uses Giscus to power comments via GitHub Discussions.

  1. Make sure your repository has GitHub Discussions enabled
  2. Install the Giscus app on your repo
  3. Open giscus.app, fill in your repo details, and copy the configuration
  4. Update src/components/Giscus.astro with your data-repo, data-repo-id, data-category, data-category-id values

Search uses Pagefind and runs as a post-build step (pnpm build runs astro build && npx pagefind --site dist). No external service or API key needed.

Modifying Translations

Edit src/i18n/ui.ts to add or modify translation keys:

export const ui = {
  zh: {
    'nav.home': '首页',
    'nav.blog': '文章',
  },
  en: {
    'nav.home': 'Home',
    'nav.blog': 'Blog',
  },
};

Keys are strongly typed through the TranslationKey type in the same file.

Deployment

This is a fully static site — deploy to any static hosting platform (Cloudflare Pages, Vercel, Netlify, etc.). Build command:

pnpm build

Deploy the dist/ directory to your hosting platform.