Selectors
Every visual edit is anchored to a selector: a string that tells the snippet runtime which element to modify. Picking a stable selector is the difference between a test that runs for months and one that breaks the next time engineering touches the page.
The selector ladder
When you click an element, the Visual Editor generates a chain of candidate selectors by walking a fixed ladder. Each rung is tried in order, and (new in this release) every candidate is verified against the live page before it is kept: the editor runs the selector and only accepts it when it matches exactly one element, and that element is the one you clicked.
data-avsb-target: A custom attribute reserved for A vs B testing. Add it to elements you plan to test, and the editor will always prefer it.id: The HTMLidattribute, when it is author-written (framework-minted ids likeradix-…are skipped) and unique on the page.data-test-id: The QA/test-tooling attribute family (data-test-id,data-testid,data-cy,data-qa). Stable enough to anchor experiments.data-*attributes: Every other stabledata-*attribute is tried until one verifies as unique. Attributes that repeat across elements (for exampledata-nimgon every image) no longer produce a selector on their own.- Accessible-name attributes:
aria-label,role+aria-label, ornameon form fields. A good anchor when markup has no test hooks but is accessible. - Class combination: A short list of stable class names that uniquely identify the element. Build-hash classes (
css-1abc234,jsx-392012, CSS-module hashes, and similar) are filtered out. When a hashed class still carries stable text around the hash (CSS modules produce names likeButton_root__AbC1d), the editor writes a pattern address from just the stable parts (button[class*="Button_root__"]), which keeps matching after the next build swaps the hash. - Structural path: A CSS path from a document-unique anchor down to the element (
#pricing > ul > li:nth-of-type(2)). The anchor itself must be unique on the page; the full path is verified before it is kept. - Text content: Matches a button or link by its visible text. Useful for stable copy that rarely changes.
- AI suggestion (last resort): When the best the ladder produced is still fragile or ambiguous, the editor asks AI for a better selector. See AI last-resort suggestions below.
The strongest candidates are saved together as a fallback chain: if the page changes and the first selector stops matching, the runtime falls back through the rest of the chain.
Qualification: when a good anchor isn't unique
If a normally-strong hook (an id, a test-id, a data-* attribute) matches more than one element, the editor doesn't discard it outright: it tries to qualify it, prefixing the nearest unique ancestor or appending :nth-of-type(), and keeps the qualified form only if that verifies as unique. Qualified selectors are demoted to MEDIUM confidence and the reason ("qualified: 3 matches for base selector") appears in the confidence pill's audit tooltip.
The confidence pill and the match count
When you pick an element, the Selector row in the sidebar shows the chain head in an editable field, with a confidence pill next to it:
- Green pill ("Strong"): Anchored to
data-avsb-target, a uniqueid, a test-id, or a stable uniquedata-*attribute. Safe to ship. - Amber pill ("Medium"): A class combination, accessible-name attribute, qualified anchor, or a verified AI suggestion. Usually fine, but a styling refactor can break it.
- Amber pill ("Structural" or "Text-only"): The selector relies on DOM position or visible text. Sibling reorders or copy edits will break it.
Hover the pill to see the per-candidate audit: each selector in the chain, plus a short reason explaining what could break it.
Live match count. The editor also measures the chain head against the current page continuously. If a saved selector now resolves to more than one element (or to none), an amber chip appears next to the pill ("3 matches" / "No matches") with an explanation in the hint line. The Test button does the same on demand for whatever you've typed: it reports exactly how many elements match and highlights the match on the canvas, and warns if the selector resolves to a different element than the current selection.
A warning is not a blocker: the editor will still save the change. But a multi-match head means the runtime may restyle the wrong element. Use Improve with AI, edit the selector by hand, or ask engineering for a data-avsb-target.
AI last-resort suggestions
When the deterministic ladder can't do better than a fragile selector, AI steps in, as an enhancement, never a gate:
- Automatic: After you select an element whose best selector is structural or text-only (or a structural path that had to be qualified or truncated), the editor quietly asks AI for a better selector in the background. Selection stays instant; the row shows "Asking AI for a more precise selector…" while the request runs.
- Manual: Whenever the match count isn't exactly 1 or confidence is low, an Improve with AI button appears on the Selector row.
The model receives the element's HTML, a summary of its ancestors, and the selectors already tried, and is instructed to prefer semantic and attribute anchors and to never use build-hash class names.
Verification is non-negotiable. A suggestion is applied only after the editor re-checks it against the live page and confirms it matches exactly the element you selected, once, and nothing else. An unverified suggestion is never saved. If the AI can't produce a valid selector (or the request fails, or you've hit the rate limit), the row says so: "AI suggestion unavailable", and your captured chain is kept untouched.
AI selector suggestions are part of the same organization-level AI copilot access as the Copilot panel (element HTML is sent to the model). If the copilot is not available for your organization, the editor skips the AI rung entirely.
Confidence levels
Every generated selector is tagged with one of four confidence levels. The badge appears next to the selector in the editor and is also reported in the variation's history.
- STRONG: Backed by
data-avsb-target, a unique author-writtenid, a test-id, or another stable uniquedata-*attribute. - MEDIUM: Class combination, accessible-name attribute, a qualified anchor, or a verified AI suggestion. Usually stable, but a rename or refactor can break it.
- STRUCTURAL: Walks the DOM from a unique anchor by tag and child index. Fragile to sibling reordering. The editor warns you when this is the best it can do, and asks AI for something better.
- TEXT_ONLY: Matches by visible text content. Will stop matching the moment the copy changes, including translation or a typo fix.
The data-avsb-target attribute
For any element you plan to test more than once, the most reliable approach is a dedicated data-avsb-target attribute. The editor checks for it first, and your developers can guarantee that page redesigns preserve it.
Tag the exact element you want to target. The editor only honours data-avsb-target when the attribute is on the element you click: it does not search the clicked element's ancestors. Tagging a wrapper <div> and clicking the button inside it will not anchor the button to the wrapper's tag; put the attribute on the button itself.
Here is the recommended pattern:
<h1 data-avsb-target="hero-headline"> Save 30% on your first order</h1><button data-avsb-target="primary-cta"> Get started</button>Use kebab-case identifiers that describe the role of the element, not its appearance: hero-headline, not large-red-text. The role outlives the design.
How selectors resolve at runtime
When a visitor is bucketed into your variation, the snippet runtime walks each change in the variation and resolves its selector against the live DOM. Changes saved with Apply to all similar elements apply to every element the selector matches; every other change targets a single element, and when a selector unexpectedly matches several, the runtime uses the rest of the saved chain to pick the one you actually edited. If nothing matches, the runtime skips that change. Changes are applied before any user-authored variation JavaScript or CSS, so you can rely on them being in place when your custom code runs.
Selecting multiple elements
Some edits should target every match, not just one (restyling every product card in a grid, for example). When the element you've selected shares a stable class with sibling elements of the same kind, the editor offers an Apply to all N similar elements toggle just below the selector field. Turning it on broadens the selector, highlights every match on the page, shows a live match count, and stages your style edits as a single change that covers all matches, including matching elements the page adds later. The editor warns you when the broadened selector matches more than 50 elements so you can check the set is intentional. See apply to all similar elements for the full behaviour.
Styling frameworks often emit class names that change every build, and the editor never writes the changing part into a selector. When stable text surrounds the hash (CSS modules produce names like Button_root__AbC1d, where Button_root is written by a developer and AbC1d is minted by the build), the editor keeps only the stable parts and writes a pattern address (button[class*="Button_root__"]) that still matches after the next build replaces the hash. Fully random names, such as styled-components' sc-bdVaJa or Emotion's css-1abc234, carry no stable text at all, so they are filtered out entirely, and the AI is instructed never to use them. If they're all an element has, expect a structural or text-only selector, and consider asking engineering for a data-avsb-target.