No JavaScript Needed

I have never been a fan of JavaScript. I don’t want it on my personal website. And I found ways to do things I usually would need JS for with static HTML and CSS.
After a phishing attack on my friend’s computer I started to look closer at all the code I run without ever reading it. That attack was Node.js, not JavaScript in a browser - but its core trick, fetching code from a remote server and executing it on the spot, is exactly what every third-party tag on a website does. So I decided to ditch JavaScript entirely, where possible. I started to use NoScript and saw how awful most of the websites on the Internet are, if you disable JavaScript. Tons of external integrations (tracker?, analysis of user behaviour?) and completely broken sites where even the first load of content is not executed.
I did not want to put users on my page in the same situation so I removed almost all JavaScript functionality from my site here and replaced it with static HTML and some CSS. Let’s have a little walk through what I normally would use JavaScript for and how I replaced it.
Why JavaScript is used
JavaScript is often used to add features that HTML is not capable of. But then people forget to remove JavaScript when HTML finally added support for a certain feature, f. ex. lazy loading of images. Browsers have done this natively for years, yet many sites still ship a library for it. Every script that stays is code the visitor has to download, parse and execute - on every single page load.
Another thing is tracking. Analytics, session recordings, heatmaps - all of this runs as JavaScript in the visitor’s browser. It collects data the visitor never agreed to share and it is often a reason why a site feels slow.
Or third-party integrations. Embedded videos, social media buttons, chat widgets or comment sections load scripts from other servers. Each of them tells another company that someone visited my page, which is something I do not want.
So either I can build it into my site directly, without relying on third-party JS integrations, controlling what goes in and out, or I skip it.
What I replaced
I understand, there is probably not a real HTML-only (in combination with CSS) alternative to every functionality you want to have on your website. I found that for my use cases I do not need to rely on JavaScript - and hopefully will not in the future.
Contact Form
Usually you would send your contact form with JavaScript. No page reload, nice success message once sent, or showing some errors underneath the input fields. My contact form uses staticform, my custom written form backend for static pages.
I get HTML validation of fields on the frontend using HTML5 attributes and proper field types.
1After submitting, the backend service validates again and redirects to a success or error page.
No JS needed.
Site search
A search on a static site usually means shipping a search index as a big JSON file and some JavaScript that crawls through it. Search engines have already indexed my page, so why not use them?
My search page is a simple form that sends the query to DuckDuckGo and limits the results to my domain with a hidden field.
1I chose DuckDuckGo because it does not track searches, save IP addresses, or build user profiles.
No JS needed.
Mobile menu
Using details element
The classic hamburger menu on mobile does not need JavaScript. HTML has an element for exactly this use case built-in:
. The is the clickable part, everything else is shown when it is open.
1
2
3 [OPEN ICON]
4 [CLOSE ICON]
5
6
7 Menu item 1
8 Menu item 2
9 Menu item 3
10
11layouts/partials/menu.html
The browser keeps track of the open state and sets the open attribute on
. With Tailwind’s group-open: variant I swap the menu icon for the close icon. list-none and the ::-webkit-details-marker selector remove the default triangle. On larger screens md:hidden hides it completely and the normal navigation shows up.
Using Popover API
A newer option is the Popover API, available in all major browsers. The popover attribute turns any element into a popover, and a button with popovertarget toggles it.
1
2
3 Menu item 1
4 Menu item 2
5 Menu item 3
6It works a bit differently than the details element with regard to placement and closing with the Esc key. Still, a very good alternative to JavaScript.
No JS needed.
Image Lazy Loading
For a long time lazy loading images meant a library watching the scroll position and swapping a data-src into the src attribute. Browsers have supported this natively for some years.
1loading="lazy"loads the image only when it gets close to the viewportdecoding="async"does not block rendering of the remaining page while decoding the imageheightandwidthlet the browser reserve the space, so the layout does not jump around
No JS needed.
Reading progress
A bar on top of the screen that fills while you scroll through a post. This is usually an event listener on scroll calculating the percentage and setting the width of an element. With scroll-driven animations CSS can do this on its own.
1@keyframes reading-progress {
2 to { background-size: 100% 100%; }
3}
4
5@media (prefers-reduced-motion: no-preference) {
6 @supports (animation-timeline: scroll()) {
7 body:has(main > div > article)::before {
8 content: "";
9 position: fixed;
10 inset: 0 0 auto 0;
11 z-index: 10;
12 height: 5px;
13 background: linear-gradient(var(--color-accent), var(--color-accent)) no-repeat left / 0% 100% var(--color-zinc-200);
14 animation: reading-progress 1ms linear both;
15 animation-timeline: scroll(root);
16 }
17 }
18}Scroll-driven animations are not supported in every browser yet, currently Firefox is lacking support for this. Honestly this is not a big problem for me: browsers without support simply do not show the bar, and Chromium-based browsers still cover a big chunk of the browsers people use.
No JS needed.
Closing thoughts
With all these changes I am quite happy with the balance of interactivity on my site and features for reading content - and all this without JavaScript.
There is one tiny script left: a visitor counter using GoatCounter, a privacy-respecting analytics solution. It does not use cookies or track individual visitors. It follows my understanding: I host the script on my own server, so no third-party code runs on my page - only the page view itself is sent to GoatCounter. You can find the link to the dashboard in the footer section.
(Almost) no JS needed.
- Form Validation Essentials: https://www.ashleysheridan.co.uk/blog/Form+Validation+Essentials