Anchors
Marking elements, and why not CSS selectors.
Declaring anchors
Every element a tour can point at lives in one registry. Flows reference the registry, never a raw string, so renaming a key breaks the flow file at compile time.
import { defineAnchors } from "@cairnkit/core";
export const anchors = defineAnchors({
questions: {
tabCreate: "questions.tab-create",
save: "questions.save",
},
});Applying them
One spread. Your components import nothing else from Cairn, and stay unaware they are part of a tour.
import { anchor } from "@cairnkit/core";
<button {...anchor(anchors.questions.save)}>Save</button>For config-driven UI such as a sidebar, put the id on the item and let the renderer forward it as data-cairn.
Components that swallow props
Some third-party components do not forward unknown props. <TourAnchor> wraps them without adding a box to the layout.
import { TourAnchor } from "@cairnkit/react";
<TourAnchor id={anchors.questions.save}>
<ThirdPartyButton />
</TourAnchor>How resolution works
- Matches are filtered to visible elements first.
- A missing anchor is waited for, via
MutationObserver, up towaitForMs. - If a resolved element later disappears, resolution restarts.
Why not CSS selectors
A selector like .btn-primary:nth-child(2) is a guess about structure that nothing enforces. Rename the class, reorder the DOM, and the tour breaks with no signal. A declared anchor is a contract: the compiler checks the reference, and CI checks the element still exists.