Skip to content

Selectors & match strategy

A sightmap names parts of the UI. To do that, it has to point at them — somehow — in a way that survives across frameworks, refactors, and rendering modes. Sightmap uses CSS selectors. The choice is not arbitrary; the alternatives are worse.

CSS selectors are the one selection language every web framework and every browser-driving tool already understands. React, Vue, Svelte, Angular, server-rendered HTML, web components — all produce DOM, and DOM is queryable with CSS. Playwright, Puppeteer, every accessibility tree, every devtools console: CSS works.

The alternatives each fail one constraint:

  • Framework-specific component handles (<DatePicker> in React DevTools, Vue component instance IDs) require the curator and the consumer to agree on the framework.
  • XPath is universal but verbose and brittle.
  • Custom DOM mutations (injecting unique IDs at runtime) change the page the agent is observing — a non-starter for tools like Subtext that enrich live sessions.

CSS selectors are universal, non-mutating, and already in every agent’s toolkit. They are also imperfect — which is why the spec adds two things on top.

A selector field accepts either a single CSS string or an array of strings tried in order. First match wins.

- name: DepartureDatePicker
selector:
- '[data-component="DepartureDatePicker"]'
- '[data-picker="departure"]'
- '.flight-search .date-picker:first-child'

This is how sightmaps survive refactors. The first selector is the long-term ideal — a data-component attribute the team has agreed to maintain. The second is the legacy attribute the picker shipped with. The third is the structural fallback for the day someone removes both. The map keeps working through all three states; the curator drops the stale entries when they’re confident nothing relies on them.

For nested components, child selectors are evaluated within the parent’s matched subtree, not against the document root. Two unrelated card components can both contain a button.primary without colliding.

The recommended marker is a data-component="ComponentName" attribute on the root element of each component you want to name. It is:

  • Stable across refactors — the attribute moves with the component file.
  • Invisible to users and to most CSS — no styling implications.
  • Trivially queryable[data-component="DatePicker"].
  • Consistent with framework norms — React, Vue, and Svelte all pass data-* props through to the DOM.

The @sightmap/react codegen recognizes a related attribute, data-sightmap, when scanning source. Either works; the convention exists so a new project has a default to reach for.

The same priority rule applies to routes. When a URL is matched against views[].route, the first matching pattern wins. Same for requests[].route. List the more specific patterns first; let the general ones catch the rest. See Route matching in the schema for the glob syntax. The principle is the same in every match path: ordered alternatives, first hit wins, no scoring.