Building This Site with Astro, Tailwind, and Claude Code

3 min read
  • #astro
  • #ai

This site is a static portfolio and blog built with Astro. This post covers how it was built, the tools involved, and what the development process looked like with Claude Code as a collaborator throughout.

The Stack

Astro — the framework. Astro is a content-focused static site generator that ships zero JavaScript by default. Pages are .astro files, blog posts are Markdown files in a content collection. The build output is plain HTML — fast to serve, easy to deploy.

Tailwind CSS v4 — styling. v4 drops the tailwind.config.js file in favour of CSS-native configuration via @theme blocks. No build plugin needed beyond @tailwindcss/vite. All design tokens (colours, fonts) live in global.css.

Pagefind — static search. Runs after the build, indexes the HTML output, and ships a small JS bundle for client-side search. No server required. The /search page loads the Pagefind UI and wires a custom input to it.

Vitest — unit tests. Covers utility functions: reading time calculation, tag deduplication. Fast, no configuration overhead since it shares the Vite pipeline already used by Astro.

Playwright — end-to-end tests. Tests run against the dev server on Desktop Chrome and Pixel 5 mobile. Covers page rendering, navigation, tag filtering, URL persistence, and the QR code back link.

oxlint — linter. Significantly faster than ESLint. Runs on staged files via lint-staged before every commit.

Prettier — formatter. With prettier-plugin-astro and prettier-plugin-tailwindcss for .astro file support and class sorting. Also runs on staged files pre-commit.

Husky + lint-staged — git hooks. Pre-commit runs Prettier and oxlint on staged files, then the full unit test suite.

Vercel — deployment. Connected to the GitHub repo. Every merge to main triggers a production deploy.

The Development Process

The entire site was built with Claude Code — Anthropic’s CLI for AI-assisted development. Not as a one-shot prompt, but as an ongoing back-and-forth: propose, review, adjust, repeat.

A few things that worked well in practice:

Small, focused files. Astro’s page-per-file model fits naturally. Each page is its own file, each utility is its own module. When a file has one job, the AI produces focused output. When a file has many jobs, the output gets muddier. Keeping files small isn’t just good structure — it’s also better input.

Extracting logic before testing. The reading time calculation and tag deduplication both started as inline expressions in .astro frontmatter. Before adding tests, they were extracted into src/utils/. Two small pure functions. The unit tests practically wrote themselves — and more importantly, it was easy to verify they were correct.

AI generates, I review. The test suite was generated by Claude Code and reviewed by me. The review process: read first (does this test what I think it tests?), run second (does it pass?), then think about what’s missing and ask for more. The loop is fast when the code is well-structured. When it isn’t, the tests are hard to verify — which is usually a signal that the code needs to be split, not the tests.

CI as a safety net, not a gate. The GitHub Actions workflow runs on every PR: format check, lint, unit tests, build, e2e tests. It’s not there to slow things down — it’s there to catch the things that slip through when you’re moving quickly.

Slash commands for repetitive git work. Claude Code supports custom slash commands — scripts that expand into full prompts. Two that became part of the daily workflow here: /commit-push-pr and /clean_gone — covered in more detail in a separate post.

What’s on the Site

  • Blog posts (this one included)
  • Tag filtering with URL persistence — /?tag=astro gives you a shareable filtered view
  • Static full-text search via Pagefind
  • An /about page with contact links and a QR code
  • RSS feed at /rss.xml
  • A 404 page

Source

The source is on GitHub if you want to look around.