Skip to main content
BAP uses semantic selectors that describe elements by their purpose instead of their DOM position. They survive layout changes, dynamic content updates, and A/B tests.

Selector Types

TypeSyntaxExampleWhen to use
Stable ref@<ref>@submitBtnRefs returned by bap observe — most precise
Positional refe<N>e15From snapshot refs (playwright-cli compatible)
Rolerole:<role>:"<name>"role:button:"Submit"Interactive elements by ARIA role — most reliable
Texttext:"<content>"text:"Sign in"By visible text content
Labellabel:"<text>"label:"Email"Form inputs by associated label
Placeholderplaceholder:"<text>"placeholder:"Search..."Inputs by placeholder attribute
Test IDtestid:"<id>"testid:"submit-btn"By data-testid — most stable for apps that use them
CSScss:<selector>css:.btn-primaryCSS selector (fallback)
XPathxpath:<path>xpath://button[@type]XPath selector (fallback)
Coordinatescoords:<x>,<y>coords:100,200Click by pixel coordinates (last resort)

Priority and Recommendations

1

Start with stable refs from observe

Run bap observe and use the exact @ref values it returns. These are the most precise selectors because they map to a specific element in the registry.
bap observe
# Output: @ep44e3j  button  "Submit"
bap click @ep44e3j
2

Use semantic selectors when the target is obvious

Role selectors are the most reliable for interactive elements. Label selectors are best for form fields.
bap click role:button:"Submit"
bap fill label:"Email" "user@example.com"
3

Fall back to text selectors for links and content

bash bap click text:"Learn more" bap click text:"Next page"
4

Use CSS/XPath only when semantic selectors cannot match

bap click css:.custom-widget-btn
bap click xpath://div[@class='grid']//button[2]

Using Selectors in Commands

Single commands

bap click role:button:"Submit"
bap fill label:"Email" "user@example.com"
bap hover text:"Learn more"
bap click @ep44e3j
bap click e15

In composite actions (bap act)

The step syntax is action:selector=value for fill/type and action:selector for click/check/hover:
bap act fill:label:"Email"="user@example.com" \
        fill:label:"Password"="secret123" \
        click:role:button:"Sign in"

In the TypeScript SDK

import { role, text, label, testId } from "@browseragentprotocol/client";

await client.click(role("button", "Submit"));
await client.fill(label("Email"), "user@example.com");
await client.click(text("Sign in"));
await client.click(testId("submit-button"));

In the Python SDK

from browseragentprotocol import role, text, label, test_id

await client.click(role("button", "Submit"))
await client.fill(label("Email"), "user@example.com")
await client.click(text("Sign in"))
await client.click(test_id("submit-button"))

In MCP tools

selector: "role:button:Submit"
selector: "text:Sign in"
selector: "label:Email address"
selector: "testid:submit-button"
selector: "ref:@submitBtn"

Role Selector Examples

The role selector uses ARIA roles, which cover most interactive elements:
role:button:"Submit"         # Buttons
role:textbox:"Email"         # Text inputs
role:link:"Home"             # Links
role:heading:"Welcome"       # Headings
role:checkbox:"Remember me"  # Checkboxes
role:combobox:"Country"      # Dropdowns
role:searchbox:"Search"      # Search inputs
role:listitem:"Item 1"       # List items

Disambiguation

When multiple elements match a selector, BAP returns the first visible, interactive match. To disambiguate:
  1. Use a more specific role (e.g., role:button:"Submit" instead of text:"Submit")
  2. Run bap observe --max=100 to see all candidates and use the exact @ref
  3. Combine with CSS if needed: css:#login-form button[type=submit]
Identical elements (e.g., six “Add to cart” buttons) produce ambiguous text:Add to cart selectors. Use the specific @ref from bap observe or a CSS selector with an ID to target the right one.

Fallback Behavior

If a semantic selector does not match, BAP returns a clear error message with the closest matches found on the page, helping the agent self-correct.