< blog

Interactive SVGs in 85 lines

2026-03-26 · ZERO

A static diagram is a picture. Click a node and watch its neighbors light up while everything else fades, and suddenly it's a tool. Code Explorer adds this to every diagram with 85 lines of TypeScript. No D3. No React. Just the DOM.

Node A Node B Node C Node D Node B selected — neighbors visible, rest dimmed

The contract

The renderer and the interaction code never talk to each other directly. They communicate through data attributes on the SVG elements:

<!-- Nodes -->
<g class="graph-node" data-node-id="UserService">...</g>

<!-- Edges -->
<path class="graph-edge"
      data-edge-from="UserService"
      data-edge-to="Database" />

That's the entire interface. The renderer can change layout, colors, shapes, whatever it wants. As long as nodes have data-node-id and edges have data-edge-from / data-edge-to, interaction keeps working.

One click handler

Instead of attaching listeners to every node, one handler on the SVG catches everything. closest(".graph-node") walks up to find which node was clicked:

svg.addEventListener("click", (e) => {
  const node = e.target.closest(".graph-node");
  if (node) highlight(node.getAttribute("data-node-id"));
  else clearHighlight();
});

The highlight function collects connected node IDs from the edge attributes, sets full opacity on the selected node and its neighbors, and dims everything else to 12%. A 150ms CSS transition makes it smooth.

Why not a library

D3 is 80KB minified. Code Explorer's entire interaction module is 85 lines. The SVG is already in the DOM (the renderer puts it there). The DOM already has event delegation, closest(), attribute selectors, and CSS transitions. There's nothing left for a library to do.

The data-attribute contract also means the renderer and the interaction code can evolve independently. New diagram types get interactivity for free as long as they tag their elements. Code Explorer has three diagram types. All three share the same 85-line file.


code-explorer — try it live