Hard performance budgets, enforced on every PR β’ Zero-JS baseline β’ Astro 7
π Live Demo β’ Quick Start β’ Documentation β’ Contributing
Most Astro templates hand you a fast first commit and no way to keep it fast. This one ships a performance ratchet β hard budgets wired into CI that fail the build when a change regresses them. The Lighthouse score is the evidence; the enforcement is the point.
- Enforced, not aspirational β halt-on-violation CI gates on JS bundle size, per-image size (source and build output), font-preload count, and Lighthouse on both mobile and desktop. A regression fails the PR β it doesn't ship. (ADR-039)
- Images can't sink you β the #1 real-world perf killer is gated: Astro
<Image>β AVIF with responsivesrcset, plus a 200KB-per-raster ceiling checked in CI (details in the next section). - Agentic discipline built in β Layered AI constitution (
CLAUDE.md+.claude/), role-separated workflow. Your agent clears the same gates you do. - Fast by default, measured β ~48KB raw JS (~17KB gzipped) total across the site; ~19β21KB gzipped CSS per page; 99 Performance / CLS 0 on home and blog, desktop and mobile.
- Modern stack β Astro 7.x Β· TypeScript 5.x (strict) Β· Tailwind 4.x Β· Biome 2.x Β· Node 24.x Β· pnpm 10.x
- Accessible β WCAG AA via semantic HTML, ARIA, and validated contrast; accessibility gated β₯ 95 in CI (most routes score 100).
One oversized hero image can cost more Lighthouse points than every other issue combined β it's the single most common regression adopters hit. So images are a first-class, gated concern here, not an afterthought:
- Modern formats by default β
src/components/atoms/Image.astrowraps Astro's<Image>, emitting AVIF with responsivesrcsetand lazy loading. Raw<img>for raster is discouraged; SVG passes through untouched. (ADR-030) - A hard per-image ceiling β 200KB per raster file, enforced in CI on both source (
public/,src/) and build output (dist/) β so a heavyweight PNG, including an accidental fallback emitted alongside AVIF/WebP, fails the build. Override withIMAGE_BUDGET_KB. (ADR-057) - Font preloads capped β β€ 2 preloaded fonts per page, so preloads don't crowd out the LCP image. (ADR-058)
Click Use this template on GitHub, then:
git clone https://github.com/YOUR_USERNAME/your-site.git
cd your-site
pnpm install
pnpm run devpnpm create astro@latest my-site -- --template clownware/astro-performance-starter
cd my-site
pnpm run devOpen http://localhost:4321/ β you're up and running.
First build? Token compilation happens automatically on first
devorbuildcommand.No pnpm? Run
corepack enablefirst, or see troubleshooting.
After cloning, update these files to make the template yours:
| File | What to change |
|---|---|
src/config.ts |
Site title, author, GitHub URL, docs URL, social links |
package.json |
name, description, author, repository, homepage, bugs |
LICENSE.txt |
Copyright year and holder name |
src/content/navigation/header.json |
Navigation links and GitHub URL |
src/content/bio/default.mdx |
Name, title, social links, bio text |
public/logo.svg |
Your logo / wordmark |
public/favicon.svg |
Your favicon |
tokens/base.json |
Brand colors |
.github/CODEOWNERS |
Uncomment and set your GitHub username |
CHANGELOG.md |
Start fresh for your project |
README.md |
CI badge URL, deploy buttons, --template command |
- Astro 6.x with zero-JS by default (islands when needed)
- TypeScript 5.x in strict mode for type safety
- Tailwind CSS 4.x with CSS-native
@themedesign token system - Biome 2.x for formatting and linting (20x faster than ESLint+Prettier)
- Node.js 24.x (locked via
.nvmrc) - pnpm 10.x (enforced via
engine-strict) - Atomic design structure in
src/components/ - Content collections with MDX support
- GitHub Actions CI/CD (build, lint, type-check, security audit)
- Pre-commit hooks for code quality
- E2E tests with Playwright, unit tests with Vitest
Everything you need to customize and extend lives in docs/:
- Onboarding Guide β Detailed setup and concepts
- Launch Demo β Get running in 5-10 minutes
- Quick Deploy β Ship to production in under an hour
- Implementation Roadmap β Phased development guide
- AI Context Guides β Optimized for AI assistants
The
docs/folder contains extensive reference material. These files never ship to production.
One path, three natural stopping points:
- Foundation (Phases 0β4) β Working site skeleton with design system, content schemas, and CI pipeline β pre-configured in template
- Build (Phases 5β8) β Components, pages, content, and QA β scope each phase to Essential / Recommended / Advanced
- Polish (Phases 9β12) β Performance budgets, deployment, documentation, monitoring β stop when goals are met
See the Implementation Guide for details.
This template is a reference implementation of the layered AI constitution pattern β agent-readable rules with halt-on-violation enforcement. Drop Claude (or any agent) into the repo and it works under the same rules you do.
The constitution layers responsibility:
CLAUDE.mdβ top-level entry: stack, scope boundaries, halt conditions.claude/engineering.mdβ components, design system, TypeScript, testing discipline (ADR-037).claude/workflow.mdβ three-pass Architect β Coder β Reviewer (ADR-038), quality gate (ADR-039).claude/stack.mdβ tooling, versions, performance budgets.claude/roles/β per-pass prompts (architect, coder, reviewer)
Every rule names its halt condition with a stated reason β no soft guidance. pnpm quality:ci halts on broken tests, lint, types, or markdown β the same gate your agent must clear before claiming done.
pnpm quality:ci # the halt-on-violation gate β agent must clear this before claiming donePattern source: Robert C. Martin and Justin Martin's Clean AI: Agentic Discipline series. Layered-constitution precedent: unclebob/swarm-forge.
The constitution is the canonical source. The same rules ship in multiple formats so the discipline is tool-agnostic:
| Tool | File(s) | What it does |
|---|---|---|
| All modern AI tools | AGENTS.md (root) |
Cross-tool spine β read natively by Cursor, Codex CLI, Copilot, Aider, Devin, Zed, Continue, Amp, Amazon Q |
| Claude Code | CLAUDE.md + .claude/ |
Constitution, layered engineering/workflow/stack files, skills, subagents |
| Windsurf | .windsurfrules (root) |
Thin overlay for Cascade-specific directives; full context comes from AGENTS.md |
| Maintenance | pnpm agents:build |
Regenerates AGENTS.md from the layered source files; CI fails on drift |
One source of truth, every tool stays in sync. Edit the layered files in .claude/ (or CLAUDE.md for halt-on-violation rules) and run pnpm agents:build. See ADR-045 for the cross-tool spine rationale and ADR-036 for the layering.
Entry points:
- Agent workflow:
.claude/workflow.mdβ three-pass pattern, quality gate, ADR discipline - Architectural constraints:
docs/adr/β every Accepted ADR is a rule the agent must respect - Performance limits: budgets in
.claude/stack.md, checked before adding dependencies - Zero config: no MCP server, no API β well-structured markdown any agent can read
See AI Context Setup Guide for the multi-tool sync workflow.
You don't have to build on this template to benefit from it β you can point an agent at an existing site and use this repo as the standard to audit against. In one real case that workflow took a personal site from Lighthouse 73 to 100 (desktop) by surfacing exactly the regressions these gates encode.
The workflow:
- Clone this repo next to your project (or open both side by side).
- Tell your agent: "Audit this site against the norms in the astro-performance-starter repo β its ADRs,
.claude/stack.mdbudgets, and CI gates. Report deviations ranked by performance impact." - Have it check what this template treats as non-negotiable:
- Images β modern formats, responsive
srcset, every raster under the 200KB ceiling (source and shipped). Usually the single biggest win. - Islands β Preact for interactive islands (ADR-001), hydrated as late as possible up the ladder
client:visibleβclient:idleβclient:mediaβclient:load(client:loadonly with ADR justification). - Fonts β subset
woff2, β€ 2 preloads per page so preloads don't fight the LCP image (ADR-053). - Budgets & gates β JS/CSS/image/font budgets and Lighthouse floors, run on every PR so nothing silently regresses.
- Images β modern formats, responsive
Because each norm has an ADR behind it, the agent can justify every recommendation from the why rather than cargo-culting it.
The full apparatus is built for a template that must never regress. A personal site rarely needs all of it. Keep the ratchet, drop the ceremony β this is subtraction from one config, not a second maintained tier:
Keep β this is the performance ratchet, and it's cheap to run:
lighthouserc.json+lighthouserc.mobile.jsonβ the Lighthouse gates- the JS bundle-size step,
images:gate, andfonts:gatein CI pnpm test:e2eas a smoke test that pages actually render
Safe to drop for less overhead:
- Mutation testing β remove
.github/workflows/mutation.yml, thetest:mutatescript, and Stryker from devDependencies (ADR-042). - The full ADR apparatus β
docs/adr/and thedocs:countguard keep a shared template honest; a solo project can keep a lightweightDECISIONS.md(or nothing) and dropdocs:countfromquality:ci. - Cross-tool agent spine β if you use only one agent, drop
agents:build/agents:checkand keep justCLAUDE.md. - Template-invariant guards β
version:checkandog:checkprotect template-specific invariants; trim them fromquality:cias needed.
pnpm run dev # Start dev server
pnpm run build # Production build
pnpm run preview # Serve already-built dist/ locally
pnpm run quality # Full quality check (format + lint + type-check)
pnpm run test:unit # Unit tests (Vitest)
pnpm run test:e2e # E2E tests (Playwright)
pnpm run test:a11y # Accessibility tests (axe-core)
pnpm run tokens:build # Rebuild design tokens (rarely needed)All Scripts Reference
Development
| Command | Description |
|---|---|
pnpm run dev |
Start dev server on port 4321 |
pnpm run dev:host |
Dev server exposed to LAN |
pnpm run dev:debug |
Dev server with verbose logging |
pnpm run preview |
Serve already-built dist/ locally |
pnpm run preview:build |
Build then preview |
Code Quality
| Command | Description |
|---|---|
pnpm run quality |
Format + lint + markdown lint + type-check (auto-fixes format) |
pnpm run quality:ci |
Same checks, no auto-fix (CI mode) |
pnpm run format |
Format with Biome |
pnpm run format:check |
Check formatting without writing |
pnpm run lint |
Lint with Biome |
pnpm run lint:md |
Lint markdown files |
pnpm run check |
Astro diagnostics |
pnpm run check:types |
TypeScript type-check |
Testing
| Command | Description |
|---|---|
pnpm run test:unit |
Vitest (single run) |
pnpm run test:coverage |
Vitest with coverage report |
pnpm run test:e2e |
Playwright (all browsers) |
pnpm run test:e2e:ui |
Playwright with interactive UI |
pnpm run test:a11y |
Accessibility tests (axe-core via Playwright) |
Advanced / optional β not on the clone critical path; not in quality:ci.
| Command | Description |
|---|---|
pnpm run test:mutate |
Mutation testing (Stryker). Slow; runs nightly in CI, rarely run locally. See ADR-042. |
Build & Deploy
| Command | Description |
|---|---|
pnpm run build |
Validate env + build tokens + Astro build |
pnpm run build:ci |
Same with verbose output |
pnpm run tokens:build |
Compile design tokens from tokens/ |
pnpm run clean |
Remove dist/, .astro/, tokens/dist/ |
pnpm run clean:all |
Clean + clear node_modules/.cache |
Performance & Validation
| Command | Description |
|---|---|
pnpm run perf:lighthouse |
Lighthouse HTML report (requires running dev server) |
pnpm run perf:budgets |
Track JS/CSS budget violations |
pnpm run perf:baseline |
Establish performance baseline |
pnpm run bundle:analyze |
Build + analyze bundle composition |
pnpm run design:validate |
Validate semantic color contrast ratios |
pnpm run budgets:validate |
Validate budget override configuration |
pnpm run images:analyze |
Analyze image sizes and formats |
pnpm run images:gate |
Fail on any raster image over the per-file budget (CI gate, ADR-057) |
pnpm run fonts:gate |
Fail on any built page over the font-preload budget (CI gate, ADR-058) |
pnpm run images:optimize |
Interactive image optimization |
Release & Maintenance
| Command | Description |
|---|---|
pnpm run release:changelog |
Generate CHANGELOG from commits |
pnpm run audit |
pnpm audit (production deps) |
pnpm run roadmap:update |
Update implementation roadmap status |
Budgets (see .claude/stack.md):
- JavaScript: < 160KB raw total
- CSS: < 50KB
- Images: < 200KB per raster file, source + build output β enforced in CI (ADR-057); override with
IMAGE_BUDGET_KB - Font preloads: β€ 2 per page β enforced in CI (ADR-058); override with
MAX_FONT_PRELOADS - Lighthouse: gated on desktop and mobile β Performance β₯ 90, Accessibility β₯ 95, Best-Practices β₯ 95, SEO β₯ 90 (CI floors; see
lighthouserc.json+lighthouserc.mobile.json)
The default starter ships well under budget β measured on a production build:
- JavaScript: ~48KB raw (~17KB gzipped) total across the whole site; a typical page loads only the view-transition router plus a tiny page script.
- CSS: ~19β21KB gzipped per page β a ~17.6KB shared stylesheet plus 1β3.5KB of inlined critical styles (fonts, view transitions); β23β24KB on pages with code blocks.
- Lighthouse: 99 Performance / CLS 0 on home and blog β desktop and mobile (measured; desktop re-verified at 100/CLS 0 on home, blog index, and a post, July 2026); accessibility 96β100 across all gated routes.
Contributions welcome! See CONTRIBUTING.md for guidelines.
Licensed under the MIT License.
Status: Active development β’ v0.9.0
