Part 1 of a series on building with the modern web platform — and letting AI handle the learning curve.
- Part 0: I Stopped Building With What I Know
- Part 1: The Web Doesn't Need Your JavaScript (this article)
- Part 2: One HTML Attribute Replaced 70 Lines of JavaScript (coming soon)
- Part 3: AI Didn't Write My Code. It Let Me Design It. (coming soon)
In the previous article I wrote about picking Astro for the first time and learning it while building. But there was another reason I chose Astro (opens in a new tab), one I didn't spell out: it pushes you toward shipping almost no JavaScript.
Static by default. Zero JS in the final page unless you opt in. No hydration unless you need it. For a content site, that wasn't a constraint — it was the point.
What we're still doing
I learned to build the web in the era of jQuery, then AngularJS (that old 1.x), then React. I got used to solving every interaction with JavaScript. A mobile menu? Toggle a class, listen for clicks, handle Escape, sync aria-expanded, manage an overlay. A modal? Same story. A dropdown? Same story. We've been writing the same scripts for years, with different syntax and different frameworks, but the logic is always ours.
Meanwhile the platform kept moving. The HTML and CSS we have today aren't the same as ten years ago. We have native dialogs, popovers, anchor positioning, container queries, :has(), discrete transitions. A lot of the behavior we've been implementing in JS is now declarative — an attribute, a pseudo-class, a few lines of CSS. We just never stopped to look.
I'd been seeing the "you don't need JavaScript" angle more and more on LinkedIn and in the dev community. It sounded good in theory. I hadn't really put it to the test.
The experiment
When I built the blog with Astro, I made a conscious choice: use the platform first. If the browser can do it, don't ship JS for it.
The result surprised me. The entire site is mostly HTML and CSS. The pages are static. Navigation, layout, theming, typography — all without a single line of custom JavaScript in the critical path. The only script I had left was the mobile menu: a hamburger button that toggled a nav panel, an overlay, and the usual event listeners. It was the last piece of "we've always done it this way" still sitting in the codebase.
So I asked a simple question: what if the browser could do that too?
What the platform can do now
Turns out it can. The Popover API (opens in a new tab) — popover and popovertarget — gives you exactly that: a panel that opens and closes, light-dismiss (click outside, Escape), focus management, and top-layer stacking. No state, no event listeners, no overlay div. You declare it in HTML. The browser handles the behavior.
That's one example. There are others. <dialog> for modals. inert for blocking interaction. @starting-style and transition-behavior: allow-discrete for animating things that appear and disappear. We're so used to reaching for JS that we don't check the spec anymore. I hadn't. Once I did, the mobile menu was an obvious candidate.
I'm not saying every app can be zero-JS. I'm saying a lot of what we assume has to be JS doesn't. Content sites, marketing pages, blogs, docs — huge chunks of the web are still shipping scripts for things the browser already knows how to do. We're carrying complexity we don't need.
Why we still ship it anyway
Habit is one reason. We know the pattern. We've debugged it. We're comfortable.
Another is that learning the new primitives takes a moment. You have to look up popover, read how ::backdrop works, figure out the right selectors. It's easier to copy the old script and move on. The cost of "not learning" feels low; the cost of "maybe it won't work in browser X" feels high. So we default to what we know.
And frameworks don't always push the other way. A lot of them give you components and hooks for menus and modals. They don't say "first check if the platform does this." So we keep building the same things in JS, framework after framework.
The goal of software design, for me, is removing complexity. If the platform can do the job, shipping our own version is complexity we're choosing to keep. Once I started looking at the blog that way, the mobile menu wasn't a feature to implement. It was a feature to delete — and replace with a few attributes and some CSS.
| Menu closed | Menu open |
|---|---|
![]() | ![]() |
I've blurred the screenshots so they don't affect the experiment I'm still running with this blog.
In the next article I'll walk through what actually changed: the before and after, the bumps we hit, and what we gained (and gave up) when one HTML attribute replaced about seventy lines of JavaScript.
Next in this series: One HTML attribute replaced 70 lines of JavaScript (coming soon) — the Popover API, step by step.

