Skip to main content
Automation

How we built a landing page in under 24 hours with Astro and AI

· 9 min read

82 commits. 29,183 lines of code. Three languages. WCAG AA accessible. About 10 hours of actual work, shipped the next morning. One founder, one AI agent, zero budget.

This isn’t a toy demo. This is hayka-pacha.io, the site you’re reading right now.

Here’s exactly how we did it, what worked, what broke, and whether this workflow is repeatable for your next project.

Why build a landing page in one day?

We needed a web presence fast. A real marketing site, not a placeholder. Something that makes potential partners think “these people know what they’re doing.” Here’s what we needed:

  • Trilingual (English, French, Spanish) with proper hreflang and i18n routing
  • Fast: sub-2s load times on every device
  • Accessible: WCAG AA compliant from day one
  • SEO-ready: structured data, sitemaps, meta tags, Open Graph images
  • Blog-ready: content collections with MDX, draft system, scheduled publishing
  • Lead capture: contact form with categorized inquiries, branded email notifications

A typical agency would quote 2-4 weeks and $5,000-$15,000 for this scope. We spent $0 and 17 hours.

The stack: why every choice matters

We didn’t pick tools because they’re trendy. Every dependency is there because it solves one problem without creating two more.

LayerToolWhy this one
FrameworkAstro 6Static-first, native i18n routing, zero JS by default
StylingTailwind CSS v4 + DaisyUI v5Utility-first + component library = fast UI without design debt
ContentMDX + Content CollectionsType-safe blog with Zod schema validation
HostingCloudflare WorkersEdge deployment, global CDN, free tier handles our scale
FormsResendTransactional email without SDK bloat: one API call
TestingPlaywrightE2E tests for i18n routing and form submission

Total dependencies: 12. No state management. No CSS-in-JS. No component library dragging 47 peer dependencies behind it. Fewer moving parts means less breaks.

What is Astro? Astro is a web framework that renders pages to static HTML at build time, shipping zero JavaScript to the browser unless you explicitly opt in. It’s ideal for content-heavy sites where performance and SEO matter more than client-side interactivity.

The AI-assisted workflow: what actually happened

Let’s be specific about what “AI-assisted” means. We didn’t type “build me a website” and walk away. A human made every decision. The AI wrote the code, fast.

Phase 1: Architecture (~1 hour)

I wrote a 400-line design spec. Brand identity, colors, typography, section requirements. Everything from the Quechua etymology of our name to exact OKLCH color values. If it wasn’t in the spec, it wasn’t getting built.

Claude Code took that spec and produced a 12-task implementation plan with dependencies and file paths. Then it scaffolded the Astro project, wired i18n, and created the content collection schema.

Key decision: We chose Astro’s native i18n routing (/{lang}/ prefix on all routes) over third-party i18n libraries. This meant the router handles language detection at the edge, and translation files stay as simple JSON with no runtime overhead.

// src/content.config.ts: the schema every blog post must satisfy
const blog = defineCollection({
  loader: glob({ pattern: '**/*.mdx', base: './src/content/blog' }),
  schema: z.object({
    title:       z.string(),
    description: z.string(),
    pubDate:     z.coerce.date(),
    lang:        z.enum(['en', 'fr', 'es']),
    category:    z.enum(['SEO', 'Infra', 'Automation', 'Business']),
    readTime:    z.number(),
    draft:       z.boolean().default(false),
  }),
});

This schema is enforced at build time. If a blog post is missing a required field or has an invalid category, the build fails. No silent errors, no broken pages in production.

Phase 2: Components (~5.5 hours)

This phase was fast. I described what I wanted in plain English, the AI wrote the component, I reviewed it, pushed back on what looked wrong, and the AI fixed it. Rinse, repeat, eight times.

We built 8 components in 5.5 hours:

ComponentPurposeJS Required?
Nav.astroLogo + language switcherNo
Hero.astroFull-screen hero with CTAsNo
Manifesto.astro3-beat brand narrative with scroll animationsMinimal (IntersectionObserver)
TechStrip.astroInfinite logo marquee, 13 tech partnersNo (CSS-only @keyframes)
BlogPreview.astroLatest 3 articles with category badgesNo
Contact.astroLead capture form with dropdownMinimal (fetch API)
Footer.astroLinks, social, language switcherNo
LanguageSwitcher.astroEN/FR/ES toggleNo

6 out of 8 components ship zero JavaScript. The only JS on the page is a lightweight IntersectionObserver for scroll animations (under 1KB) and the form submission handler. Everything else is rendered to static HTML at build time.

Phase 3: Polish and hardening (~6 hours)

Most “build it fast” projects skip this part. We didn’t.

