I built 61 dev tools without a framework — here's what I learned about HTML tokenization
The pitch is simple: tools4market.online is 61 browser-side dev tools — cron expression generator, JSON→CSV, HTML minifier, SVG optimizer, Markdown↔HTML, hash generator, text diff, email validator, robots.txt generator,
The pitch is simple: tools4market.online is 61 browser-side dev tools — cron expression generator, JSON→CSV, HTML minifier, SVG optimizer, Markdown↔HTML, hash generator, text diff, email validator, robots.txt generator, and 50+ more. No login, no upload, no framework, no backend.
The interesting part is what I had to build to stay dependency-free:
1. A hand-written HTML tokenizer (~190 lines)
The HTML5 spec says
and <textarea> are RCDATA elements, but ONLY in the HTML namespace. Inside <svg>, they're normal elements. Get this wrong and a raw <title> inside an SVG silently swallows the rest of your page — which is exactly the bug I shipped and then spent a day finding.</p> <p>The fix is a namespace context stack (html / svg / math). When you enter <svg>, push svg onto the stack; RCDATA/RAWTEXT rules apply only when the stack top is html. This is what the HTML5 spec calls "foreign content" handling.</p> <p><strong>2. A multi-size favicon ICO encoder</strong></p> <p>Modern browsers (everything since 2015) accept embedded PNG inside ICO files. So I skipped BMP encoding entirely — the ICO is a 6-byte header, N×16-byte ICONDIR entries, then concatenated PNG bytes per size. Tested by reading the archive back and comparing every CRC with node:zlib.</p> <p>The PNGs themselves are rendered to canvas at 16/32/48/64/180/192/512, then toBlob'd. The ZIP is hand-encoded store-only (no DEFLATE) — favicons are tiny, no need for compression.</p> <p><strong>3. A conservative SVG optimizer</strong></p> <p>Strips comments, editor metadata (sodipodi/inkscape namespaces), unreferenced IDs, default attributes, collapses whitespace, shortens hex colors.</p> <p>The non-obvious part: before removing an ID, you have to scan for references — url(#id), href="#id", xlink:href="#id", marker-start/mid/end, clip-path, mask, filter. AND scan <style> text content for ID selectors (path#myId { ... }). AND scan SMIL begin="myId.click" attributes. I initially shipped without the <style> scan and called it a "documented limitation" — then went back and did it properly.</p> <p><strong>What I gave up staying dependency-free:</strong></p> <ul> <li>JS minifier (deferred — needs Acorn or Terser, ~50KB, not worth the cost until traffic justifies it)</li> <li>Full CommonMark (shipped a subset — tables, footnotes, GFM extensions all unsupported, documented on-page)</li> <li>Any client-side analytics beyond GTM</li> <li>A real test framework (using hand-rolled Node assertions)</li> </ul> <p><strong>The build:</strong></p> <p>Single Node script (build.mjs) reads tool definitions from src/data/<em>.js, inlines scripts from src/runtime/</em>.js, writes static HTML. Cloudflare Workers serves it. Desktop PageSpeed is 100/100/100/100 because there's nothing to slow it down — no framework hydration, no images on tool pages, no third-party JS.</p> <p>I'm shipping a new tool every few days. Looking for: what tool is missing from the set, what's broken in the ones I shipped, what tradeoffs you'd make differently.</p> <p>Site: <a href="https://tools4market.online">https://tools4market.online</a></p>Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.