Skip to content

Re anchoring

MohamedAshrafElsaed edited this page Jul 9, 2026 · 1 revision

Re-anchoring

The hardest problem in visual feedback: a pin must survive the developer rewriting the markup. Loupe stores a multi-signal fingerprint of the target element and re-resolves it by scoring candidates on every load and DOM change.

What is captured

captureAnchor(el) records:

  • Stable id / testiddata-testid, data-test, or a non-framework id (the strongest signal).
  • CSS path — rooted at the nearest stable ancestor (e.g. [data-testid="card"] > div:nth-of-type(2)).
  • XPath — an absolute positional path.
  • Text — normalized, truncated textContent.
  • Attributesrole, aria-label, name, type, alt, href, placeholder, title.
  • nth-of-type and the bounding rect + viewport at capture time.

How it re-resolves

flowchart TD
  A["resolveAnchor(anchor)"] --> B{"unique testid / id?"}
  B -- yes --> B1["score 0.98 — return"]
  B -- no --> C{"cssPath rooted at a stable id,<br/>single match, same tag?"}
  C -- yes --> C1["score 0.90 — return<br/>(survives changed content)"]
  C -- no --> D["weighted similarity scan"]
  D --> E{"best score ≥ 0.5?"}
  E -- yes --> E1["pin to best element"]
  E -- no --> E2["mark 'detached'"]
Loading

The weighted scan combines: text 0.34 · attributes 0.22 · testid 0.22 · cssPath 0.20 · tag 0.12 · position 0.10, normalized, accepted at ≥ 0.5.

Why the tiers matter

  • Tier 1 (testid/id) handles the common case — the developer kept the stable id.
  • Tier 1.5 (stable-ancestor cssPath) handles content changes: a revenue value that went from $482k to $482,109 still resolves because its position under a data-testid parent is stable.
  • Tier 2 (scan) handles structural changes — the element moved or its wrapper changed tag — by matching on text, attributes, and proximity.

If nothing clears the threshold, the comment is flagged detached rather than pinned to the wrong element. Verified live: see the before/after redeploy screenshots in the repo's docs/.

Clone this wiki locally