Design system: Every color, spacing value, and typography rule got extracted into .claude/skills/hayka-design-system/SKILL.md. Now when the AI builds a new component, it reads this file first. Brand consistency without thinking about it.

Accessibility audit: We ran AccessLint across every component and fixed:

  • Missing form labels
  • Insufficient color contrast (recalibrated the entire OKLCH palette for WCAG AA)
  • Missing skip navigation link
  • Focus styles for keyboard navigation

Security hardening:

  • CSRF origin checks on the contact form API
  • Security headers via Cloudflare (CSP, X-Frame-Options, HSTS)
  • Environment secrets read from Cloudflare Workers runtime, not baked into the build

i18n completeness: Every single string in every component was extracted to translation files. We caught 14 hardcoded English strings that would have shipped untranslated.

Phase 4: Deploy and iterate (~4 hours)

Cloudflare Workers deploy, OG image, robots.txt, sitemap. Then 23 commits of pure copy tweaks. Rewording the manifesto. Fixing anglicisms in the French version. Getting the guillemets right.

Last commit landed before lunch.

What broke, and what we learned

Stuff broke. Here’s what.

The CI cross-platform nightmare

Our biggest time sink: 5 commits fighting native binary dependencies. Tailwind CSS v4 uses platform-specific native binaries (@tailwindcss/oxide). The package-lock.json generated on macOS didn’t include the Linux binaries needed for CI. We tried npm ci, cache clearing, and explicit optional dependencies before finding a stable solution.

Lesson: When using tools with platform-specific native modules, test your CI pipeline early, not after you’ve built 8 components.

The Cloudflare Pages vs Workers confusion

We initially configured for Cloudflare Pages, but the Astro Cloudflare adapter outputs a Worker format. Two commits were wasted on the wrangler.jsonc configuration conflict between Pages and Workers deployment modes.

Lesson: Read the adapter documentation before the first deploy, not after wrangler deploy fails.

The invisible content bug

We added a CSS class that hid content until JavaScript loaded (for scroll-in animations). This meant crawlers and screenshot tools saw empty sections. One commit to fix, but it could have killed our SEO if we’d missed it.

Lesson: Never hide content behind JavaScript unless you have a fallback. Search engines don’t wait for your IntersectionObserver.

The numbers

MetricValue
Actual work time~10 hours
Commits82
Files created/modified222
Lines of code29,183
Dependencies12
Languages3 (EN/FR/ES)
Components8
JavaScript shipped to browser< 2KB
Translation keys per language50
Bug fix commits26 (32% of total)

32% of commits were bug fixes. That’s normal. Moving fast means breaking things. The difference is we caught them in seconds because the AI could fix-and-test in a loop that would take a human ten times longer.

Is this workflow repeatable?

Yes. But it depends on you, not on the AI.

The 400-line design spec made this work. Without it, the AI would’ve guessed at every design decision and gotten most of them wrong. Garbage spec, garbage output. That’s true regardless of the model.

The disciplined stack helped too. 12 dependencies. Each one justified. The AI doesn’t struggle with your code. It struggles with your 47 transitive dependencies and the weird interaction between three overlapping libraries.

And honestly? I’ve been using Astro, Tailwind, and Cloudflare for years. The AI made me faster. It didn’t make me capable. If I didn’t already know this stack, I’d have spent the 17 hours debugging the AI’s mistakes instead of building.

This was also a content site. Static pages, a form, some animations. If you’re building something with complex state management or real-time features, expect more back-and-forth with the AI and more manual fixes.

What’s next

This site is the base for everything we’ll publish. Case studies about our infrastructure. Deep dives into the systems we run across 400+ sites on Kubernetes.

Next up: how our AI agents manage that entire fleet without us touching anything. Check our blog for the full series, or get in touch if you want us to build something like this for you.


FAQ

$0 in direct costs. Astro is open-source. Cloudflare Workers has a free tier. Claude Code is a subscription we already pay for. The real cost? 17 hours of a founder's time and a decade of experience that made those hours count.

No. You need to understand web architecture, deployment, and how to write a clear spec. The AI writes code. You make decisions. That split won't change anytime soon.

For a marketing site that's 95% static content, shipping a React runtime makes no sense. Astro sends zero JS by default. Better Core Web Vitals without even trying. Next.js is great when you actually need client-side interactivity.

Claude Code for everything. AccessLint for accessibility checks (via MCP). A design generation tool for visual components. No Figma. The design system lives in code.

One JSON file per language (src/i18n/en.json, fr.json, es.json), 50 keys each. Blog posts are separate MDX files per language, written by hand. No machine translation. Astro handles the routing natively: /en/blog/, /fr/blog/, /es/blog/.