A personal website.
This website is built with:
- Astro - Static site generation
- GitHub Actions - CI/CD
- Cloudflare Pages - Hosting and deployment
Blog posts are Astro content collections in src/content/blog/. Each post is a directory containing index.md and any co-located images.
Frontmatter:
---
title: "Post Title"
date: "YYYY-MM-DD"
path: "/blog/slug"
excerpt: "Short description for the listing page."
draft: true # optional, excluded from production
archived: true # optional, excluded from listing
---
Projects are defined in src/data/projects.json. Sorted by lastCommitDate (newest first).
This repo also hosts standalone microsites, each with its own domain, layout, and styles:
- useyourdamnhands.com — A public service announcement about unnecessary tools
- whatarewedoinghere.org — Coming soon
Domain routing is handled by Cloudflare Pages Functions middleware (functions/_middleware.ts). There are two ways to create a microsite:
For lightweight microsites that don't need Astro's build pipeline:
- Create
src/pages/<name>/index.html— Astro treats raw.htmlfiles insrc/pages/as page routes without any processing. The file is served at/<name>/in both dev and production. - To attach a custom domain, add an entry to
MICROSITESinfunctions/_middleware.ts:{ prefix: '/<name>', domain: 'example.com' }
src/pages/useless-box/index.html is a minimal example that renders a red box.
For microsites that need components, data, TypeScript, or multiple routes:
src/microsites/<name>/— layouts, components, data, styles (not routed by Astro)src/pages/<name>/— page routes (index.astro,404.astro,robots.txt.ts,sitemap.xml.ts)public/<name>/— static assets (images, favicon)
After adding routes under src/pages/<name>/, exclude the prefix from the main sitemap in astro.config.mjs and register the domain in the middleware as above.
The site supports multiple visual themes, toggled via a dropdown in the bottom-left corner:
- Default - Clean blue/coral palette
- Sharks - San Jose Sharks teal/orange palette with hockey easter eggs
- Canada - Canadian flag red/white palette with falling snow and maple leaves
- Underwater - Deep ocean gradient with animated waves, manta rays, and coral
Themes are defined in src/themes/ as TypeScript files satisfying the Theme interface. See CONTRIBUTING.md for the full theme API and conventions.
- Node.js 22+ - install via
brew install node(assuming you have Homebrew installed) - pnpm — install via
corepack enable pnpm
$ pnpm install
$ pnpm dev
| Command | Description |
|---|---|
pnpm dev |
Start dev server |
pnpm build |
Production build |
pnpm lint |
ESLint check |
pnpm lint:fix |
ESLint auto-fix |
pnpm format |
Prettier format all files |
pnpm format:check |
Prettier check |
pnpm typecheck |
TypeScript type check |
pnpm test |
Run unit tests (Vitest) |
pnpm test:e2e |
Run e2e tests (Playwright) |
ESLint and Prettier run automatically:
- On save — VS Code formats via Prettier
- On commit — Husky pre-commit hook runs
lint-staged - In CI — GitHub Actions checks lint and formatting before build
Deployed to Cloudflare Pages via GitHub Actions (.github/workflows/main.yml).
Production — on push to main:
- CI runs lint, format check, typecheck, tests, build, and Lighthouse
- Build output is deployed via
wrangler pages deploy --branch=main
Preview — on pull requests:
- CI runs the same checks, then deploys with
--branch=${{ github.head_ref }} - A preview URL (e.g.
feat-my-thing.tinney-dev.pages.dev) is posted as a PR comment - The comment is updated on subsequent pushes to the same PR
Custom domains (tinney.dev, useyourdamnhands.com, whatarewedoinghere.org) are configured in the Cloudflare Pages dashboard. Domain-based routing is handled by the middleware in functions/_middleware.ts.
See CONTRIBUTING.md for workflow, commit conventions, architecture, and code style guidelines.
MIT