Supported Selector Formats in ego-lite: CSS, XPath, Text, and loc= Syntax

ego-lite supports six distinct selector formats—raw CSS, prefixed XPath (xpath=), text matching (text=), compact loc= expressions (css:, role:, href:), and snapshot references (@N)—all normalized by the element-resolver before execution.

The citrolabs/ego-lite repository provides a lightweight browser automation framework where agent commands rely on flexible string selectors to target DOM elements. Understanding the supported selector formats in ego-lite is critical for writing resilient automation scripts that handle dynamic web pages. These formats are parsed and validated by the format module, then resolved into executable queries by the element-resolver pipeline.

Raw CSS Selectors

Raw CSS selectors require no prefix and accept any valid CSS3-compliant selector string. The resolver passes these directly to the browser's native query engine.

// Select by ID and class combination
await click('#main .save-button');

// Select by attribute
await type('input[data-testid="email"]', 'user@example.com');

XPath Selectors (xpath=)

XPath expressions provide complex DOM traversal capabilities and must be prefixed with xpath=. This format is parsed in src/element-resolver.ts and converted to CDP runtime calls.

// Select button by text content via XPath
await click('xpath=//button[text()="Submit"]');

// Select by partial attribute match
await waitFor('xpath=//input[contains(@class, "required")]');

Text Matching (text=)

The text= prefix performs exact visible text matching, useful when semantic selectors are unavailable. The resolver normalizes whitespace during comparison.

// Click link containing exact text
await click('text=Forgot Password');

// Wait for specific heading text
await waitFor('text=Dashboard Loaded');

Compact loc= Syntax

The loc= prefix provides a unified DSL for specialized locator strategies, implemented in src/locator-query.ts. This syntax supports three primary namespaces:

  • loc=css: — Explicit CSS wrapper functionally identical to raw CSS selectors
  • loc=role: — ARIA role selection with optional accessible name filtering via curly braces {name}
  • loc=href: — Exact href attribute matching for anchor elements
// loc=css: equivalent to raw CSS
await click('loc=css:.nav-item.active');

// loc=role: with name filter (role:button{Submit})
await click('loc=role:button{Save Changes}');

// loc=href: targeting specific paths
await click('loc=href:/account/settings');

Snapshot References (@N)

Snapshot references allow agents to target elements captured in previous browser states. The parser recognizes numeric IDs prefixed with @ or the explicit @ref= syntax.

// Reference snapshot ID 42
await click('@42');

// Alternative explicit syntax
await waitFor('@ref=button42');

Internal Parsing Architecture

The selector resolution pipeline involves four critical source files that demonstrate how raw strings transform into executable browser commands:

  • src/format.ts — Declares the public API contract and TypeScript interfaces that define accepted selector patterns
  • src/element-resolver.ts — Contains the core normalization logic that distinguishes between CSS, XPath, text, and loc= prefixes before routing to specific handlers
  • src/locator-query.ts — Implements low-level conversion of loc= strings (role, href, css) into Chrome DevTools Protocol (CDP) runtime queries
  • src/helpers.ts — Provides utility functions that programmatically construct loc= strings for common patterns like loc=role:button{name}

Practical Usage Examples

// 1. Raw CSS - simplest form
await click('.primary-button');

// 2. XPath - complex structural queries
await type('xpath=//form[@id="login"]//input[@type="password"]', 'secret');

// 3. Text matching - semantic-free pages
await click('text=Accept Cookies');

// 4. loc=role - accessibility-first selection
await click('loc=role:checkbox{Subscribe to newsletter}');

// 5. loc=href - link verification
await click('loc=href:https://example.com/privacy');

// 6. Snapshot reference - stateful navigation
await click('@15');

Summary

  • Raw CSS selectors require no prefix and pass directly to the browser's query engine
  • XPath selectors use the xpath= prefix for complex DOM traversal expressions
  • Text selectors use the text= prefix for exact visible text matching
  • loc= syntax provides compact wrappers for CSS (loc=css:), ARIA roles (loc=role:{name}), and href attributes (loc=href:)
  • Snapshot references use @N or @ref= syntax to target previously captured elements
  • The element-resolver and format modules handle normalization, while locator-query.ts generates CDP commands

Frequently Asked Questions

What is the difference between raw CSS and loc=css: selectors?

Both target elements using CSS syntax, but loc=css: explicitly routes through the locator pipeline in src/locator-query.ts. Raw CSS selectors are passed directly to document.querySelector, while loc=css: strings undergo additional preprocessing and validation, making them preferred when building selectors programmatically via src/helpers.ts.

How do I select an element by its accessible role in ego-lite?

Use the loc=role: prefix followed by the ARIA role and optional name in curly braces. For example, loc=role:button{Submit} selects a button with the accessible name "Submit". This is processed by the role resolver in src/locator-query.ts which queries the browser's accessibility tree.

Can I combine multiple selector types in a single query?

No, ego-lite expects a single selector string per operation. To implement complex logic (e.g., "find element matching CSS or text"), execute multiple locator calls or use XPath which supports boolean logic via | operators or complex predicates within the xpath= string.

What happens if a snapshot reference like @42 points to a stale element?

If the referenced element no longer exists in the current DOM, the element-resolver in src/element-resolver.ts will return a "not found" error, typically throwing an exception that your agent code should catch. Snapshot references are bound to specific browser states and do not auto-update when the page changes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →