Loopdocs

Concepts

In-app guides

Six things Loop can render inside your product: a checklist, a tooltip, a modal, a banner, an inline card, and a multi-step tour. You do not build any of them. You designate where they may appear, and Loop's SDK does the rest, inside a shadow root, so your CSS and ours can never reach each other.

The six types

Content is a discriminated union on type: a tooltip with no anchor and a checklist with no steps are both refused by the API rather than quietly rendered as banners.
TypeNeeds from your DOMContent it carries
checklistNothingtitle, 1–12 steps, sequential, progress strings
tooltipanchor: a CSS selectortitle, body, placement, optional CTA
modalNothingtitle, up to 10 paragraphs, CTA and secondary
bannerslot, if mode is pushtext up to 280 characters, edge, mode
inlineslot: a CSS selector, alwaystitle, body, appearance, cta_kind
tourA selector per step, or null for a centred stepversion, 2–12 steps, resume_window_days

What we touch in your page

  • One open shadow root per mount. Your CSS cannot match into it and ours cannot leak out. It is open, not closed, so your own tooling can still see us.
  • Anchors are selectors into a page we did not ship. The SDK treats a resolved anchor as a fact with a short shelf life. If your app has not painted the element yet, the guide waits, mounted nowhere and seen by nobody, and the wait is cancelled the moment the route changes.
  • A banner never moves your layout uninvited. The default, overlay, costs zero layout shift and accepts that it may cover edge content, so it is thin and always dismissible. Push shifts the page once, at mount, and is legal only into a slot you designated.
  • hideSurfaces() takes all of it back off: mounts, listeners, observers, timers.

Loop ships no copy

Every user-facing string, including the aria labels, travels on the wire as authored content. There is no default English Dismiss hiding in the bundle, because a default English Dismiss injected into a French product is our bug, not your oversight.

Interpolated strings are templates and the SDK substitutes rather than concatenates. Translations carry copy and never structure: a translation cannot change a step's completion rule, a tooltip's anchor, or which steps exist, because a French variant with different rules is not a translation, it is a second product that will be measured as if it were the first.

Targeting, triggers and caps

FieldWhat it does
segment_idWho is eligible. Null means everyone.
trigger.url_containsWhere it may show. Empty means any page. A substring, not an equality test: URLs carry query strings and ids nobody can predict.
frequencymax_views over window_days. One view per seven days is once a week. An absent max_views is uncapped. Enforced on the server, per request.
priority0–100. Which one wins when two are eligible at once.
themeauto or custom. brand_color is a hex string; an unreadable or absent one falls back to a neutral theme that still passes AA. Loop never invents a brand it cannot read.
experimentAn experiment_id and a variant_key, or null. Both halves or neither: an experiment_id with no arm is a surface that cannot say which side of the test it is on. The variant_key can never be holdout: the holdout is the people who see nothing.