Resolved: light

Teaching

In-product guidance, composed on the Surface primitive and the @phauna/ds overlays. TeachingPopover is an anchored coachmark; Tour strings those coachmarks into a guided walkthrough that highlights real on-page targets (and folds to bottom-sheet step cards on mobile); SetupGuide is a checklist-style setup panel. Everything is presentational — props + callbacks, no server actions. Each piece manages only its own ephemeral UI state (open/step/“don't show again”).

TeachingPopover
Coachmark over the @phauna/ds Popover (Base UI). trigger (the anchor), title, body (children), actions slot, top-right dismiss (X). Controlled (open/onOpenChange) or uncontrolled (defaultOpen). storageKey adds a “Don't show again” checkbox that persists to localStorage and keeps it suppressed. Client (touches localStorage + open state).
Tour
steps: { target, title, body, placement?, actions? }[] — target is a selector / element / getter. open / onOpenChange / onComplete drive it. Desktop: a fixed spotlight box over the target (ring + dim scrim) doubles as the coachmark anchor; mobile: bottom-sheet step cards. Each step is a fresh TeachingPopover (focus moves in); focus returns on exit; reposition + scroll respect reduced-motion. Client.
SetupGuide
Surface panel wrapping a vertical ProgressSteps run: items: { id, title, description?, status, action?, optional? }[] become rich task rows (numbered circles, completed checks, connectors) under a “x of n complete” progress bar. Server-safe (presentational; action slots are the caller's).
a11y
Popover focus move-in / focus-return is Base UI's; Tour also restores the pre-tour focus and announces the step counter via aria-live. SetupGuide's bar is a role=progressbar; step status comes from ProgressSteps' aria wiring. No color-only meaning (checks, labels, aria suffixes). Motion is reduced-motion aware.
presentational
No fetch, no server actions, no router. The parent owns every value and reacts to onOpenChange / onComplete / the action callbacks. All demos run on in-memory fixtures.

TeachingPopover

An anchored coachmark over the @phauna/ds Popover: a title, body copy, an actions slot, and a top-right dismiss. Use it controlled (open + onOpenChange) or uncontrolled (click the trigger, or defaultOpen to auto-show). Give it a storageKey and it grows a “Don't show again” checkbox that writes localStorage and keeps the coachmark suppressed across reloads — the third demo persists; reset it with the button beside it.

click-to-open · externally controlled · auto-show with “don't show again” (writes localStorage)
trigger / title / children / actions
trigger is the anchor node (rendered inline). title names the popup; children is the body; actions is the footer slot (e.g. a primary Button). The dismiss (X) shows by default — set dismissible={false} to drop it (Tour does, supplying its own nav).
open / defaultOpen / onOpenChange
Controlled via open; uncontrolled defaults to closed (the trigger opens it) or defaultOpen to auto-show. onOpenChange fires on open and on close; onDismiss fires on any close.
storageKey / dontShowAgainLabel
When storageKey is set, a “Don't show again” checkbox persists dismissal to localStorage[storageKey]; once stored the coachmark never opens again. dontShowAgainLabel overrides the control text.
side / align / sideOffset
Placement relative to the trigger — passed straight to the underlying Popover (default side bottom, align center, 10px offset).

Tour

A guided walkthrough that sequences TeachingPopovers across targeted steps. On desktop each step lays a fixed spotlight box over its target (a highlight ring plus a dim scrim that punches a hole around it) and anchors the coachmark there; on a narrow viewport the step becomes a bottom-sheet card instead. Back / Next / Done and Skip are built in, focus moves into each step and returns on exit, and the reposition + scroll-into-view honour reduced-motion. Click Start, then resize the window below the sm breakpoint to see the mobile shape.

Idle
Roster (128)
a 3-step product tour anchored to real on-page targets (resize below sm → bottom-sheet step cards)
steps[]
{ target, title, body, placement?, actions? }. target is a CSS selector, an Element, or a () => Element|null. placement sets the coachmark side. actions injects extra step-specific buttons left of the nav.
open / onOpenChange / onComplete
Controlled open; onOpenChange routes Skip / Esc / outside-click (and the final advance). onComplete fires once when the user advances past the last step. The step index resets each time it opens.
labels / spotlightPadding
labels overrides back / next / done / skip and the “Step i of n” status builder. spotlightPadding (px, default 6) pads the highlight ring around the target.
anchoring
Targets must be on-page when the step runs (it measures getBoundingClientRect and re-measures on scroll/resize). The spotlight is pointer-transparent, so the page underneath stays visible — it's a non-modal coachmark tour.

SetupGuide

A checklist-style guided setup panel: a Surface wrapping a vertical ProgressSteps run, where each step is a rich task row (title, description, an action slot) and a “x of n complete” bar summarizes progress. Finish the current step in the demo to watch the rail advance — the next task becomes current and the bar fills. Presentational: status is driven from props and every action is your own node.

Finish setting up

A few quick steps and you're ready to organize.

1 of 4 complete25%
  1. Complete your profile

    Name, photo, and how you'd like to help.

  2. Add your address

    So we can match you to nearby rescues and your representatives.

  3. Join a community

    Find your local team and the WhatsApp groups that fit.

  4. Set notification preferencesOptional

    Choose email, SMS, or in-app for alerts.

a live onboarding checklist — finish the current step to advance the rail + progress bar
items[]
{ id, title, description?, status, action?, optional? }. status is completed | current | pending (drives the circle, the check, and the screen-reader suffix). action is the per-row slot (a Button / link). optional tags the row without counting against completion.
title / description / footer
Panel heading + sub-line; footer is a hairline-divided slot (e.g. a “Skip for now” link). All optional.
showProgress
Toggles the summary progress bar (role=progressbar, valuemin/max/now). Default on; completion = count of status==='completed'.
composition
No bespoke stepper — it reuses ProgressSteps (vertical) for the numbered circles, checks, and connectors, and Surface for the panel. Server-safe; mark a parent 'use client' only for interactive action slots.