Personal website built with SvelteKit 2 + Svelte 5, deployed to Cloudflare Workers.
- Framework: SvelteKit 2 + Svelte 5
- Styling: Tailwind CSS
- Content: mdsvex (Markdown with Svelte components)
- Syntax Highlighting: @bitmachina/highlighter + Shiki
- Deployment: Cloudflare Workers (via wrangler)
- Package Manager: pnpm
src/
├── lib/
│ ├── components/ # Svelte components
│ ├── data/ # Post and project loaders
│ └── constants.ts # SITE_URL constant
├── posts/ # Markdown blog posts
├── projects/ # Markdown project pages
└── routes/ # SvelteKit routes
└── robots.txt/ # Dynamic robots.txt (SSR)
static/ # Static assets (images, favicon)
docs/plans/ # Implementation plans for future work
SITE_URL- Set via environment variable, exposed throughvite.config.ts(envPrefix: ["VITE_", "SITE_"])- Falls back to
http://localhost:5173in development
ENVIRONMENT- "production" or "staging"SITE_URL- Site URL for the environment
Defined in wrangler.jsonc under env.production and env.staging.
pnpm dev # Start dev server
pnpm build # Build for production
pnpm preview # Preview production build
pnpm deploy # Build and deploy to production
pnpm deploy:staging # Build and deploy to staging
pnpm lint # Run prettier + eslint
pnpm check # Run svelte-check (TypeScript)SITE_URL=https://johnhooks.io pnpm deploy
SITE_URL=https://website-staging.johnhooks.workers.dev pnpm deploy:stagingGitHub Actions workflows in .github/workflows/:
deploy-production.yml- Manual trigger, deploys to productiondeploy-staging.yml- Manual trigger, deploys to stagingwrangler-deploy.yml- Reusable workflow with shared deploy logic
Required GitHub secrets:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
- Production: https://johnhooks.io (also www.johnhooks.io)
- Staging: https://website-staging.johnhooks.workers.dev
- Workers.dev: https://website.johnhooks.workers.dev
Most pages are prerendered at build time (prerender = true in layout).
Dynamic routes (SSR):
/robots.txt- Returns different content based onENVIRONMENT
- See
docs/plans/sitemap.mdfor sitemap implementation plan