The problem
A new user opens your app. You want to show them how to create something and publish it. There are three usual answers, and each falls short:
- A video goes stale with the next interface change, and the viewer can’t try the step themselves halfway through.
- A row of tooltips says “click here” but never shows what happens next, so the viewer learns where the button is and not what it does.
- A scripted demo with fake clicks fires synthetic mouse and keyboard events. That can set off side effects the app never planned for, and the script can’t tell the viewer’s own click from its own.
And all three make handing control back awkward: a modal you have to close, or an “Exit the tour?” box to confirm.
The idea
Vetrina separates the show from the substance.
- The show is an overlay. The cursor, the captions and the gestures are drawn on a layer that can’t touch your app — it ignores the mouse entirely.
- The substance is your code. Every real change happens because a step calls a function you passed in, such as
save()oraddNote(). Vetrina never fakes a click, so it never does anything your app didn’t offer. - The first real input wins. A genuine click or keystroke ends the tour on the spot, and it lands on whatever the viewer aimed at. After that, the functions you passed in stop working for the tour, so it can never change your app again.
- A tour is a script of steps. Say a line, point at an element, type, click, run an action, wave or circle something. Write it as a chain, as a plain list of steps, or as an
asyncfunction when you need branches. - It can hand over and wait. A “your turn” step pauses the tour until the viewer does the expected thing themselves, with a timeout so a distracted viewer can’t stall it.
It is accessible by default: the caption is announced to screen readers, the Exit control is reachable by keyboard, and with reduced motion turned on the cursor stops sweeping but the pacing stays readable.
How to use it
Install the package:
npm install @laticent/vetrina
Vetrina publishes to npm with Lattice 1.0. Until then it lives in the Lattice repository at
docs/src/lib/vetrina. React is optional, needed only for@laticent/vetrina/react.
A complete tour over a title field and a Save button:
<input id="title" /> <button id="save">Save</button> <button id="demo">Show me</button>
<script type="module">
import { run, scene } from '@laticent/vetrina';
const title = document.querySelector('#title');
// Your app's real functions. The tour changes things only through these.
const actions = { save: () => alert(`Saved "${title.value}"`) };
const tour = scene()
.say('Give your deck a title…')
.point('#title').type('#title', 'Q4 Board Update')
.say('…then save it.')
.point('#save').click().act((a) => a.save()).check()
.hold(800)
.build();
document.querySelector('#demo').addEventListener('click', () =>
run({
root: document.body, actions, play: tour,
type: { set: (t) => { title.value = t; }, append: (t) => { title.value += t; } },
onStop: (reason) => console.log('tour ended:', reason), // complete, takeover, exit or error
}));
</script>
Notice that .click() only shows a click. The .act(...) that follows does the work, through your function.
| Call | What it gives you |
|---|---|
run({ root, actions, play }) | Plays a tour. Returns a handle with stop(). One tour at a time |
scene() | The chainable builder: say, point, type, click, act, gestures, hold, build |
storyboard(seed, steps) | The same tour as a plain array of steps |
ctx.awaitUser({ match, timeout }) | Wait for the viewer to do a step themselves |
useWalkthrough(ref, configure) | The React hook, from @laticent/vetrina/react |
theme and --vt-* CSS variables | Accent color, pointer, caption style, speed and motion |
Where else it fits
- Onboarding in any web app: watch it once, then do it yourself.
- Interactive tutorials and guided support, confined to one embedded widget if you like.
- Show-don’t-tell documentation: put a live component in a docs page and let the cursor demonstrate it.
- Kiosk and trade-show loops that repeat until a visitor touches the screen.
- Narrating what an assistant changed: each step waits for the real edit, so the cursor walks through the changes as they happen.
How Lattice uses it
In the Studio, Lattice’s editor in the browser, the first-run tour and the step-by-step lessons run on Vetrina’s React hook. In Present mode, the Guide option uses the same cursor and gestures to point at the part of the slide being narrated. That pointer is bundled into narrated HTML exports, so a deck sent as a file can still point at what it’s talking about.
What it doesn’t do
- It isn’t a test runner. It sends no synthetic events, so it can’t prove a button works. Your functions do the work.
- One cursor, one viewer, one tour. No multiple cursors, no syncing several viewers, no jumping into the middle of a live tour.
- It has no voice of its own. Narration is a plug-in point; bring your own text-to-speech.
- Web pages only. It draws a page-level overlay and doesn’t reach inside shadow DOM.