# Ego-Lite Element Resolver Selector Formats: CSS, XPath, ARIA, and Text Syntax

> Explore ego-lite's element resolver supporting CSS, XPath, ARIA, text, and more. Efficiently target elements with diverse selector formats for robust automation.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: api-reference
- Published: 2026-08-23

---

**Ego-Lite's element resolver supports six distinct selector formats including raw CSS, XPath expressions, ARIA role queries, text-based lookups, hyperlink references, and numeric snapshot refs, all parsed in [`element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/element-resolver.ts) and resolved via Chrome DevTools Protocol.**

The [`element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/element-resolver.ts) module in the citrolabs/ego-lite repository serves as the central hub for DOM element location, transforming declarative selector strings into concrete CDP (Chrome DevTools Protocol) commands. Understanding the full range of **ego-lite element resolver selector formats** enables developers to write stable, resilient browser automation scripts that withstand DOM structural changes.

## Snapshot References (@)

The resolver handles persistent element references through the `parseRef` function (lines [70-104](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L70-L104)). When a selector starts with `@`, the parser extracts the numeric snapshot identifier and queries the internal ref-map (managed in [`ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/ref-map.ts)).

This mechanism stores the `backendNodeId` from previous DOM snapshots, allowing the resolver to:
- Locate elements across page navigations using stable backend node identifiers
- Resolve the correct CDP session when elements exist inside iframes
- Fall back to role/name lookups if the backend node ID fails

```typescript
// Click using a snapshot reference from a previous action
await click('@12');

```

## Locator Parsing Syntax

For non-reference selectors, `parseLocator` (lines [147-188](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L147-L188)) parses strings into structured locator objects. The parser recognizes specific prefixes that determine the resolution strategy.

### CSS Selectors

The `css:` prefix denotes standard CSS selectors. If no prefix is provided, the resolver falls back to CSS parsing (lines [212-217](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L212-L217)), optionally accepting an `nth` index for positional selection.

```typescript
// Explicit CSS prefix
await click('loc=css:.submit-button');

// Implicit CSS (equivalent behavior)
await click('.submit-button');

// CSS with positional index (zero-based)
await click('loc=css:.item;nth=2');

```

### XPath Expressions

XPath selectors use the `xpath:` prefix and are evaluated via `document.evaluate` in the page context. This supports complex traversal logic unavailable in standard CSS selectors.

```typescript
await click('loc=xpath://div[@class="container"]//button[contains(text(), "Submit")]');

```

### ARIA Role Selectors

The `role:` prefix triggers accessibility tree queries via `Accessibility.getFullAXTree`. These selectors support optional `name=` filters and `nth` indices (lines [201-211](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L201-L211)), providing resilient selection based on semantic roles rather than DOM structure.

```typescript
// Basic role selection
await click('role:button');

// Role with accessible name filter
await click('role:button[name="Close"]');

// nth occurrence of a role
await click('role:button;nth=1');

```

### Text-Based Selectors

Text matching uses `text:` or `text=` prefixes, with an optional `exact:` modifier for precise string matching. The resolver generates JavaScript evaluation code via `buildLocatorAllJs` (lines [610-632](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L610-L632)) and filters results through `textMatchJs` (line [696](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L696)).

```typescript
// Fuzzy text match
await click('text=Accept Terms');

// Exact text match
await click('text=exact:Submit');

// nth text occurrence (zero-based index)
await click('text=Next;nth=2');

```

### Attribute Selectors

Several aliases exist for common text-like attributes, all resolving to the generic text-matching kind:
- `label:` – Accessible label or associated label text
- `placeholder:` – Input placeholder attributes
- `alt:` – Image alternative text
- `title:` – Element title attributes
- `testid:` – data-testid attributes

```typescript
await click('label:Username');
await click('testid:login-button');

```

### Hyperlink Selectors

The `href:` prefix filters anchor elements by URL path or full URL, implemented in `hrefElementsJs` (line [997](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L997)). This matches against the `pathname` or complete `href` attribute.

```typescript
await click('href:/settings/account');
await click('href:https://example.com/dashboard');

```

## Resolution Implementation Strategies

After parsing, the resolver dispatches to specialized resolution functions based on the locator kind.

### Accessibility Tree Resolution

For role-based locators, `findBackendNodeIdByRoleName` (starting at line [496](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L496)) queries Chrome's accessibility tree. This approach remains stable even when DOM classes or structure change, as it relies on semantic ARIA information rather than markup details.

### DOM Evaluation

CSS, XPath, and href selectors execute in the page context via `Runtime.evaluate` CDP commands:
- CSS uses `queryAllExpression` (defined in [`locator-query.ts`](https://github.com/citrolabs/ego-lite/blob/main/locator-query.ts)) with normalized selectors
- XPath executes through `document.evaluate`
- Href selectors filter `<a>` elements by path matching

### Center and Object ID Extraction

Two primary resolution functions provide different output formats:
- `resolveElementCenter` (lines [63-146](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L63-L146)) returns `{x, y, sessionId}` coordinates using `DOM.getBoxModel` or `getBoundingClientRect()`
- `resolveElementObjectId` (lines [149-238](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L149-L238)) returns the CDP `objectId` via `DOM.resolveNode` for direct element manipulation

## Error Handling

Resolution failures wrap in `ElementResolutionError` with classification via `matchCountKind` (lines [46-50](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts#L46-L50)):
- **Transient errors**: Retryable conditions (e.g., element not yet attached)
- **Permanent errors**: Non-recoverable failures (e.g., selector matches zero elements after timeout)

## Summary

- **Six syntax families**: Snapshot refs, CSS, XPath, ARIA roles, text/attributes, and href selectors
- **Central parsing**: `parseLocator` in [`element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/element-resolver.ts) handles all string-to-object conversion
- **Dual resolution paths**: Accessibility tree queries for roles, DOM evaluation for CSS/XPath
- **iframe support**: Snapshot references (`@N`) resolve correct CDP sessions across frame boundaries
- **Stable selectors**: ARIA role-based queries resist DOM structural changes compared to CSS selectors

## Frequently Asked Questions

### What is the difference between `text:` and `text=` prefixes?

Both prefixes invoke the same text-matching logic in [`element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/element-resolver.ts). The colon and equals sign are interchangeable for text selectors, though `text=` often appears in documentation examples while `text:` maintains consistency with other prefix-based selectors like `css:` or `xpath:`.

### How does the resolver handle elements inside iframes?

When using snapshot references (`@N`), `parseRef` resolves the correct CDP session including iframe context before attempting backend node ID lookup. For standard locators, the resolver evaluates JavaScript in the appropriate execution context, though explicit frame targeting requires separate page context management.

### What happens when a selector matches multiple elements?

The resolver applies an `nth` index parameter (zero-based) to select specific occurrences. Without an explicit `nth` value, functions like `resolveElementCenter` typically return the first match or throw a transient error indicating multiple matches, depending on the calling helper's configuration.

### Can I combine multiple selector types in one query?

While the base syntax does not support compound selectors (e.g., combining CSS and text), the ARIA role selector accepts a `name=` filter that effectively combines role and text criteria. For complex combinations, chain multiple actions or use XPath expressions that can incorporate both structural and text conditions in a single query.