An anchor records what was pointed at

A ticket points at a button on a page rebuilt twice since it was filed. Two anchors can hold it there, a selector and a quote, and they break apart.

Two glass anchor pins bridge a dark break, with the nearer pin glowing orange

Two anchors can hold a ticket to the thing somebody complained about: a selector, which names a node in the page’s structure, and the sentence they highlighted, which names words. They survive different edits to that page, so the anchor arrives with its confidence declared and the reviewer decides which one to believe.

Two anchors, and the page comes apart around them differently

A selector is an address. It says which node, in a tree, using whatever names that node was carrying when somebody pointed at it: an attribute, an id, a tag with a couple of its classes, a position among its siblings. It is the only one of the two anchors that can be clicked.

A quoted passage is a different record. “This paragraph is wrong” and “this sentence in this paragraph is wrong” are two different tickets, and a selector can only ever express the first, because the smallest thing it can address is an element. The quote carries the reviewer’s actual subject in words, which is also the form that survives a rebuild: the markup around a sentence can be regenerated by a different component on a different release, and the sentence somebody objected to is still findable by reading.

Both are kept when there is a highlight to keep, and the quote is attached only when the highlighted passage belongs to the element that was confirmed. A highlight left over from elsewhere on the page is dropped rather than filed against the wrong node.

The ladder the picker walks, and where confidence comes from

Picking an element in review mode does not compose a clever selector. It walks a fixed ordering of ways to name the element, takes the first that resolves to exactly one node in the document, and records which rung it landed on. The confidence on the ticket is that rung: a statement about which name the page offered, not a score somebody tuned.

What the element already carriedStrategy recordedConfidenceWhat had to hold
A data-pb-id attributedata-pb-idhighThe value is at most 128 characters, and the attribute selector matches exactly one node
A data-testid attributedata-testidmediumThe same test, under the same length bound
An id beginning with a letter or underscore, up to 64 characters of letters, digits, hyphens and underscoresidmediumThe id selector matches exactly one node
Nothing but its tag and its classescsslowThe tag plus at most two of its classes already matches exactly one node
Not even thatcsslowA path of at most five levels, each a tag with up to two classes and, where the node has same-tag siblings, its position among them, matches exactly one node
Nothing that resolves to a single nodenone recordednone recordedThe pick is refused

The ordering is a preference for names a person put there deliberately. A dedicated identifying attribute is high because somebody chose it and nothing else in the page mints one. A test id or an id is medium: a real name, but one a component library or a template loop can mint and re-mint per render. A tag with at most two classes, or a five-level path, is low, because it is a shape rather than a name.

The last row is the one worth reading twice. When no rung produces a unique name, the overlay says so on the element itself and points the reviewer one step up the tree to its parent. Nothing is filed against a node the page cannot name twice, so a reviewer ends up with a ticket about a container rather than a confident ticket about the wrong button. Inputs, textareas and selects are refused on the same screen for a different reason, along with anything the page has marked private and anything whose id or name reads like a credential. The refusal stops there: a button is selectable, which is why a button is the example above.

The grammar a stored anchor has to fit

The server does not accept CSS. It accepts a narrow grammar, and it re-checks that a selector matches the strategy it claims: a selection announcing itself as an attribute anchor while carrying a path is rejected at the door.

BoundValue
Selector the picker builds from a tag, its classes or a pathat most 256 characters
Selector ingest will acceptat most 512 characters
Segments in a path anchorat most 5, joined only by the child combinator
Classes per segmentat most 2
Pseudo-classes admittedone, :nth-of-type, indexed 1 to 9999
Attribute value in a data attribute anchor1 to 128 characters
A quoted passageat most 300 characters

The path grammar leaves out everything else CSS can express, on purpose: no descendant combinator, no sibling combinators, no attribute matching outside those two data attributes, no :has, no :nth-child. It is cheap to validate and useless as a payload. The attribute and id forms are checked against shapes of their own, which bar the combinators and the comma but say nothing about a colon, so an id anchor carrying a pseudo-class is stored rather than refused. That costs nothing here: a selector is text on a card, and the panel never evaluates one.

