A single-page, content-driven portfolio that ships ~zero JavaScript by default. One small React island fetches live PostHog metrics for the project sparklines; everything else is static HTML rendered at build time. Lighthouse 100, out of the box.
- Astro 6 static output with selectively hydrated React islands (
client:visible). - Tailwind v4 via
@tailwindcss/vite— CSS-first config, no PostCSS pipeline. - Live sparklines from PostHog, proxied through a Vercel serverless route with edge caching (
s-maxage=600, stale-while-revalidate=86400). - Env-driven availability toggle — flip the "Open to new roles" badge by changing one Vercel env var. No code change, no third-party feature flag service.
- Generated assets — favicon set and OpenGraph image rebuilt from
src/assets/icon.svgon everydev/build. - Strict tooling: Biome (lint + format), Fallow (unused exports/deps), Lefthook git hooks,
bun test+happy-dom+ Testing Library.
| Concern | Choice |
|---|---|
| Framework | Astro 6 (static output, React islands) |
| UI runtime | React 19 + TypeScript (strict) |
| Styles | Tailwind v4 via @tailwindcss/vite |
| Package manager + runtime | Bun |
| Lint + format | Biome |
| Unused deps / exports | Fallow |
| Git hooks | Lefthook (pre-commit: Biome + typecheck · pre-push: Fallow) |
| Test runner | bun test + happy-dom + Testing Library |
| CI | GitHub Actions (bun run ci on PRs and main) |
| Hosting | Vercel (@astrojs/vercel adapter) |
Note
The site is one route, mostly static (about / experience / projects / articles), with one small interactive island (the project sparklines). Astro ships zero JS by default and hydrates only the React components that need it.
