Portfolio and writing site for case studies, shipped work, and technical reflections, built with Astro 6, Tailwind CSS 4, and MDX.
This repository powers a custom monochrome portfolio experience with an
immersive landing page, a case-study-driven about page, and a writing archive
for project notes, hackathon writeups, and implementation retrospectives. The
current site is a substantial theme, content, and information-architecture
rewrite of astro-erudite.
- Portfolio-first experience with an immersive monochrome landing page, case-study about page, writing archive, project listings, author pages, and tag pages
- MDX-powered publishing workflow for blog posts, project entries, and author profiles
- SEO-friendly setup with canonical URLs, sitemap generation, RSS output, Open Graph images, and favicon metadata
- Rich technical writing support with KaTeX, Shiki, Expressive Code, and custom callout components
- Light and dark theme support with reusable UI primitives and Astro islands for selective interactivity
- Astro 6
- React 19 for interactive islands
- Tailwind CSS 4
- MDX
- Shiki, Expressive Code, and KaTeX
- Vercel for deployment
- Node.js
22.x - npm
-
Install dependencies:
npm install
-
Copy the example environment file:
cp .env.example .env
-
Set
PUBLIC_SITE_URLin.envto your production domain. For local work, a placeholder domain is fine. -
Start the dev server:
npm run dev
-
Open http://localhost:1234.
The site resolves its canonical URL in src/lib/site-config.ts using the
following order:
| Variable | When to use it | Notes |
|---|---|---|
PUBLIC_SITE_URL |
Recommended for local and production use | Primary source for canonical URLs, sitemap entries, and RSS metadata |
SITE_URL |
Optional fallback | Useful if you prefer a non-public env name in deployment config |
VERCEL_PROJECT_PRODUCTION_URL |
Automatic on Vercel | Keeps preview deployments pointed at the production domain for SEO-safe metadata |
VERCEL_URL |
Automatic on Vercel | Last-resort fallback |
For npm run dev, the site falls back to http://localhost:1234. Production
builds intentionally fail fast if no site URL is configured.
| Command | Description |
|---|---|
npm run dev |
Start the local Astro development server on port 1234 |
npm run start |
Alias for npm run dev |
npm run build |
Run Astro checks and create a production build in dist/ |
npm run preview |
Preview the production build locally |
npm run astro -- <args> |
Run Astro CLI commands directly |
npm run prettier |
Format ts, tsx, css, and astro files |
.
|-- public/
| |-- fonts/
| `-- static/
|-- src/
| |-- components/
| |-- content/
| | |-- authors/
| | |-- blog/
| | `-- projects/
| |-- layouts/
| |-- lib/
| |-- pages/
| `-- styles/
|-- astro.config.ts
|-- package.json
`-- README.md
src/consts.tscontains the site title, description, navigation links, profile copy, landing-page content, social links, and featured content counts.src/lib/site-config.tsresolves the canonical site URL from environment variables.
Content schemas are defined in src/content.config.ts.
- Blog posts live in
src/content/blog/ - Author profiles live in
src/content/authors/ - Project entries live in
src/content/projects/
Example blog post frontmatter:
---
title: 'Post title'
description: 'Short summary'
date: 2026-04-03
tags: ['astro', 'portfolio']
authors: ['chai-pin-zheng']
draft: false
---Legacy posts carried over from the original astro-erudite template can stay in
the repo as writing or implementation references. Mark those entries with
draft: true so they are excluded from blog listings, RSS, and generated static
paths until you intentionally republish them.
Example author profile frontmatter:
---
name: 'Chai Pin Zheng'
avatar: '/static/logo.png'
bio: 'Project retrospectives, technical writing, and product engineering notes.'
github: 'https://github.com/Ducksss'
linkedin: 'https://www.linkedin.com/in/chai-pin-zheng/'
mail: 'chaipinzheng@gmail.com'
---- Global tokens and theme styles live in
src/styles/ - Favicons and static social assets live in
public/ - Social preview graphics are stored in
public/static/
This site builds to static output, so it can be deployed anywhere Astro static sites are supported. Vercel is the intended hosting target for this repo.
Before deploying:
- Set
PUBLIC_SITE_URLto the production domain. - Run
npm run build. - Verify canonical URLs, sitemap output, and RSS metadata use the expected domain.
This site started from astro-erudite by jktrn. The current repository is an extensive theme, content, and information-architecture rewrite tailored to Chai Pin Zheng's portfolio and writing archive.
This repository includes the MIT License.