The length rows have a gap of their own. The picker measures its 256 on the tag-and-classes form and on the path; the three attribute rungs return the moment they resolve to a single node. An attribute value of 128 characters is short prose, but escaped for a selector it can come out several times longer, and a value made mostly of punctuation can end up past the bound the server checks. When that happens the anchor is refused at ingest rather than trimmed, and the report arrives without one.

An anchor is optional evidence and never a reason to refuse a report. A selection whose selector decodes to something matching a sensitive pattern, or one of the project’s own masked keys, is dropped whole, and the written feedback lands with the rest of the evidence capture around it.

A quote is a smaller record than it looks

The passage is capped at 300 characters at both ends of the wire, and a highlight shorter than two characters is treated as a stray click rather than a quote. Whitespace runs are collapsed to single spaces, because those runs come from how the HTML was indented and not from anything the reviewer dragged over. Past the cap, one character of the budget belongs to the ellipsis, and the cut prefers a word boundary only when there is a late one: backing up most of a quote to find a space is worse than cutting a word, and a long token such as a URL has no boundary at all.

What the quote deliberately omits is the surrounding context and the character offsets it was taken from. Both would widen how much of somebody else’s page ends up on the ticket, and neither buys anything the element and the passage together do not already give.

The passage is read back to the reviewer as a quotation while they type, so the note is written against the words rather than against a memory of them, and it travels on the ticket record with the selection. If the passage itself trips a sensitive pattern the quote is dropped and the element selection survives: a leaked secret costs the ticket its quote, never the thing that was pointed at.

What the card says, and what to give the page

The element block on a ticket prints the tag, the accessible name where the element had one, the selector itself as copyable text, the path of the page it was picked on, the strategy and the confidence. Read in that order they answer the reviewer’s real question, which is not where this thing was, but how firmly this ticket is still attached to what the reporter meant. A high line means the anchor is a name the page’s own author put there on purpose; a low one means it is a shape, worth confirming against the written description before anybody spends an afternoon on it.

One action follows from that, and it is cheap. Put data-pb-id on the elements worth keeping addressable, with a value that means something to your team rather than to a build: the checkout button, the price cell, the empty state, the components that generate arguments. Keep the value unique in the rendered page, because the rung is taken only when the attribute selector matches a single node, and a template that stamps one value down twenty rows sends the picker back to the shape-based rungs. Where it is unique, every report filed against that element is anchored by the attribute at high confidence, whatever the markup around it does between releases.

The rest is a reading habit, the same one a rebuilt screenshot asks for. Evidence describes a page at a moment, and a ticket that says out loud how it was anchored is cheaper to act on than one that leaves a reviewer guessing why the selector no longer lands where the steps to reproduce say it should.

Common questions

Why does a bug report point at the wrong element after the page is redeployed?

An anchor is recorded against the page as it stood when somebody pointed at it, and the ticket declares how firmly. A low-confidence anchor names an element by its tag, at most two of its classes, and its position among same-tag siblings where it has any; an element carrying a stable attribute is anchored by that attribute instead.

What does the confidence level on a selected element mean?

Confidence records which name was available on the element, not a score somebody tuned. High means a dedicated identifying attribute, medium means an id or a test id, and low means a tag with classes or a short path.

How do I keep an element on my page addressable by bug reports?

Add a stable identifying attribute to it, with a value that appears on one element only. An element carrying a unique `data-pb-id` is anchored by that attribute rather than by its position, and every report filed against it is recorded at high confidence. Repeat the value down a template's rows and the uniqueness test fails, so the pick falls to a weaker rung.

Can a feedback tool record which sentence somebody objected to?

A highlighted passage can be stored beside the element, capped at 300 characters and collapsed to single spaces. A selector addresses a whole element, so the quote is what separates a wrong paragraph from a wrong sentence inside it.

What happens when a page element cannot be identified uniquely?

The pick is refused rather than approximated. The overlay says the node cannot be identified and offers its parent, one step up the tree, so a ticket carries an anchor to a container instead of a confident anchor to the wrong node.

The Notarus team

The people who build Notarus. We write up the decisions behind the product when the reasoning is worth more than the changelog line.

Keep reading

Reports with the evidence attached.

Start free, one script tag.

<script src="…/widget.js" data-key="pb_pub_…">