@inkly-org/interactive-demo
v0.2.1
Published
React player and schema for interactive product demos.
Readme
@inkly-org/interactive-demo
The React player and the demo schema for interactive product demos.
A demo is a demo.config.json file: an ordered list of steps, each a
screenshot (image) or a screen recording (video) with hotspots,
captions and an optional voiceover, plus cover screens made of widgets.
The schema is defined with Zod and exported from ./schema.
Install
npm install @inkly-org/interactive-demo react react-domUse as a React component
import { Demo } from '@inkly-org/interactive-demo';
import '@inkly-org/interactive-demo/styles.css';
export function ProductTour() {
return <Demo src="/demos/onboarding/" />;
}src is either the demo folder's URL (the config is fetched from it and
media resolves against it) or the config object itself (an imported
demo.config.json; pass baseUrl for the folder its media lives in).
Demo validates the config with DemoSchema and renders the player. Props
of note: baseUrl, resolveAssetUrl (a custom rule for relative media
paths, e.g. a CDN), themeTokens, onEvent (step views,
completion, CTA clicks, form submits), layout and controls.
DemoModal opens the same player in a pop-up over your page, rendered
in-process through a portal: <DemoModal open={open} onClose={…}><Demo … /></DemoModal>.
A page that isn't React opens a demo page in a pop-up with the loader
instead: @inkly-org/interactive-demo/embed.js (the CLI's build copies it
to dist/embed.js) adds InteractiveDemo.open(url).
The runtime reference has every prop, the events and the page contract.
Use the self-contained player on a static page
player.js bundles React and the player. A page mounts a demo with one
JSON script tag and a root element:
<link rel="stylesheet" href="./player.css" />
<script id="demo-config" type="application/json">{ /* demo.config.json */ }</script>
<div id="root"></div>
<script src="./player.js"></script>Media paths in the config (assets/<file>) resolve relative to the page.
The CLI's build command writes exactly this layout.
The page's #root gets the demo-level canvas background (background /
backgroundColor in the config), and two query parameters are honoured:
?autoplay=1 starts playback as soon as the player is ready, and
?render=1 (for an exporter driving the page) implies it.
Once mounted, the player exposes a small contract on window.__demo so a
host page or exporter can detect readiness, drive the player and wait for
the end:
window.__demo = {
ready: true, // set when the player has parsed the config and mounted
complete: false, // flips to true when the last step finishes
stepIds: string[], // every step id, in order
controls, // play(), pause(), next(), prev(), seekToStep(id), …
demo, // the validated config
};Schema
import { DemoSchema, parseDemo } from '@inkly-org/interactive-demo/schema';DEMO_CONFIG_SCHEMA_URL is the $schema URL for editor autocompletion.
Theme
import { resolveDemoTheme } from '@inkly-org/interactive-demo/themes';There is one theme: indigo accent, dotted canvas, serif display headings.
Customise it with four tokens — primary, secondary, font, radius —
through themeTokens or a demo's theme.tokens. resolveDemoTheme().css
is the theme's scoped CSS for a React host to inject. Theme presets were
removed: theme.preset and the themeId prop are accepted and ignored.
Fonts (optional)
The stylesheet never loads fonts. The UI sans (Inter) and the mono (Geist Mono) fall back to the system fonts unless you opt in. The default theme's heading stack names "Waldenburg" first; it is not shipped (proprietary) and falls back to Inter:
import "@inkly-org/interactive-demo/styles.css";
import "@inkly-org/interactive-demo/fonts.css"; // self-hosted, no networkfonts.css ships two @font-face rules (Inter and Geist Mono, variable
weights) pointing at dist/fonts/*.woff2 from the fontsource packages, SIL
Open Font License. The CLI's dev and
build include it automatically as player-fonts.css next to player.css.
