Skip to content
All work
2026·Engineer & Open Source

loupe-core

Durable DOM anchoring — the engine

An open-source, dependency-free TypeScript library that stores a description of an element — its text, its neighbours, where it sits — instead of a CSS selector, then finds it again after the page has changed. No dependencies, no DOM mutation, no UI.

TypeScriptVitestPlaywrightZero deps
Open source · MIT
0
Tests
0
Weighted signals
0
Dependencies

The Problem

A CSS selector is an address: it says where an element lives and breaks the moment anything upstream is renovated. Every anchoring scheme built on stored selectors keeps failing the same way — pins land on the wrong element after a redeploy, or not at all, and teams learn to distrust the whole tool. What survives isn't the path to an element; it's a description of the element itself.

Research

Markup churns constantly, copy rarely, structure somewhere in between. If you describe an element by what it contains, what it sits between, and where it lives inside the nearest stable region, the description survives the changes that destroy addresses. Text and deliberate names — test ids, aria-labels, alt text — should be half the weight of any score, because they are the two things a designer renaming every class never touches. And the hard problem is ambiguity, not correctness: a duplicated component scores a perfect match against two elements, and a header that rejects both answers is how naive resolvers pin the wrong thing.

Approach

A pure library — DOM in, data out, no rendering, no network. captureAnchor snapshots an element and its neighbourhood as plain JSON; resolveAnchor finds it again and returns not an element-or-nothing but a band: anchored, flagged, or orphaned. Nine signals are compared, weighted, and renormalized over whatever was actually available, so an element with no test id is not punished for lacking one and an image leans on its alt text and filename instead of a missing text node. Geometry is bucketed and weighted at 0.02, kept only to break ties.

01

Three bands instead of element-or-nothing

A library that is 60% sure must neither lie nor give up. Flagged fires at a 0.45 floor or whenever the top two candidates sit within 0.08 of each other — even a perfect 1.00 duplicate is flagged, because two candidates tied.

02

Weights are relative, not a budget

Each signal counts only when the stored anchor actually had it, and the total renormalizes over what was comparable. An <img> has no text, so media — the filename — carries it through a redesign that changes everything else.

03

Filter classes before storing them

Emotion, CSS Modules, and styled-components classes are machine output. isGeneratedClass() discards them from the anchor, keeps the discard count as signal, and lets Tailwind and BEM tokens — things a person typed — keep their weight.

04

Never buy what you don't need

Candidates are gathered in tiers — identity, text, stored selector, same-tag sweep — so the 800-row list never pays the budget up front, and geometry only runs on the top six candidates. Same work, 15 layout reads instead of 400.

Challenges

The failure modes are invisible, so the tests had to be the spec. Eleven scenarios cover real things that happen between someone filing a comment and someone else reading it: class refactors, copy edits, inserted siblings, duplicated components, deleted regions, and a scope that disappears whole. Late content proved absence is not proof, so resolution retries on a backoff before declaring a pin orphaned. And jsdom has no layout engine, so dragged-area and reflow tests run in real Chromium at two viewport widths.

Outcome

loupe-core 0.1.x is live on npm under MIT with zero dependencies: 30 unit tests over eleven mutation scenarios and 10 browser tests. It powers every pin Loupe places, and because it has no opinion about what you anchor, it works anywhere a team needs an element found again after the page changed.

Reflection

The benchmark table is the spec — each row is a real thing that happens to a codebase. And the flagged band is the whole point: a pin that says 'I'm not sure this is still the same button' keeps the trust that a confidently wrong pin destroys.

Let's talk

Have an ambitious idea? Let's turn it into a product.

Whether it's a fintech platform, a developer tool, or an AI-powered experience — I love helping teams ship things people enjoy using.

Start a conversation