Marketing and lead-generation site for ScaleForce.agency, an AI / automations / operations agency. Built with Astro 5 and Tailwind 4, with blog content aggregated from local markdown, Contentful, and the author's personal blog at thomhayner.com.
npm install
npm run dev # local dev server
npm run build # production build to ./dist/
npm run preview # preview the production build locallyDev server defaults to http://localhost:4321.
Set these in the Vercel project (Production + Preview + Development) or in a local .env for development. See .env.example for a template.
The build pulls content from external sources and will fail without these set:
| Variable | Purpose |
|---|---|
CONTENTFUL_SPACE_ID |
Contentful space for CMS-authored blog posts and case studies. |
CONTENTFUL_DELIVERY_TOKEN |
Contentful Content Delivery API token (published content). |
CONTENTFUL_PREVIEW_TOKEN |
Contentful Preview API token (draft content in preview mode). |
These are referenced only by the unwired Airtable integration under src/lib/astro-airtable/. They default to empty strings and the build does not require them. Set them only if you re-enable that integration.
| Variable | Purpose |
|---|---|
AIRTABLE_API_KEY |
Airtable API key (legacy integration). |
AIRTABLE_BASE_ID |
Airtable base id (legacy integration). |
These are consumed only by the api/contact.ts Vercel Function at request time. The static build does not depend on them.
| Variable | Purpose |
|---|---|
RESEND_API_KEY |
Resend API key used by the contact-form serverless function (api/contact.ts). |
CONTACT_TO_EMAIL |
Destination inbox for contact-form submissions. |
CONTACT_FROM_EMAIL |
Verified Resend sender used as the From: address on contact-form mail. |
| Command | What it does |
|---|---|
npm run dev |
Start the Astro dev server. |
npm run build |
Produce a production build in ./dist/. |
npm run preview |
Serve the built ./dist/ locally. |
npm run check |
Run check:astro then check:eslint. |
npm run check:astro |
Astro type/diagnostic check (astro check). |
npm run check:eslint |
ESLint across the repo. |
npm run fix |
eslint --fix across the repo. |
npm run astro ... |
Pass-through to the Astro CLI. |
- Astro 5 — static-site generation, content collections, image pipeline.
- Tailwind CSS 4 — CSS-first theme via
@tailwindcss/vite. - TypeScript 5.9 — strict mode.
- React 19 — installed dependency for legacy/planned widget work (e.g. the unwired
Calendly.astro); not currently wired into the active Astro runtime/bundle. - Node 22+ — required runtime for local dev and CI.
- Contentful — CMS for blog / case-study content.
- Vercel — production hosting.
feature → dev → main:
main— production. Deploys to the live site. PRs only, fromdev.dev— staging. Integration branch.- feature branches — short-lived (
feat/...,fix/...,chore/...) cut fromdev.
Standard flow:
git checkout dev && git pull && git checkout -b feat/my-change- Open a PR targeting
dev. CI must pass. - Once merged to
dev, verify on the staging deploy. - When a release is ready, open a PR from
dev→main. Merging triggers the production deploy.
Hotfixes may branch from main directly, but must be back-merged to dev immediately after release.
Non-trivial design choices are recorded as ADRs under docs/adr/. Start there for the why behind the phased Astro / Tailwind / React upgrades, the multi-source blog architecture, the CI layout, and the detach from the AstroWind template.
This codebase started from the AstroWind template (MIT-licensed, © onWidget) and has since diverged substantially — it now runs Astro 5, Tailwind 4, React 19, and Node 22+, with a bespoke multi-source blog pipeline, custom CI, and an ADR-based decision log. Upstream AstroWind is no longer tracked; future upgrades are owned directly here. See docs/adr/ADR-2026-04-21-detach-from-astrowind-template.md for the decision record.
MIT — see LICENSE.md.