General5 min read

Building Zero-JavaScript Websites: Why Less Is More

How reducing front-end client JS payloads drastically increases speed, battery life, and SEO performance.

E

Listen to Article

Click play to listen to audio narration

Building Zero-JavaScript Websites: Why Less Is More

Modern websites often bundle megabytes of client-side JavaScript for features that could easily be handled with modern CSS and server-side rendering.


The Hidden Cost of Client-Side JavaScript

Heavy JavaScript bundles cause:

  1. High CPU Utilization: Parsing and executing JS consumes mobile device CPU and battery.
  2. Network Latency: Large bundles degrade performance on 3G/4G connections.
  3. Poorer Accessibility: Over-reliance on JS-driven renders often breaks accessibility tree defaults.

Achieving Interactivity with Modern CSS

Many legacy JS tricks are now natively supported in pure CSS:

Smooth Accordions with <details>

<details class="p-4 border rounded">
  <summary class="font-bold cursor-pointer">Click to toggle content</summary>
  <p class="mt-2">No JavaScript required for accordion state!</p>
</details>

CSS Scroll-Driven Animations

CSS scroll animations eliminate the need for heavy JS scroll libraries.


Frameworks That Support Zero-JS Defaults

  • Astro: Strips JavaScript by default unless explicitly instructed (client:load).
  • Eleventy: Pure static HTML generator.
  • SvelteKit / Next.js: Support static rendering modes.

Build leaner, faster, and more accessible websites by prioritizing zero-JS architecture.

What “Zero JavaScript” Actually Means

A zero-JS site delivers pure HTML and CSS to the browser. No JavaScript bundle downloads, no hydration cost, no JavaScript parse time. The browser renders markup directly.

This is not the same as no interactivity. CSS alone supports:

  • Animated transitions and keyframes
  • Responsive layouts via media/container queries
  • Hover and focus states
  • The <details> / <summary> disclosure pattern
  • Form validation using :valid / :invalid pseudo-classes
  • The <dialog> element (requires minimal JS for focus management, but no framework)

Building a Collapsible FAQ with Zero JavaScript

<details class="faq-item">
  <summary>What is static site generation?</summary>
  <p>
    Static site generation pre-builds HTML pages at compile time.
    The server delivers pre-rendered files; no runtime computation occurs per request.
  </p>
</details>
details.faq-item summary {
  cursor: pointer;
  font-weight: 600;
  padding: 0.75rem 0;
  list-style: none;
}
details.faq-item summary::marker { display: none; }
details.faq-item[open] summary { color: var(--accent); }

The <details> element is interactive, fully keyboard navigable, and requires zero JavaScript.

CSS-Only Dark Mode

:root {
  color-scheme: light dark;
  --bg: #ffffff;
  --text: #111111;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #111111;
    --text: #f0f0f0;
  }
}

body {
  background: var(--bg);
  color: var(--text);
}

No localStorage, no script, no flash of unstyled content — the browser applies the correct scheme before rendering.

When to Accept a JavaScript Dependency

Zero-JS is appropriate for:

  • Informational publications and blogs
  • Marketing landing pages
  • Documentation sites
  • Portfolio pages

JavaScript is genuinely required for:

  • Real-time data (WebSocket feeds, live pricing)
  • Client-side state management (shopping cart, authentication tokens)
  • Complex form workflows (multi-step wizards, file uploads)
  • Maps, charts, and data visualisations

Performance Impact

A typical React SPA ships 150–400 KB of JavaScript before your application code. A zero-JS Astro page ships 0 bytes of JavaScript by default. On a 4G mobile connection, parsing a 300 KB JS bundle takes roughly 300–1000 ms depending on the device CPU.

For content-first sites, zero-JS pages consistently score 100/100 in Lighthouse Performance audits.

Building Zero-JS Pages in Astro

Astro is zero-JS by default. Every .astro component renders to static HTML unless you explicitly add a client directive:

---
// This renders to pure HTML — no JavaScript sent to browser
const title = "Hello";
---
<h1>{title}</h1>

<!-- Only hydrate this component when it enters the viewport -->
<InteractiveChart client:visible />

Remove client:* directives to return any component to zero-JS output.

E

Written by Editorial Team

Tech contributor covering software architecture, AI research, cloud infrastructure, and systems engineering practices.

View Profile
Recommended For You

Related Articles

Quick:
Navigate Select
Loading search index...