I was working on an Open Graph inspector last week. You know the drill: check if a page has the right og:title, og:image, the usual social sharing metadata. I found three tools online. One wanted me to create an account. Another loaded 4MB of JavaScript for what is essentially a string parser. The third one was down.
![]()
So I built one in a single HTML file. 75KB. Open it in a browser. Done.
Then I looked at my desktop and realized I’d been doing this for years. A media query checker here, an OG generator there. All single HTML files. All sitting in random folders.
That’s when the idea clicked: what if I collected all of these into one repo, made it open source, and let other people add their own?
The Architecture Decision: Why a Build Step for a “No Build” Project
Here’s the irony. The whole philosophy is “no build step, no dependencies, just open the file”. But the landing page that showcases these tools? It needs a build step.
The reason is simple. I wanted contributors to only touch a JSON file when adding a new tool. Not wrestle with HTML markup, not copy-paste card templates, not break the layout. Just add an entry:
{
"id": "open-graph-inspector",
"name": "Open Graph Inspector",
"shortDescription": "Analyze meta tags, social previews, and SEO metadata for any URL.",
"longDescription": "## Open Graph Inspector\n\nA comprehensive...",
"category": "web-seo",
"tags": ["open-graph", "meta-tags", "seo", "social-media"],
"techStack": ["HTML5", "CSS3", "Vanilla JS", "Fetch API"],
"difficulty": "Medium",
"status": "live"
}
The build script reads this JSON and generates the full landing page. Zero npm dependencies. Just node build.js. The individual tools themselves remain pure single-file HTML with no build step whatsoever.
The Build Script: 0 Dependencies, 5 Designs
The build.js is roughly 500 lines of plain Node.js. No Express, no templating engines, no markdown libraries. It does three things:
- Reads
tools.jsonand enriches each tool entry (derives file paths, GitHub links, checks if a screenshot PNG exists) - Converts Markdown to HTML using a hand-rolled parser (handles headers, lists, code blocks, links, bold, italic)
- Generates
index.htmlwith five complete design layouts you can switch between
The five designs exist because I genuinely couldn’t pick one. So I built all of them and put a switcher bar at the top:
- Tool Directory with search, category filters, and tag filters
- Hero + Scroll with stats and categorized tool lists
- Interactive Showcase with a live JSON formatter demo embedded in the page
- Terminal / Dev with ASCII art and monospace everything
- Bento Grid with mixed-size cards
Each design pulls from the same tool data. Click any tool card in any design and you get a modal with the full description, tech stack badges, tags, and links.
The Filtering System
Design 1 (Tool Directory) has three layers of filtering that combine with AND logic:
Category filter (pills) ──┐
Tag filter (from JSON) ──┼── AND ── visible cards
Search (text input) ──┘
Tags are collected automatically from every tool’s tags array in the JSON. No hardcoded list. Add a tool with a new tag, rebuild, and the tag pill appears. Click a tag to filter, click again to deselect.
The implementation is straightforward. Each card gets data-category, data-tags, and data-search attributes baked in at build time. The client-side JS just reads those attributes:
cards.forEach(function (c) {
var okCat = activeCat === "all" || c.dataset.category === activeCat;
var okTag = !activeTag || c.dataset.tags.split(",").indexOf(activeTag) !== -1;
var okSearch = !q || c.dataset.search.indexOf(q) !== -1;
c.style.display = okCat && okTag && okSearch ? "" : "none";
});
No framework. No virtual DOM diffing. Just show/hide with display: none. It’s fast because there’s nothing to be slow.
Screenshots: PNG Next to HTML
Every tool can have a screenshot. The convention is dead simple: if your tool is tools/json-formatter.html, your screenshot goes at tools/json-formatter.png. The build script checks fs.existsSync() for each tool and sets a hasThumbnail flag. If the PNG exists, you get the real screenshot on the card and modal. If not, you get the category emoji as a placeholder.
No image processing pipeline. No responsive image generation. Just a PNG file next to your HTML file.
Making It SSoC-Ready
This repo is part of Social Summer of Code, an open-source programme for students and beginners. That shaped a few decisions:
- Only Easy and Medium labels. No Hard. The single-file constraint naturally keeps scope manageable.
- Every PR is either a new tool or an enhancement. Clear, scoped contributions.
- The JSON-only workflow means a contributor’s first PR can literally be adding 10 lines to a JSON file and dropping two files in
tools/. - A starter HTML template in the contributing guide so nobody starts from a blank file.
Points are 20 for Easy, 30 for Medium. Labels are assigned by the maintainer, slightly negotiable.
Deployment
Cloudflare Pages. Build command: node build.js. Output directory: /. That’s the entire config.
The build generates index.html in the repo root. Cloudflare serves that plus everything in tools/. Each tool is directly accessible at its own URL. No routing, no SPA, no 404 handling needed.
What I Learned
The constraint of “one tool, one file” is surprisingly freeing. You don’t think about architecture. You don’t debate folder structures. You don’t set up linting configs for a file that will never import another file. You just build the thing.
The hardest part was the landing page, and that’s because the landing page is the only part that isn’t a single file. There’s a lesson in there about where complexity actually lives in software projects. It’s rarely in the individual features. It’s in the thing that ties them all together.
The repo is live. One tool so far (the OG Inspector), nine empty categories waiting for contributions, and a build system that turns JSON into five different landing pages. If you’ve ever built a quick browser utility and thought “someone else might find this useful”, this is the place for it.
