JavaScript Without the Dark Side
Confirmed
The premise
An earlier article argued that mandatory client-side scripting carries real privacy and reliability costs: shadow profiling, silent failures, and bloat. The obvious objection is “so you just hate JavaScript.” Not quite. The useful distinction is between JavaScript as a requirement for basic function and JavaScript as an optional enhancement layered on top of something that already works. This site is proof the first is avoidable; this article is how.
Build the HTML baseline first
Progressive enhancement inverts the usual order. You build the document so that a browser with scripting disabled — or a script that failed to load on a flaky connection — still renders readable content and working navigation. Then, and only then, you consider whether a script would make a working thing nicer.
Concretely, the baseline means: real <a href> links (not
onclick handlers), real <form> elements with a
method and action, semantic headings, and server-rendered
state. If the server can produce the final HTML, the client has nothing left to do.
Add JS only as enhancement
The litmus test: disable scripting and use the feature. If it still works, your
JS is an enhancement. If it breaks, your JS is a dependency, and you have shipped the
failure mode the previous article described. A collapsible menu that is a plain nested
list without JS is an enhancement. A menu that is an empty <div> until
a bundle hydrates it is a dependency.
Server-rendered patterns that replace JS
Filtering / tabs. A query string plus server-side branching. The topic filters
on the Articles and Labs indexes here are exactly this: each tag is an
<a> to ?topic=Privacy, and the server renders the
filtered list. No JS, bookmarkable, and it works in a text browser.
// Server-rendered filter (mirrors this site's own index pages)
$topic = $_GET['topic'] ?? null; // read query string
foreach ($entries as $e) {
if ($topic !== null && !in_array($topic, $e['topics'])) continue;
echo renderCard($e); // render only matches
}
Expand / collapse. The HTML <details> and
<summary> elements give you native accordions with zero script.
<details>
<summary>Show the full command</summary>
<pre><code>rsync -avhP ...</code></pre>
</details>
Form validation. HTML5 required, type=email,
pattern, and min/max validate client-side with no
script — but you still validate again on the server, because client checks are a
convenience, never a control.
When JS is genuinely the right call
Being principled is not being dogmatic. Rich text editors, live canvas/graphics, real-time collaboration, and audio/video manipulation are legitimately interactive and have no server-only equivalent. The honest rule is a cost/benefit one: does this feature require interactivity that HTML and CSS cannot express? If yes, scope the script tightly, load it only on the page that needs it, and keep a usable fallback for the surrounding content. The failure of the modern web is not that JavaScript exists; it is that a static article refuses to render without a megabyte of it.
Takeaway
Treat JavaScript like any other dependency: justify it, scope it, and make sure the system degrades gracefully without it. “Works without JS” is not a nostalgic constraint — it is a reliability, accessibility, and privacy property you get almost for free if you build in the right order. This whole site is the proof: every page you are reading was rendered on the server and sent to you finished.
Related
- Unveiling the Dark Side of JavaScript Confirmed
- Privacy, Security, Anonymity, Convenience Confirmed
- Building a Disposable Web App Vuln Lab Lab / Educational
- Taking Over a Website Using Disguised WordPress Plugins Confirmed