https://github.com/cxro/astro-whono
一个极简双栏 Astro 主题,用于个人写作与轻量内容发布。
https://github.com/cxro/astro-whono
astro astro-theme blog markdown minimal personal-website static-site theme typography
Last synced: about 2 months ago
JSON representation
一个极简双栏 Astro 主题,用于个人写作与轻量内容发布。
- Host: GitHub
- URL: https://github.com/cxro/astro-whono
- Owner: cxro
- License: mit
- Created: 2026-01-16T16:51:58.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-04-28T07:13:11.000Z (about 2 months ago)
- Last Synced: 2026-04-28T08:32:47.175Z (about 2 months ago)
- Topics: astro, astro-theme, blog, markdown, minimal, personal-website, static-site, theme, typography
- Language: TypeScript
- Homepage: https://astro.whono.me
- Size: 16 MB
- Stars: 105
- Watchers: 0
- Forks: 33
- Open Issues: 1
-
Metadata Files:
- Readme: README.en.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# astro-whono
[中文](README.md) | [English](README.en.md)
[](https://github.com/cxro/astro-whono/actions/workflows/ci.yml) [](README.en.md#requirements) [](https://docs.astro.build/) [](LICENSE)
**✨ astro-whono is now upgraded to Astro v6**
A minimal two-column Astro theme for personal writing and lightweight publishing.
## Links
- Live demo:
- Repository:
## Preview
## Features
- Two-column layout (sidebar navigation + content area)
- Responsive design for mobile devices
- Content collections: essay / bits / memo (archive is generated from essay)
- Built-in local Admin Console (`/admin`): use Theme / Images / Checks / Data Console in development to manage site settings and assets, and take over the theme after forking or cloning
- Bits draft generator on `/bits/`: one-click Markdown output (copy/download), with multi-image support and automatic image dimension detection
- RSS: default archive feed + section feeds
- Light / dark theme + reading mode
## Getting Started
### Requirements
- Node.js 22.12+ (`.nvmrc` recommended)
### Quick Start
```bash
npm i
# Repeatable install (recommended for CI/troubleshooting)
# npm ci
npm run dev
npm run build && npm run preview
```
Windows (PowerShell) note
If execution policy blocks `npm.ps1`, use one of the following:
- `cmd /c npm run ...`
- Or use Git Bash / WSL
### Common Commands
- npm run dev
- npm run build
- npm run ci
- npm run new:bit
Check and regression commands
Use them depending on the situation:
```bash
# Default regression entry (GitHub Actions)
npm run ci
# Manual release verification for absolute links / sitemap / RSS (requires the final production domain)
SITE_URL=https://your-domain npm run build
SITE_URL=https://your-domain npm run check:prod-artifacts
# Only when changing Admin Console subroutes, /api/admin/** static boundaries, or dev/prod read-only boundaries
npm run check:preview-admin
```
- `npm test` mainly covers tag utilities, shared Theme Console validation rules, and core pure-logic regressions around theme settings `revision`.
- `npm run ci` is the default regression entry; `npm run ci:core` is only for faster local incremental checks.
- `npm run build` still works without `SITE_URL`, but SEO-related outputs will be incomplete.
- Before release, if you need to verify absolute-link artifacts, set a real `SITE_URL` and run `npm run check:prod-artifacts`.
## Deployment
### One-click Deploy
[](https://vercel.com/new/clone?repository-url=https://github.com/cxro/astro-whono) [](https://app.netlify.com/start/deploy?repository=https://github.com/cxro/astro-whono) [](https://dash.cloudflare.com/?to=/:account/workers-and-pages)
> For production, set: `SITE_URL=https://your-domain` (without a trailing slash).
> If not set, the site can still run, but link metadata for sharing/indexing may be incomplete.
Cloudflare Pages deployment (manual repository import)
**Build settings**
- Framework preset: `Astro`
- Build command: `npm run build`
- Output directory: `dist`
**Node.js version (usually not required)**
- This project includes `.nvmrc`, and Cloudflare Pages reads it automatically.
- If you need to set it manually, add `NODE_VERSION=22.22.0` in environment variables.
**Environment variables (strongly recommended for production)**
- In Pages project -> Settings -> Environment variables, add: `SITE_URL=https://your-domain` (for example `https://astro.whono.me`, without a trailing `/`).
**Why set `SITE_URL`?**
- Astro uses it to generate canonical, Open Graph `og:url`, RSS links, sitemap, and other fields that require absolute URLs. Without `SITE_URL`, deployment still works, but these links may fall back to relative paths or placeholder domains, which can hurt share previews and search indexing.
**About sitemap / robots**
- `sitemap` is generated only when `SITE_URL` is set, and `/robots.txt` includes a `Sitemap:` line only in that case (to avoid pointing to the wrong domain).
Post-deploy checklist
- Home page / list pages / detail pages are accessible
- RSS endpoints are accessible (`/rss.xml` and section feeds)
- With `SITE_URL` set: canonical / `og:url` point to your domain
- No network requests to demo-domain resources
## Configuration and Entry Points
### Project Entry Points
- Site config: `site.config.mjs`
- Content collections: `src/content.config.ts`
- Shared style entry: `src/styles/global.css`
- Page / scene style entries: `src/styles/home.css`, `src/styles/about.css`, `src/styles/memo.css`, `src/styles/article.css`, `src/styles/bits-page.css`
- Admin style entry: `src/styles/components/admin-shell.css` + route-specific Admin styles; the full `admin.css` aggregate is no longer provided
### Admin Console (`/admin`)
astro-whono includes a local Admin Console as the development entry point for viewing the site overview, adjusting theme settings, and importing/exporting settings snapshots.
#### Admin Entry Points
Admin Console is intended for **local development** by default.
Start the dev server:
```bash
npm install
npm run dev
```
Then open `http://localhost:4321/admin/` in your browser.
(If you changed the dev server port, replace `4321` with your actual port.)
| Entry | Status | Purpose |
| :---: | :---: | :--- |
| `/admin/` | Available | Stable Admin entry and Site Overview |
| `/admin/theme/` | Available | Theme Console for editing site information, sidebar, home page, inner-page copy, and more |
| `/admin/images/` | Available | Image resource browser and path helper |
| `/admin/checks/` | Available | Structured diagnostics and pre-release checks |
| `/admin/data/` | Available | Settings snapshot export / dry-run import / confirmed write |
| `/admin/content/` | In progress | Placeholder for content management and visual writing |
🖼️ Theme Console Overview
astro-whono provides a local Theme Console for centralized theme-level configuration in development.
#### Current Theme Console Support
Theme Console mainly covers **site-level** and **page-level** settings, including:
- Site title, description, brand name, and other basic metadata
- `/admin/` Overview public visibility and hidden-state copy
- Home intro copy and Hero image settings
- Sidebar navigation labels, visibility, and ordering
- Social links and custom social items
- Footer copyright line / basic footer copy
- Main title and subtitle for fixed inner pages
- Article metadata display rules
- Default author for the `/bits/` page
For more details, see the [Theme Console configuration guide](https://astro.whono.me/archive/theme-console-guide/).
#### Production behavior
- Theme Console / Data Console provide write capabilities only in local development; Content Console is still a placeholder.
- `/admin/content/` and `/admin/content/:collection/` currently only show the work-in-progress notice; collection overview, details, and frontmatter editing are not exposed.
- Production builds remain static output. `/admin/` can show a read-only public Overview or a hidden-state message based on Theme settings; production does not show Admin tabs, and other Admin subroutes only keep a local-development notice.
- `/api/admin/settings/` is for local development only and should not be treated as a production API
- `/api/admin/content/entry/` is for local development frontmatter writes only and should not be treated as a production API
- `/api/admin/data/settings/` is for local development settings export only and should not be treated as a production API
#### Compatibility for existing forks
- If `src/data/settings/*.json` does not exist yet, the frontend still reads config via `settings > legacy > default`
- The JSON files are generated only after the first save in `/admin/theme/`, so no manual migration script is required
## Content and Writing
### Collections and Routes
Content Collections:
- Essay: `src/content/essay`
- Bits: `src/content/bits`
- Memo: `src/content/memo/index.md`
- Archive: generated from essay entries via the `archive` field
Main routes:
- List pages: `/archive/`, `/essay/`, `/bits/`, `/memo/`, `/about/`
- Canonical detail route: `/archive/[slug]` (`/essay/[slug]` remains as a compatibility redirect)
### Image Assets
- Images inside article content: prefer `src/content/**` or `src/assets/**`, so Astro can process and optimize them during build
- `/bits/` images: place them under `public/bits/**` and use the actual file path, for example `bits/demo-01.jpg`
- Default avatar for `/bits/`: place it under `public/author/**` and use the actual file path, for example `author/your-avatar.png`
- Home Hero: supports `src/assets/**`, `public/**`, and `https://` image URLs
- If you need a public direct URL, or do not want Astro to process the asset, place it under `public/**`
### Core Frontmatter Fields
Essay:
```yaml
title: My Post
date: 2026-01-01
draft: false # Draft: hidden from list/RSS in production (visible in local preview; default false, optional)
archive: true # Archive switch: false excludes it from /archive and /archive/rss.xml (default true; detail page and /essay remain available)
slug: optional # Custom URL slug (defaults to the flattened content path, e.g. 2024/my-post -> 2024-my-post)
badge: optional # List badge; if omitted, list shows "Essay"
```
Bits:
```yaml
date: 2026-01-01T12:00:00+08:00 # Example; generator outputs local timezone
tags: # Optional tags (defaults to empty array)
- loc:Shenzhen # Location tag format: loc:; only the first one is displayed
- reading
images: # Optional: multi-image list (dimensions reduce CLS)
- src: bits/demo-01.webp # Supports relative path bits/... or absolute URL https://...
width: 800
height: 800
# draft: true # Optional draft; visible in `dev`, hidden by default in `build/preview` and production
```
`/bits/` does not currently generate detail routes from `slug`, nor does it render it as visible UI text; unless you are extending the theme, you usually do not need to set it.
Author info (on `/bits/` only):
- Default author and avatar are read from Theme Console via `page.bits.defaultAuthor`; if `src/data/settings/page.json` does not exist yet, they fall back to `site.author` / `site.authorAvatar` in `site.config.mjs`
- `authorAvatar` should be a relative image path only (no `public/`, no leading `/`), for example: `author/avatar.webp`; the file must actually exist under `public/**`
- Per-bit overrides are supported via `author` in frontmatter:
```yaml
author:
name: Alice
avatar: author/alice.webp
```
- Per-bit `author.avatar` follows the same rule as the default avatar: it must be a relative image path pointing to an existing file under `public/**`
- If the avatar is missing or fails to load, it automatically falls back to an initial-based avatar.
### Excerpt and Description (`description`)
- List excerpt is generated from content by default (sanitized and truncated)
- Use `` to define excerpt split point
- `description` is used for SEO/OG (meta description) only and does not affect list excerpts
### Writing Conventions (Content Blocks)
- Callout: recommended directive syntax `:::note[title] ... :::` (`note` / `tip` / `info` / `warning`); in HTML form use `.callout-title`, and use `data-icon="none"` to hide icon
- Figure: `figure > (img|picture) + figcaption?`
- Gallery: `ul.gallery > li > figure > (img|picture) + figcaption?` (optional `cols-2` / `cols-3`)
- Quote: standard `blockquote`, optional `cite` for source
- Pullquote: `blockquote.pullquote`
- Code Block: toolbar / copy button / line numbers are enhanced at build time (no extra author-side syntax needed)
Callout example:
```md
:::note[Note]
Body text goes here...
:::
```
HTML example:
```html
Note
Body text goes here...
```
## Fonts and Licensing
This theme uses two typeface families (self-hosted + subsetted):
- Noto Serif SC (400 / 600)
- LXGW WenKai Lite (Regular)
The repository includes subsetted WOFF2 files (`latin` / `cjk-common` / `cjk-ext`, loaded on demand via `unicode-range`), so you can use the project immediately after cloning.
Subset charset is generated from repository text plus `tools/charset-base.txt` (3,500 common characters) to reduce missing-glyph cases.
To regenerate font subsets:
1. Install Python 3, then run `python -m pip install fonttools brotli zopfli`
2. Make sure `pyftsubset --help` works; if it does not, add the Python Scripts directory to `PATH`
3. Put the source fonts in `tools/fonts-src/`
4. Run `npm run font:build`
5. If glyphs are missing, add the characters to `tools/charset-base.txt` and rerun `npm run font:build`
6. `tools/charset-common.txt` is regenerated by `npm run font:charset`; do not edit it unless you only want to rerun `npm run font:subset`
Font file list (subsets + source files)
Subset files (tracked in repository):
- `public/fonts/lxgw-wenkai-lite-latin.woff2`
- `public/fonts/lxgw-wenkai-lite-cjk-common.woff2`
- `public/fonts/lxgw-wenkai-lite-cjk-ext.woff2`
- `public/fonts/noto-serif-sc-400-latin.woff2`
- `public/fonts/noto-serif-sc-400-cjk-common.woff2`
- `public/fonts/noto-serif-sc-400-cjk-ext.woff2`
- `public/fonts/noto-serif-sc-600-latin.woff2`
- `public/fonts/noto-serif-sc-600-cjk-common.woff2`
- `public/fonts/noto-serif-sc-600-cjk-ext.woff2`
Source files (not tracked in repository):
- `tools/fonts-src/LXGWWenKaiLite-Regular.woff2`
- `tools/fonts-src/NotoSerifSC-Regular.ttf`
- `tools/fonts-src/NotoSerifSC-SemiBold.ttf`
Font license: SIL Open Font License 1.1 (see `public/fonts/OFL-LXGW-WenKai-Lite.txt` and `public/fonts/OFL-NotoSerifSC.txt`).
## RSS
- `/rss.xml` (default feed; uses the same archive items as `/archive/rss.xml`)
- `/archive/rss.xml` (archive feed)
- `/essay/rss.xml`
Setting `SITE_URL` is recommended for deployment (affects absolute links in RSS/OG/canonical).
## Contributing
Issues are welcome for bug reports and ideas.
Pull requests are welcome; using a `feature/*` branch is recommended.
### Sync Upstream in a Fork
```bash
git remote add upstream https://github.com/cxro/astro-whono.git
git fetch upstream --tags
git checkout main
git merge upstream/main
git push origin main --tags
```
## Acknowledgements
- Thanks to [elizen/elizen-blog](https://github.com/elizen/elizen-blog), the starting point of this theme design, which is inspired by the Hugo theme [yihui/hugo-ivy](https://github.com/yihui/hugo-ivy)
## License
License: MIT