Building this Astro Template
Behind-the-scenes case study of building a production-ready Astro starter template focused on performance, accessibility, and developer experience.
Key Results
Tech Stack
Project Overview
Most Astro starters prioritize features over fundamentals, shipping with bloated dependencies, inconsistent accessibility, and performance that degrades the moment a real design is applied. This project set out to prove a different premise: that a beautiful, branded site can be fast and accessible by default, with no trade-offs left for teams to rediscover later.
The result is the Astro Performance Starter—a production-ready foundation that clocks a 99 Lighthouse performance score out of the box and reaches 100/100 on accessibility, best practices, and SEO.
The Challenge
The web performance space is littered with starters that look great in demo conditions but fall apart under real-world constraints. Three patterns kept appearing across projects:
- Accessibility as an afterthought. WCAG compliance was either assumed or deferred, meaning teams would ship inaccessible sites and only discover it during audits.
- Design tokens missing entirely. Hard-coded hex values scattered across stylesheets made brand updates painful and dark mode nearly impossible to retrofit.
- Zero-JS as a goal with no tooling support. Astro’s islands architecture is powerful, but without guidance on when to hydrate, teams default to hydrating everything.
The goal was to solve all three at the architecture level—not as documentation, but as constraints baked into the template itself.
Architecture
Why Astro?
Astro’s component island model is uniquely suited to content-heavy sites: HTML is rendered at build time, JavaScript ships only where interactivity is explicitly requested. This makes “zero JS by default” a natural outcome of the framework, rather than a discipline teams must enforce manually.
Pairing Astro with Vite 7 and Preact (instead of React) keeps the interactive island budget small. A Preact component with hooks weighs roughly 4KB gzipped versus React’s 45KB—a meaningful difference when optimizing for First Input Delay on low-powered devices.
Core Stack
- Framework: Astro 6.x + Vite 7.x
- Styling: Tailwind CSS 4.x configured via CSS-first
@theme inline - TypeScript: Strict mode throughout, including content schema validation
- Content: MDX with typed Content Collections and Zod schemas
- Image Optimization: Sharp with AVIF/WebP output and responsive
srcset - Code Quality: Biome 2.x — lint and format in a single pass, ~20x faster than ESLint + Prettier
Design Token Pipeline
Rather than sprinkling color values throughout component files, all visual decisions flow through a single token pipeline:
tokens/base.json → tokens/semantic.json → tokens/dist/tokens.cssBase tokens define the raw palette (grays, brand colors, spacing scales). Semantic tokens map those primitives to roles (foreground-primary, background-secondary, border-primary). Components only ever reference semantic tokens, which means a brand refresh is a one-file change—and dark mode is handled automatically via CSS custom property overrides on the .dark class.
Component Architecture
Components are organized into three tiers mirroring atomic design:
- Atoms —
Button,Badge,Image: single-responsibility, no internal state - Molecules —
PostCard,ProjectCard,Head: composed from atoms, tied to data shapes - Structural —
Header,Footer,Section,Grid: layout primitives used once or twice per page
This separation means atoms can be tested in isolation, molecules can be swapped without touching layout, and structural components evolve independently as the site grows.
Key Features
Performance
Sharp processes every image at build time, generating AVIF with WebP fallback and emitting a srcset for five responsive breakpoints. The result is an LCP image that loads at the right resolution regardless of device—without any client-side JavaScript.
Font loading uses font-display: swap with preload hints for the critical weight, eliminating layout shift from late-loading type. The total JavaScript budget across the entire site is 47KB gzipped, most of which is the Preact runtime loaded only on pages with interactive islands.
Accessibility
WCAG AA compliance is enforced structurally, not just documented:
- Color contrast is validated at the token level. Semantic tokens are designed so any foreground/background pairing meets the 4.5:1 ratio for normal text.
- All interactive elements use visible
:focus-visiblerings derived from the primary color token. - Skip navigation, landmark roles, and logical heading hierarchy are part of the base layout—not optional additions.
- The
SkipLinkcomponent ensures keyboard users can bypass repeated navigation on every page.
Developer Experience
New contributors can clone the repo and start productive development in under five minutes. The setup involves no manual configuration: Biome handles formatting and linting from the first commit, TypeScript strict mode catches type errors at the editor level, and the token pipeline regenerates automatically on npm run build.
Results
Performance Metrics
| Metric | Target | Achieved |
|---|---|---|
| Lighthouse Performance | 95+ | 99/100 |
| Lighthouse Accessibility | 95+ | 100/100 |
| Bundle Size (JS) | <100KB | 47KB |
| Build Time | <30s | 14.6s |
| First Contentful Paint | <1.8s | 0.9s |
| Largest Contentful Paint | <2.5s | 1.3s |
Impact
The template has been deployed to multiple production client projects without modification to the performance or accessibility layer. Onboarding a new developer to an active project built on this template takes less than five minutes from clone to first commit.
Sites maintained on this foundation consistently hold 95+ Lighthouse scores after content updates—the architecture absorbs change without degrading the baseline.
Lessons Learned
What Worked
Zero-JS baseline delivered the most measurable return. By defaulting to static HTML and adding hydration only for explicit interactive components, the template avoids the performance cliff that hits most React-based sites at scale. Teams working with the template haven’t needed to think about bundle optimization because the architecture makes the right choice the easy choice.
The design token pipeline proved its value when a client requested a brand color change mid-project. Updating two values in tokens/semantic.json propagated correctly through every component, including dark mode variants—no grep-and-replace required.
Content Collections with Zod schemas caught several data modeling mistakes at build time rather than at runtime. The type safety extends all the way to MDX frontmatter, so adding a new content field means updating the schema first, not discovering missing data in production.
Challenges
Semantic token naming required more upfront discipline than anticipated. Early iterations used vague names like color-accent that became ambiguous once secondary brand colors were introduced. The solution was a strict naming convention: foreground-* for text, background-* for surfaces, border-* for dividers—roles rather than colors.
MDX and Content Collections had a subtle interaction: the Zod schema validation runs on frontmatter fields but doesn’t validate the rendered content tree. Custom MDX components (Callout, Figure) needed their own prop validation to surface helpful errors when authors used them incorrectly.
Documentation sync between code and the template’s own ADRs (Architecture Decision Records) required a manual discipline. Automated tools that parse JSDoc into documentation introduced version drift when the code evolved faster than the tooling. Switching to hand-maintained ADRs—brief, decision-focused, version-aware—proved more reliable and faster to update.
Summary
This template demonstrates that with the right architecture and discipline, it’s possible to deliver a beautiful, accessible, and performant site—without sacrificing developer experience or maintainability. The constraints are built in, so teams spend their time building features, not debugging performance regressions.