A Static Site That Still Feels Alive
No framework, no CDN, no trackers. How a handful of HTML files, one stylesheet and one script can still deliver search, filtering and fluid navigation.
This journal has no build framework, no package manager, no node_modules, no analytics, no font CDN, and no cookie banner. It is a directory of HTML files, one stylesheet, two small scripts, and some SVG. It still has instant search, category filtering, a table of contents, theme switching, and animated page transitions.
I wanted to find out how much perceived interactivity is actually just HTML and CSS with a thin layer of JavaScript on top. The answer: almost all of it.
The file tree
index.html about.html categories.html tags.html 404.html
posts/*.html <- one file per essay
assets/css/styles.css <- every visual rule
assets/js/site.js <- behaviour
assets/js/posts.js <- generated metadata index
assets/img/*.svg <- locally generated artwork
robots.txt
tools/ <- local generator and preview server
There is a small Python generator in tools/, but it is a convenience for keeping the shared header and footer consistent — not a runtime dependency. The files it writes are ordinary, complete HTML documents. You can open them directly from disk and they work.
Progressive enhancement as architecture
The rule I followed: if JavaScript never runs, every page must still be fully readable and every link must still work. That means article lists are real markup, not client-rendered JSON. Search, filtering, and transitions are added afterwards, on top of a complete document.
Search without a service
A generated metadata array of six essays is small enough to ship with the page. Filtering it in memory on each keystroke is imperceptibly fast and works offline. There is no search provider, no API key, and no third-party request leaking reading behaviour.
const matches = window.BLOG_POSTS.filter(post =>
(post.title + post.excerpt + post.tags.join(' '))
.toLowerCase().includes(query.toLowerCase())
);
The same array powers related essays and the command-palette style overlay. One data file, three features, zero network calls.
The performance budget
- Zero external origins. No fonts, icons, scripts, or images are fetched from anywhere else.
- One stylesheet, one behaviour script, both deferred or cached aggressively.
- All decorative artwork is SVG or CSS gradients; photographs are avoided entirely.
- Only
transformandopacityare animated on scrolling elements. content-visibilityand lazy loading keep long pages cheap.
What I gave up
There is no comment system, no server-side full-text index, and no admin UI. Writing a new essay means adding an entry to a Python data file and running one command. For a personal journal that is a feature, not a limitation: the surface area is small enough that I can still understand every byte I ship.
The most reliable dependency is the one you never added.
The reason this site has no package.json
Static does not have to mean inert. With a clear separation between content, presentation, and a thin behaviour layer, a directory of files can feel as considered as any application — and it will still load in 2036.