fission-onboarding 0.1.0

Guided in-app tours and spotlight onboarding for Fission applications
Documentation
# fission-onboarding

Help people learn your app by using your app. `fission-onboarding` places a
spotlight and a small guided callout over **real Fission widgets**—a button,
field, result, or any other retained widget. The rest of the screen stays in
place, so a first-run tour can guide an actual task instead of showing a stack
of slides.

The crate supplies validated, versioned flows, stable anchors, spotlight and
callout rendering, accessible controls, and host-defined styling. Your app
decides when a step is complete, where to navigate, what to say, and how to save
progress. It works with the Fission application model, not a browser-specific
DOM tour.

## Add it

```toml
[dependencies]
fission = "0.15.1"
fission-onboarding = "0.1"
```

## Smallest working tour

This complete example highlights a real button. Clicking that button—or
choosing **Finish** in the callout—ends the tour.

```rust
use fission::prelude::*;
use fission_onboarding::{OnboardingAnchor, OnboardingFlow, OnboardingHost, OnboardingStep};

#[derive(Clone, Debug, Default)]
struct State { finished: bool }
impl GlobalState for State {}

#[fission_reducer(FinishTour)]
fn finish_tour(state: &mut State) { state.finished = true; }

#[derive(Clone)]
struct App;
impl From<App> for Widget {
    fn from(_: App) -> Widget {
        let (ctx, view) = fission::build::current::<State>();
        let finish = ctx.bind(FinishTour, reduce_with!(finish_tour));
        let target = WidgetId::explicit("demo.create");
        let flow = OnboardingFlow::new("first-use", 1, vec![OnboardingStep {
            id: "create".into(),
            anchor: target,
            route: None,
            title: "Make your first item".into(),
            body: "Use this button to start. You can revisit the tour later.".into(),
        }]).expect("valid tour");

        let mut children = widgets![OnboardingAnchor {
            id: target,
            child: Button {
                child: Some(Text::new("Create item").into()),
                on_press: Some(finish.clone()),
                ..Default::default()
            }.into(),
        }];
        if !view.state().finished {
            children.push(OnboardingHost::new(
                "first-use", flow, 0, finish.clone(), finish,
            ).into());
        }
        Column { children, ..Default::default() }.into()
    }
}

fn main() {
    DesktopApp::<State, _>::new(App).run().expect("start app");
}
```

Run the checked-in copy:

```sh
cargo run --example minimal --features desktop-demo
```

## Guide a real task

For more than one step, create an `OnboardingFlow` with one stable
`WidgetId::explicit(...)` per target. Wrap the live target in
`OnboardingAnchor`, then render `OnboardingHost` alongside the normal app tree.
The host is presentation only; your reducers own transitions. This makes it
straightforward to require a real result before enabling Continue:

```rust
let mut host = OnboardingHost::new(
    "first-task", flow, state.step,
    ctx.bind(NextStep, reduce_with!(next_step)),
    ctx.bind(FinishTour, reduce_with!(finish_tour)),
);
host.continue_enabled = state.created_item.is_some();
host.on_skip = Some(ctx.bind(SkipTour, reduce_with!(skip_tour)));
host.labels.progress = format!("Step {} of 3", state.step + 1).into();
host.style = my_app_onboarding_style(&view.env().theme);
```

See [the complete guided-task example](https://docs.rs/crate/fission-onboarding/0.1.0/source/examples/guided_task.rs) for a runnable
three-step flow with a field, a gated task action, Back, and Skip:

```sh
cargo run --example guided_task --features desktop-demo
```

The optional `route` on a step is metadata for your app's navigation. If a
target is on another screen or below a scroll viewport, navigate and call
Fission's `ensure_visible` effect before displaying that step. The anchor must
belong to a widget in the active retained tree. For responsive layouts, give
each live branch its own stable anchor and choose the matching flow anchor.

## Save and resume progress

`fission-onboarding` intentionally does not create a second persistence system.
Keep the flow ID, version, current step ID, and completed/skipped state in your
app's normal store. With Fission Store, use a typed `StoreKey<T>` in an
`onboarding` namespace, hydrate it through a startup action, and persist each
transition from reducers. On a new flow version, choose whether to restart or
skip previously completed tours. Your app also decides whether Skip is offered
and where a user can restart the tour.

## What you control

| Concern | Owner |
| --- | --- |
| Spotlight, anchored callout, buttons, accessible dialog | This crate |
| Target widgets and stable IDs | Your app |
| Text, translation, colours, spacing | Your app |
| Step conditions, navigation, scrolling | Your app |
| Progress storage and restart policy | Your app |

The crate has no account service, analytics, network call, or product-specific
state. It depends on Fission and `thiserror` only. Licensed MIT.