// writing

How this site ships zero JavaScript frameworks — and scores 100/100/100/100

The engineering behind airahul.com is in the build, not the runtime. Here's how a hand-built Astro pipeline produces static HTML with no hydration, inlined CSS, and a perfect Lighthouse score — deployed to S3 + CloudFront.

4 min read PerformanceAstroFrontend

The site you’re reading has no React, no Next, no hydration, and no client-side router. It ships as static HTML with a critical path that’s essentially empty. Every page scores 100 across Performance, Accessibility, Best Practices, and SEO in Lighthouse, with a Largest Contentful Paint under half a second.

That’s not because the site does nothing interesting. There’s an animated aurora canvas, a custom orb cursor, a ⌘K command palette, and reveal-on-scroll throughout. The trick is where the engineering lives: in the build, not the runtime.

The one decision everything hangs off

Early on I made a rule and wrote it down as an architecture decision: no client framework, no hydration. For a personal site whose entire value proposition is “this person cares about craft and performance,” reaching for React would have been actively counterproductive. A framework runtime is a cost you pay on every visit, and it buys you nothing that a portfolio site actually needs.

So the source is Astro, used the way it’s most powerful: as a compiler that produces zero-runtime static HTML. Components, layouts, and a DRY design system exist at author time. By the time a page is served, all of that has been flattened into plain markup. The browser gets HTML and a little hand-written vanilla JavaScript — nothing to boot, nothing to reconcile.

Killing the critical path

A perfect Performance score is mostly about not blocking rendering. Three things do the heavy lifting here:

The interactive flourishes are all hand-written vanilla JS, emitted inline. The aurora is a <canvas> painting blurred light ribbons; the command palette is about 60 lines that dispatch by action type; reveal-on-scroll is one IntersectionObserver. None of it is a dependency, and none of it is an external request.

Accessibility isn’t the leftover

A 100 Accessibility score is the floor, not the goal. Underneath it: a skip-to-content link, visible keyboard focus rings (especially important because the custom cursor hides the system pointer), proper <main> landmarks, honest heading order, and full prefers-reduced-motion handling that stops the canvas and the animations for anyone who asks for less motion. The command palette is a real dialog with the right roles and keyboard model, not a div that happens to open.

Security lives at the edge

The static output is deliberately dumb, so the security posture lives in front of it. The bucket is private with Origin Access Control — you can’t hit it directly. CloudFront sits in front and attaches a Response Headers Policy that sets a Content-Security-Policy, HSTS with preload, X-Content-Type-Options, X-Frame-Options: DENY, a Referrer-Policy, and a Permissions-Policy. Because everything is inlined, the CSP has to allow inline styles and scripts — which is the one honest trade-off of the inline-everything approach, and a deliberate one.

The build is the product

Here’s the part that matters for anyone building something similar: the sophistication is entirely upstream of the browser.

The pipeline is a single DRY source — design tokens and page chrome defined once, case studies sharing one stylesheet, each page keeping only its unique scoped CSS. A push to main triggers GitHub Actions, which builds the site, runs it through HTML validation and a link checker, gates it against a Lighthouse budget (median-of-three, to survive noisy CI runners), and only then deploys to two CloudFront distributions. Clean URLs, the sitemap, and per-page metadata are all generated. Images are the newest addition: source screenshots now run through the build to emit AVIF and WebP with responsive srcset, so a phone never downloads a desktop-sized image.

You never see any of that. You see a page that appears instantly and works everywhere. That’s the whole point — the invisible engineering should be as good as the visible polish. A framework would have made the visible part marginally easier and the invisible part measurably worse. For this site, that’s a bad trade.

If you’re building a personal site, resist the urge to reach for the tool you use at work. Ask what the page actually needs to do, ship exactly that, and put your cleverness in the build where the user never has to pay for it.