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 carried | Strategy recorded | Confidence | What had to hold |
|---|---|---|---|
A data-pb-id attribute | data-pb-id | high | The value is at most 128 characters, and the attribute selector matches exactly one node |
A data-testid attribute | data-testid | medium | The same test, under the same length bound |
An id beginning with a letter or underscore, up to 64 characters of letters, digits, hyphens and underscores | id | medium | The id selector matches exactly one node |
| Nothing but its tag and its classes | css | low | The tag plus at most two of its classes already matches exactly one node |
| Not even that | css | low | A 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 node | none recorded | none recorded | The 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.
| Bound | Value |
|---|---|
| Selector the picker builds from a tag, its classes or a path | at most 256 characters |
| Selector ingest will accept | at most 512 characters |
| Segments in a path anchor | at most 5, joined only by the child combinator |
| Classes per segment | at most 2 |
| Pseudo-classes admitted | one, :nth-of-type, indexed 1 to 9999 |
| Attribute value in a data attribute anchor | 1 to 128 characters |
| A quoted passage | at 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.