{"_id":"@apollovisionlabs/guide-mui","_rev":"6-8802493abb595d074da1b3196a18a8da","name":"@apollovisionlabs/guide-mui","dist-tags":{"latest":"0.4.1"},"versions":{"0.1.0":{"name":"@apollovisionlabs/guide-mui","version":"0.1.0","keywords":["react","onboarding","product-tour","walkthrough","mui","material-ui","accessibility"],"license":"MIT","_id":"@apollovisionlabs/guide-mui@0.1.0","maintainers":[{"name":"remydeme","email":"contact@apollovisionlabs.com"}],"homepage":"https://github.com/apollovisionlabs/guide#readme","bugs":{"url":"https://github.com/apollovisionlabs/guide/issues"},"dist":{"shasum":"f1a597d145e3c980027a012d50df0e6152f2b982","tarball":"https://registry.npmjs.org/@apollovisionlabs/guide-mui/-/guide-mui-0.1.0.tgz","fileCount":9,"integrity":"sha512-qHiPdvlZd1h1xdDvARvL10PTUJGqgXfNbXkAc7geUUtECOxH1Cw9Q8aQW66cjCAF12Usi0+j8Y0HRHhFVKAqEg==","signatures":[{"sig":"MEQCIBz1xPe2huo/w6Ka9Qlv+zz3JFdgsSUI47ZDWif7XRkcAiBTrtc0ycqRbrLsBuXhCx5nUIrU+eNzWimxq5ckkPfAvQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":64176},"main":"./dist/index.cjs","type":"module","_from":"file:apollovisionlabs-guide-mui-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"remydeme","email":"contact@apollovisionlabs.com"},"_resolved":"/private/var/folders/7q/5_vym4v57b55kmnn8xmw9zrm0000gn/T/8449c0781c84ffc2ad8d9a2c3e6bcd0a/apollovisionlabs-guide-mui-0.1.0.tgz","_integrity":"sha512-qHiPdvlZd1h1xdDvARvL10PTUJGqgXfNbXkAc7geUUtECOxH1Cw9Q8aQW66cjCAF12Usi0+j8Y0HRHhFVKAqEg==","repository":{"url":"git+https://github.com/apollovisionlabs/guide.git","type":"git"},"_npmVersion":"10.9.4","description":"`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the state machine, target resolution, routing, persistence and accessibility concerns, and exposes them as hooks with no rendering opinion. `@apollo","directories":{},"sideEffects":false,"_nodeVersion":"22.21.1","dependencies":{"@apollovisionlabs/guide-core":"0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@mui/material":"^7.3.9","@emotion/react":"^11.14.0","@emotion/styled":"^11.14.0"},"peerDependencies":{"react":"^19","@mui/material":"^7 || ^9","@emotion/react":"^11","@emotion/styled":"^11"},"_npmOperationalInternal":{"tmp":"tmp/guide-mui_0.1.0_1788366691787_0.8849612930825901","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@apollovisionlabs/guide-mui","version":"0.1.1","keywords":["react","onboarding","product-tour","walkthrough","mui","material-ui","accessibility"],"license":"MIT","_id":"@apollovisionlabs/guide-mui@0.1.1","maintainers":[{"name":"remydeme","email":"contact@apollovisionlabs.com"}],"homepage":"https://github.com/apollovisionlabs/guide#readme","bugs":{"url":"https://github.com/apollovisionlabs/guide/issues"},"dist":{"shasum":"4469bd07e5d5082afaeca3761499b598273204e5","tarball":"https://registry.npmjs.org/@apollovisionlabs/guide-mui/-/guide-mui-0.1.1.tgz","fileCount":9,"integrity":"sha512-O4T23z4dWFATZD/uHY1k3AVhSpzqErrp2if7fqFp8nAlMZzOlhdgHJNKgJVkj5t8GgFJgaMQd6hyVn1HPKp0sA==","signatures":[{"sig":"MEQCIGmA3AP9UcF4Jv6niJ9GlROHAQBsetzYFepRxpcs00YpAiACPEthrXnmSEb+Injmfdk5nwGML4CLLKEpSgvJ6UnE7w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@apollovisionlabs%2fguide-mui@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":64176},"main":"./dist/index.cjs","type":"module","_from":"file:/tmp/tmp.sepVeJEBFa/apollovisionlabs-guide-mui-0.1.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:17d8efb2-bbce-4a88-ae92-5e3e635644f7"}},"_resolved":"/tmp/tmp.sepVeJEBFa/apollovisionlabs-guide-mui-0.1.1.tgz","_integrity":"sha512-O4T23z4dWFATZD/uHY1k3AVhSpzqErrp2if7fqFp8nAlMZzOlhdgHJNKgJVkj5t8GgFJgaMQd6hyVn1HPKp0sA==","repository":{"url":"git+https://github.com/apollovisionlabs/guide.git","type":"git"},"_npmVersion":"12.0.2","description":"`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the state machine, target resolution, routing, persistence and accessibility concerns, and exposes them as hooks with no rendering opinion. `@apollo","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"@apollovisionlabs/guide-core":"0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@mui/material":"^7.3.9","@emotion/react":"^11.14.0","@emotion/styled":"^11.14.0"},"peerDependencies":{"react":"^19","@mui/material":"^7 || ^9","@emotion/react":"^11","@emotion/styled":"^11"},"_npmOperationalInternal":{"tmp":"tmp/guide-mui_0.1.1_1788385469060_0.8715916723398041","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@apollovisionlabs/guide-mui","version":"0.2.0","keywords":["react","onboarding","product-tour","walkthrough","mui","material-ui","accessibility"],"license":"MIT","_id":"@apollovisionlabs/guide-mui@0.2.0","maintainers":[{"name":"remydeme","email":"contact@apollovisionlabs.com"}],"homepage":"https://github.com/apollovisionlabs/guide#readme","bugs":{"url":"https://github.com/apollovisionlabs/guide/issues"},"dist":{"shasum":"18bbedb128e134bdd4fb26e18884d0c87266f5e1","tarball":"https://registry.npmjs.org/@apollovisionlabs/guide-mui/-/guide-mui-0.2.0.tgz","fileCount":9,"integrity":"sha512-mW1W2r7KuMDSeH3aQECoLn2x+91SetnzQI+pyvgMw9I8xW1m7Id0hzh6D+qggjNpOks3NCcHTTRRNHMY6eVPSw==","signatures":[{"sig":"MEUCIQCHhDihEwX290sieaZYuabQjgGBS+KY8fw3O3mXquv9rwIgeqj4dE9lOH8PGnPJshipeXTut8j6EMBzkLfhJC+3XF8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@apollovisionlabs%2fguide-mui@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":116892},"main":"./dist/index.cjs","type":"module","_from":"file:/tmp/tmp.SzRySVqL3K/apollovisionlabs-guide-mui-0.2.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:17d8efb2-bbce-4a88-ae92-5e3e635644f7"}},"_resolved":"/tmp/tmp.SzRySVqL3K/apollovisionlabs-guide-mui-0.2.0.tgz","_integrity":"sha512-mW1W2r7KuMDSeH3aQECoLn2x+91SetnzQI+pyvgMw9I8xW1m7Id0hzh6D+qggjNpOks3NCcHTTRRNHMY6eVPSw==","repository":{"url":"git+https://github.com/apollovisionlabs/guide.git","type":"git"},"_npmVersion":"12.0.2","description":"`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the state machine, target resolution, routing, persistence and accessibility concerns, and exposes them as hooks with no rendering opinion. `@apollo","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"@apollovisionlabs/guide-core":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@mui/material":"^7.3.9","@emotion/react":"^11.14.0","@emotion/styled":"^11.14.0"},"peerDependencies":{"react":"^19","@mui/material":"^7 || ^9","@emotion/react":"^11","@emotion/styled":"^11"},"_npmOperationalInternal":{"tmp":"tmp/guide-mui_0.2.0_1788452011860_0.3801323457301702","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@apollovisionlabs/guide-mui","version":"0.3.0","keywords":["react","onboarding","product-tour","walkthrough","mui","material-ui","accessibility"],"license":"MIT","_id":"@apollovisionlabs/guide-mui@0.3.0","maintainers":[{"name":"remydeme","email":"contact@apollovisionlabs.com"}],"homepage":"https://github.com/apollovisionlabs/guide#readme","bugs":{"url":"https://github.com/apollovisionlabs/guide/issues"},"dist":{"shasum":"6376dddd5c277f2531c57b25d7cc1900512a7cb2","tarball":"https://registry.npmjs.org/@apollovisionlabs/guide-mui/-/guide-mui-0.3.0.tgz","fileCount":9,"integrity":"sha512-O00NpbrDQqrl4nASo5GA1/ccZHgJFo8Fes5Rk6kJALLpRFB5MmZQx4lz4KZUw3f2vUUDx964BVYXQ/2w04MZyQ==","signatures":[{"sig":"MEQCIDxs4tiSd8GMgrJsjgXZrVrfotcxVXX2Q9eI4pRapl+gAiBRPZjZlSe7sER4+1WIu08d8LdUoY2OlM6piisTgde0Uw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@apollovisionlabs%2fguide-mui@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":123933},"main":"./dist/index.cjs","type":"module","_from":"file:/tmp/tmp.5Zd7bVdLzj/apollovisionlabs-guide-mui-0.3.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:17d8efb2-bbce-4a88-ae92-5e3e635644f7"}},"_resolved":"/tmp/tmp.5Zd7bVdLzj/apollovisionlabs-guide-mui-0.3.0.tgz","_integrity":"sha512-O00NpbrDQqrl4nASo5GA1/ccZHgJFo8Fes5Rk6kJALLpRFB5MmZQx4lz4KZUw3f2vUUDx964BVYXQ/2w04MZyQ==","repository":{"url":"git+https://github.com/apollovisionlabs/guide.git","type":"git"},"_npmVersion":"12.0.2","description":"`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the state machine, target resolution, routing, persistence and accessibility concerns, and exposes them as hooks with no rendering opinion. `@apollo","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"@apollovisionlabs/guide-core":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@mui/material":"^7.3.9","@emotion/react":"^11.14.0","@emotion/styled":"^11.14.0"},"peerDependencies":{"react":"^19","@mui/material":"^7 || ^9","@emotion/react":"^11","@emotion/styled":"^11"},"_npmOperationalInternal":{"tmp":"tmp/guide-mui_0.3.0_1788474022797_0.7766848782254041","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@apollovisionlabs/guide-mui","version":"0.4.0","keywords":["react","onboarding","product-tour","walkthrough","mui","material-ui","accessibility"],"license":"MIT","_id":"@apollovisionlabs/guide-mui@0.4.0","maintainers":[{"name":"remydeme","email":"contact@apollovisionlabs.com"}],"homepage":"https://github.com/apollovisionlabs/guide#readme","bugs":{"url":"https://github.com/apollovisionlabs/guide/issues"},"dist":{"shasum":"c0b396383855e2df13d22206a357745d0c8727d1","tarball":"https://registry.npmjs.org/@apollovisionlabs/guide-mui/-/guide-mui-0.4.0.tgz","fileCount":9,"integrity":"sha512-ioNGaH7c89mpdC7QF7tIfn8xAIV+56jFWUhja0Bob4ZUSMytJfZEVgxChZYdunhIObJE28gIKI0DuQ5Z5sZA/A==","signatures":[{"sig":"MEQCIAR/UyvWoz2U5AXItAhyCeGy4MYSN4XcSaGsgsb861B7AiAf9Pqz8VuZLPnCPG4+bC7Fkc61Xj5XjOIvxbfZa4lDbg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@apollovisionlabs%2fguide-mui@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190084},"main":"./dist/index.cjs","type":"module","_from":"file:/tmp/tmp.AX5JYP95U5/apollovisionlabs-guide-mui-0.4.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:17d8efb2-bbce-4a88-ae92-5e3e635644f7"}},"_resolved":"/tmp/tmp.AX5JYP95U5/apollovisionlabs-guide-mui-0.4.0.tgz","_integrity":"sha512-ioNGaH7c89mpdC7QF7tIfn8xAIV+56jFWUhja0Bob4ZUSMytJfZEVgxChZYdunhIObJE28gIKI0DuQ5Z5sZA/A==","repository":{"url":"git+https://github.com/apollovisionlabs/guide.git","type":"git"},"_npmVersion":"12.0.2","description":"`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the state machine, target resolution, routing, persistence and accessibility concerns, and exposes them as hooks with no rendering opinion. `@apollo","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","dependencies":{"@apollovisionlabs/guide-core":"0.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@mui/material":"^7.3.9","@emotion/react":"^11.14.0","@emotion/styled":"^11.14.0"},"peerDependencies":{"react":"^19","@mui/material":"^7 || ^9","@emotion/react":"^11","@emotion/styled":"^11"},"_npmOperationalInternal":{"tmp":"tmp/guide-mui_0.4.0_1788535601043_0.3633461041055033","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@apollovisionlabs/guide-mui","version":"0.4.1","type":"module","license":"MIT","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/apollovisionlabs/guide.git"},"keywords":["react","onboarding","product-tour","walkthrough","mui","material-ui","accessibility"],"dependencies":{"@apollovisionlabs/guide-core":"0.3.1"},"peerDependencies":{"@emotion/react":"^11","@emotion/styled":"^11","@mui/material":"^7 || ^9","react":"^19"},"devDependencies":{"@emotion/react":"^11.14.0","@emotion/styled":"^11.14.0","@mui/material":"^7.3.9"},"scripts":{"build":"tsup","test":"vitest run","typecheck":"tsc --noEmit"},"_id":"@apollovisionlabs/guide-mui@0.4.1","description":"`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the state machine, target resolution, routing, persistence and accessibility concerns, and exposes them as hooks with no rendering opinion. Two pack","bugs":{"url":"https://github.com/apollovisionlabs/guide/issues"},"homepage":"https://github.com/apollovisionlabs/guide#readme","_integrity":"sha512-sONTW8ixtiA5oXb0QnBEARitv+DhADajGBihZnJUSZknHEUBOlhxWQ0P0TJIHSDgKn4QUJExNkBNHYvA/pZILA==","_resolved":"/tmp/tmp.LCMMyDcYW4/apollovisionlabs-guide-mui-0.4.1.tgz","_from":"file:/tmp/tmp.LCMMyDcYW4/apollovisionlabs-guide-mui-0.4.1.tgz","_nodeVersion":"22.23.2","_npmVersion":"12.0.2","dist":{"integrity":"sha512-sONTW8ixtiA5oXb0QnBEARitv+DhADajGBihZnJUSZknHEUBOlhxWQ0P0TJIHSDgKn4QUJExNkBNHYvA/pZILA==","shasum":"6224955af0439107c466abec73bbcc1f30ef47de","tarball":"https://registry.npmjs.org/@apollovisionlabs/guide-mui/-/guide-mui-0.4.1.tgz","fileCount":9,"unpackedSize":190346,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@apollovisionlabs%2fguide-mui@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIE6I4lQRkscAFsAwgpHI7SvJ1WkDRrp4k45cUiCAW6CUAiEA5rNMRzuuXRMXWMrmhTAuQTK6uTw+NarXesLtN+pIVrc="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:17d8efb2-bbce-4a88-ae92-5e3e635644f7"}},"directories":{},"maintainers":[{"name":"remydeme","email":"contact@apollovisionlabs.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/guide-mui_0.4.1_1788636293409_0.5155596301389311"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-02T16:31:31.530Z","modified":"2026-09-05T19:24:53.911Z","0.1.0":"2026-09-02T16:31:31.931Z","0.1.1":"2026-09-02T21:44:29.203Z","0.2.0":"2026-09-03T16:13:32.029Z","0.3.0":"2026-09-03T22:20:22.935Z","0.4.0":"2026-09-04T15:26:41.177Z","0.4.1":"2026-09-05T19:24:53.548Z"},"bugs":{"url":"https://github.com/apollovisionlabs/guide/issues"},"license":"MIT","homepage":"https://github.com/apollovisionlabs/guide#readme","keywords":["react","onboarding","product-tour","walkthrough","mui","material-ui","accessibility"],"repository":{"type":"git","url":"git+https://github.com/apollovisionlabs/guide.git"},"description":"`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the state machine, target resolution, routing, persistence and accessibility concerns, and exposes them as hooks with no rendering opinion. Two pack","maintainers":[{"name":"remydeme","email":"contact@apollovisionlabs.com"}],"readme":"# guide\n\n`guide` is a headless React library for building in-app product tours. `@apollovisionlabs/guide-core` owns the\nstate machine, target resolution, routing, persistence and accessibility concerns, and exposes\nthem as hooks with no rendering opinion. Two packages render on top of those hooks out of the box:\n`@apollovisionlabs/guide-mui` with [MUI](https://mui.com) components (a spotlight overlay and a\npopover), and `@apollovisionlabs/guide-unstyled` with the same parts as plain DOM elements and an\noptional stylesheet. You can also render your own UI on top of `@apollovisionlabs/guide-core`\ndirectly.\n\nThis repository includes a runnable demo. From the repo root, run `pnpm --filter demo dev` and\nopen `http://localhost:5173` to try a three-page tour end to end.\n\n## Installation\n\n```bash\npnpm add @apollovisionlabs/guide-core @apollovisionlabs/guide-mui @mui/material @emotion/react @emotion/styled\n```\n\n`@apollovisionlabs/guide-mui` depends on `@apollovisionlabs/guide-core`, so both are needed to render the default UI. If you only\nwant the state machine and plan to render your own popover and spotlight, `@apollovisionlabs/guide-core` alone is\nenough.\n\n## Minimal example\n\n```tsx\nimport { GuideProvider, type Tour } from '@apollovisionlabs/guide-core'\nimport { GuideTour } from '@apollovisionlabs/guide-mui'\n\nconst tour: Tour = {\n  id: 'welcome',\n  steps: [\n    {\n      target: 'sidebar.projects',\n      title: 'Your projects',\n      body: 'Everything you create is grouped under a project.',\n    },\n  ],\n}\n\nfunction App() {\n  return (\n    <GuideProvider tours={[tour]}>\n      <Sidebar />\n      <GuideTour />\n    </GuideProvider>\n  )\n}\n\nfunction Sidebar() {\n  // The target string is matched against the data-guide attribute, not a CSS selector or a ref.\n  return <nav data-guide=\"sidebar.projects\">Projects</nav>\n}\n```\n\nStart the tour from anywhere under the provider with `useTour('welcome').start()`.\n\nDeclare tours as module constants, as above, rather than as literals built inside a component.\nThe provider compares step objects by identity, so a tour rebuilt on every render prevents the\nmissing-target policy below from ever firing.\n\n## `GuideTour` props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `labels` | `Partial<{ next, previous, finish, close, awaitingAction }>` | `{ next: 'Next', previous: 'Back', finish: 'Finish', close: 'Close', awaitingAction: 'Click the highlighted element to continue.' }` | The popover's button labels. Defaults are English; override any subset. See \"Translations\". |\n| `zIndex` | `number` | `theme.zIndex.modal` | Stacking level of the spotlight; the popover sits one above it. |\n| `padding` | `number` | `8` | Margin, in pixels, between the highlighted element and the edge of the spotlight hole. |\n| `radius` | `number` | `8` | Corner radius, in pixels, of the spotlight hole. |\n\n## `GuideProvider` props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `tours` | `Tour[]` | none | The tours available to `start()`. Tour ids must be unique. |\n| `children` | `ReactNode` | none | Your application. |\n| `navigate` | `(path: string) => void` | none | Called when a step declares a page it needs. Required for multi-page tours. |\n| `location` | `string` | none | The current pathname, used to decide whether a step's target should be on screen. Required for multi-page tours. |\n| `storage` | `GuideStorage` | none | Persists tour progress. See \"Persistence\". |\n| `translate` | `(key: string) => string` | none | Resolves `titleKey` / `bodyKey` on steps. See \"Translations\". |\n| `onEvent` | `(event: GuideEvent) => void` | none | Called for every lifecycle event. See \"Events\". |\n| `onMissingTarget` | `'skip' \\| 'wait' \\| 'error'` | `'wait'` | Default policy when a step's target never appears. Overridable per step. |\n| `targetTimeoutMs` | `number` | `5000` | How long to wait for a target before applying the missing-target policy. |\n\n## Multi-page tours\n\nA step can declare the route it belongs to and, when it isn't the current one, where to navigate:\n\n```tsx\n{\n  target: 'projects.create',\n  route: '/projects',\n  navigateTo: '/projects',\n  title: 'Create a project',\n  body: 'This step lives on another page, and you were moved here automatically.',\n}\n```\n\n`route` accepts `:param` segments and a trailing `*` wildcard, and is only used to decide whether\nthe current page already satisfies the step. `navigateTo` is the concrete path passed to\n`navigate`; when it is omitted and `route` is a literal path (no `:` or `*`), that route is used as\nthe destination.\n\n## Persistence\n\n`GuideStorage` is the two-method interface both `GuideProvider` and `ChecklistProvider` read from\nand write to. It is generic over the stored value, so one storage serves tour progress and\nchecklist progress under different keys:\n\n```ts\ninterface GuideStorage {\n  read<T>(key: string): Promise<T | null>\n  write<T>(key: string, value: T): Promise<void>\n}\n```\n\n`GuideProvider` reads and writes tour progress under `tour:<id>`. `ChecklistProvider` reads and\nwrites checklist progress under `checklist:<id>`. `HotspotProvider` reads and writes which\nhotspots have been opened under the single key `hotspots:seen`. See\n[ADR 0016](docs/adr/0016-one-storage-contract-for-tours-and-checklists.md) for why they share one\ninterface.\n\n`@apollovisionlabs/guide-core` ships `createMemoryStorage()` for tests and `createBrowserStorage(namespace?)` for\n`localStorage`. Neither talks to a server. An implementation backed by your own API looks like\nthis:\n\n```ts\nimport type { GuideStorage } from '@apollovisionlabs/guide-core'\n\nfunction createServerStorage(): GuideStorage {\n  return {\n    async read<T>(key: string) {\n      const response = await fetch(`/api/guide/${key}`)\n      if (!response.ok) return null\n      return (await response.json()) as T\n    },\n    async write<T>(key: string, value: T) {\n      await fetch(`/api/guide/${key}`, {\n        method: 'PUT',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify(value),\n      })\n    },\n  }\n}\n```\n\nPass it as the `storage` prop on either provider. `GuideProvider` reads on `start()` (unless an\nexplicit `from` step index is passed, or `resume: false` is passed) and writes whenever a running\ntour advances or completes. `ChecklistProvider` reads once on mount and writes whenever an item is\nticked, completed or the checklist is dismissed.\n\nA value read back from storage is validated before it is trusted (`isTourProgress`,\n`isChecklistProgress`, `isHotspotsProgress`, all exported from `@apollovisionlabs/guide-core`): a\nvalue that does not\nmatch the expected shape, from a hand-edited store or an older version of this library, is treated\nthe same as nothing stored, rather than crashing or resuming into a broken state.\n\n## Translations\n\nEvery step's own text is supplied by the consumer. A step can set `title` / `body` directly, or\n`titleKey` / `bodyKey` plus a `translate` function on `GuideProvider`; the key is passed through\nyour translation library and the result is displayed. When a key is set without a `translate`\nprop, the raw key is shown instead, so wiring `translate` is required for `titleKey` / `bodyKey`\nto resolve to real strings.\n\nThe popover's own chrome is the one exception: `@apollovisionlabs/guide-mui` ships English default labels\n(`Next`, `Back`, `Finish`, `Close`), so the buttons read correctly out of the box. Every one of\nthem is overridable through the `labels` prop on `GuideTour`: pass the labels in your language\nand nothing English remains:\n\n```tsx\n<GuideTour labels={{ next: 'Suivant', previous: 'Retour', finish: 'Terminer', close: 'Fermer' }} />\n```\n\n## Events\n\n`onEvent` on `GuideProvider`, `ChecklistProvider` and `HotspotProvider` each receive their own\nlifecycle events, as a discriminated union of `GuideEvent`:\n\n| Event | Payload | When |\n| --- | --- | --- |\n| `tour:start` | `{ tourId, stepIndex }` | `start()` is called. |\n| `tour:complete` | `{ tourId }` | `next()` is called on the last step. |\n| `tour:stop` | `{ tourId, stepIndex }` | The tour is stopped before completion. |\n| `step:show` | `{ tourId, stepIndex, target }` | A step's target is resolved and the step becomes visible. |\n| `target:missing` | `{ tourId, stepIndex, target }` | A step's target didn't appear within `targetTimeoutMs`. |\n| `checklist:item-complete` | `{ checklistId, itemId }` | An item is completed, by finishing its linked tour or by a manual tick. Not emitted for an item already complete. |\n| `checklist:complete` | `{ checklistId }` | The last incomplete item in a checklist is completed. Fires on every transition into the complete state, so unticking an item and reticking it emits a second time. Deduplicate downstream if you count completions. |\n| `checklist:dismiss` | `{ checklistId }` | `dismiss()` is called. |\n| `hotspot:show` | `{ hotspotId }` | A hotspot's marker is actually drawn on screen. Emitted once per hotspot per mount. |\n| `hotspot:open` | `{ hotspotId }` | The hotspot's bubble is opened, which also marks it seen. Not emitted again for a bubble that is already open. |\n\n## Accessibility\n\n- The current step position is announced through a visually-hidden `aria-live=\"polite\"` region,\n  so screen reader users hear \"2 / 4\" as the tour advances.\n- The step popover traps keyboard focus and is exposed as `role=\"dialog\"` with\n  `aria-labelledby` / `aria-describedby`, except for a step marked `interactive: true`, which\n  deliberately does **not** trap focus, so the user can tab or click past the popover to reach the\n  element they're asked to interact with.\n- `Escape` stops the tour, `ArrowRight` advances, `ArrowLeft` goes back. All three are ignored\n  while focus is in a text input, so typing isn't hijacked.\n- The highlighted element receives `aria-describedby`, pointing at the step's body text.\n- The spotlight respects `prefers-reduced-motion` and disables its transition when set.\n\n## Missing targets\n\nEach step is checked against its `target` (matched by a `data-guide` attribute) for up to\n`targetTimeoutMs` (default 5 seconds, per-provider). If the target never appears, the step's own\n`onMissingTarget` (or the provider's `onMissingTarget`, which defaults to `'wait'`) decides what\nhappens: `'skip'` moves to the next step, `'error'` stops the tour, and `'wait'` (the default)\npauses and resumes automatically if the target appears later, for instance after a slow async\nrender.\n\n## Advancing on an action\n\nA step can declare `advanceOn: 'click'` instead of ending on the popover's button:\n\n```ts\n{\n  target: 'project.share',\n  title: 'Share it',\n  body: 'Click the button yourself, this step is interactive.',\n  advanceOn: 'click',\n}\n```\n\nThe step advances when the user clicks the target, not the popover. `advanceOn` implies\n`interactive`: a step that waits for a click has to let the click through, so `GuideProvider`\nderives both `interactive` and `awaitsAction` on `ActiveStep` from `advanceOn`, rather than\nrequiring both to be set by hand. Read `activeStep.interactive` / `activeStep.awaitsAction`, not\n`step.interactive`, which is left `undefined` on a step that only sets `advanceOn`. See\n[ADR 0017](docs/adr/0017-advancing-on-an-action-implies-an-interactive-step.md).\n\n`@apollovisionlabs/guide-mui`'s popover reflects `awaitsAction`: no primary button, and the\n`awaitingAction` label in its place (see the `labels` row above). `ArrowRight` is ignored while a\nstep awaits its action, since letting it through would be a way around the very thing the step is\nasking for; `Escape` and `ArrowLeft` still work.\n\nThe click listener is attached, in the bubble phase, to the element resolved when the step\nopened, without `preventDefault` or `stopPropagation`, so your own click handler on the target\nstill runs.\n\nIf your application replaces that DOM node afterward, for instance by re-rendering a list, the\nlistener goes with it and the step stops advancing. Nothing notices: the target was found once,\nso the timeout was already cleared, no `target:missing` is emitted and no `wait`, `skip` or\n`error` policy runs. The tour simply sits on that step. The consequence is specific to\n`advanceOn`, even though the cause is not: an `advanceOn` step offers no primary button and\nignores `ArrowRight`, so a replaced node leaves the tour with `Escape` as its only exit. If the\nelement a step points at can be re-created under it, either give it a stable target that survives\nthe re-render or use an ordinary step with a Next button.\n\n## Checklist\n\nA checklist is a separate feature from the tour: a fixed list of items, each completed by\nfinishing a linked tour or by a manual tick. An item can also carry an `href`, which navigates\nand nothing more. `ChecklistProvider` holds\nits state the way `GuideProvider` holds tour state, and nests inside it so that an item can launch\na tour:\n\n```tsx\nimport { GuideProvider, ChecklistProvider, type Tour, type Checklist } from '@apollovisionlabs/guide-core'\nimport { GuideTour } from '@apollovisionlabs/guide-mui'\nimport { ChecklistLauncher } from '@apollovisionlabs/guide-mui'\n\nconst tour: Tour = {\n  id: 'welcome',\n  steps: [{ target: 'sidebar.projects', title: 'Your projects', body: 'Grouped under a project.' }],\n}\n\nconst onboarding: Checklist = {\n  id: 'onboarding',\n  items: [\n    { id: 'tour', title: 'Take the tour', body: 'Two minutes.', tourId: 'welcome' },\n    { id: 'projects', title: 'Open your projects', body: 'See the list.', href: '/projects' },\n    { id: 'profile', title: 'Set your name', body: 'Manual, ticked by hand.' },\n  ],\n}\n\nfunction App() {\n  return (\n    <GuideProvider tours={[tour]} navigate={(path) => router.push(path)}>\n      <ChecklistProvider checklists={[onboarding]} navigate={(path) => router.push(path)}>\n        <Sidebar />\n        <GuideTour />\n        <ChecklistLauncher checklistId=\"onboarding\" title=\"Get started\" />\n      </ChecklistProvider>\n    </GuideProvider>\n  )\n}\n```\n\nAn item with an `href`, or with neither `tourId` nor `href`, is completed by a manual tick only:\nactivating an `href` item navigates and stops there, since arriving on a page is not evidence that\nanyone did anything on it. An item with `tourId`\nis completed automatically when that tour is finished (`next()` called on its last step); it can\nalso be ticked by hand before that. Completion is idempotent: ticking an already-complete item, or\nfinishing a tour whose item is already ticked, does nothing and emits no event.\n\n### `ChecklistProvider` props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `checklists` | `Checklist[]` | none | The checklists available to `useChecklist`. |\n| `children` | `ReactNode` | none | Your application. |\n| `storage` | `GuideStorage` | none | Persists checklist progress under `checklist:<id>`. See \"Persistence\". |\n| `translate` | `(key: string) => string` | none | Resolves `titleKey` / `bodyKey` on items. See \"Translations\". |\n| `navigate` | `(path: string) => void` | none | Called when an item with `href` is activated. |\n| `onEvent` | `(event: GuideEvent) => void` | none | Called for `checklist:item-complete`, `checklist:complete`, `checklist:dismiss`. See \"Events\". |\n\n### `useChecklist(checklistId)`\n\nReturns `{ items, completedCount, total, isComplete, dismissed, restored, activate, toggle, complete, dismiss, reset }`.\n`items` is `ResolvedChecklistItem[]`: `{ id, title, body, completed, tourId?, href? }`, with\n`title` / `body` already resolved through `translate`. `activate(itemId)` runs an item's default\naction (start its tour, navigate to its `href`, or toggle it if it has neither); `toggle` and\n`complete` change completion directly; `dismiss()` and `reset()` act on the whole checklist.\n`restored` is whether this checklist's own initial read from storage has settled: `true`\nimmediately with no `storage` prop (there is nothing to wait for), and `true` once this\nchecklist's own read has resolved or rejected. It settles independently per checklist, so a\n`ChecklistProvider` holding several checklists never lets a slow or hung read for one hold\nanother one's `restored` false; each checklist's read runs concurrently with the others.\n\n### `Checklist` and `ChecklistLauncher` (`@apollovisionlabs/guide-mui`)\n\n`Checklist` renders the list inline: a progress bar, one row per item with a checkbox and a\ndismiss button. `ChecklistLauncher` wraps it behind a floating action button showing\n`completedCount/total`, opened as a popover. The popover stays open while items are ticked one\nafter another, and closes when an item hands off to something that needs the screen: launching a\ntour or navigating to an `href`. It does not close on a plain tick.\n\n```tsx\nimport { Checklist, ChecklistLauncher } from '@apollovisionlabs/guide-mui'\n\n<Checklist checklistId=\"onboarding\" title=\"Get started\" />\n// or, as a floating launcher:\n<ChecklistLauncher checklistId=\"onboarding\" title=\"Get started\" placement=\"bottom-right\" />\n```\n\nWith a `storage` prop configured on `ChecklistProvider`, both `Checklist` and `ChecklistLauncher`\nwait for their own checklist's restore to settle (`useChecklist(checklistId).restored`) before\ndrawing anything, rather than rendering their empty initial state (nothing completed, not\ndismissed) for one paint. The tradeoff: with a slow storage backend, a checklist now appears later\nthan it used to, instead of appearing at once and then jumping. A slow or broken read for one\nchecklist never holds a different checklist back; each restores on its own.\n\nDismissing the launcher removes it. Because the button and the popover disappear in the same\ncommit, there would be nothing left for the browser to put focus on, so the launcher leaves a\nshort off screen status message in its place, moves focus there, and removes that too as soon as\nfocus goes anywhere else. A keyboard user hears the dismissal confirmed instead of landing at the\ntop of the document.\n\n`Checklist` also takes `onDismiss`, called after the checklist is dismissed, and `onActivate`,\ncalled with the resolved item after any row is activated. `ChecklistLauncher` uses `onActivate`\nitself to close its popover; you need it only when you place `Checklist` inside a surface of your\nown that has to react the same way.\n\nNote one name collision if you import from both packages in the same file. `Checklist` is a type\nin `@apollovisionlabs/guide-core`, describing the list, and a component in\n`@apollovisionlabs/guide-mui`, rendering it. TypeScript will tell you, and an alias on the import\nsettles it:\n\n```tsx\nimport type { Checklist as ChecklistDefinition } from '@apollovisionlabs/guide-core'\nimport { Checklist } from '@apollovisionlabs/guide-mui'\n```\n\n## Hotspots\n\nA hotspot marks one element outside any tour: a small marker that opens a short explanation, and\noptionally a button that starts a tour. Unlike a tour step, a hotspot has no route and no order;\nit just sits at its target until opened. `HotspotProvider` nests inside `GuideProvider`, the same\nway `ChecklistProvider` does, so a hotspot naming a `tourId` can start it:\n\n```tsx\nimport {\n  GuideProvider,\n  HotspotProvider,\n  type Hotspot,\n  type Tour,\n} from '@apollovisionlabs/guide-core'\nimport { GuideTour, Hotspots } from '@apollovisionlabs/guide-mui'\n\nconst welcomeTour: Tour = {\n  id: 'welcome',\n  steps: [\n    { target: 'projects.create', title: 'Create a project', body: 'Start here.' },\n    { target: 'project.share', title: 'Share it', body: 'Send a link to your team.' },\n  ],\n}\n\nconst hotspots: Hotspot[] = [\n  {\n    id: 'create',\n    target: 'projects.create',\n    title: 'Start a project',\n    body: 'Everything else in here hangs off a project.',\n  },\n  {\n    id: 'share',\n    target: 'project.share',\n    title: 'Share a project',\n    body: 'Send a link to anyone on your team.',\n    // Named tours must exist on the GuideProvider above, or starting one warns and does nothing.\n    tourId: 'welcome',\n  },\n]\n\nfunction App() {\n  return (\n    <GuideProvider tours={[welcomeTour]}>\n      <HotspotProvider hotspots={hotspots}>\n        <YourApplication />\n        <GuideTour />\n        <Hotspots />\n      </HotspotProvider>\n    </GuideProvider>\n  )\n}\n```\n\nWithout a `GuideProvider` above it, starting a hotspot's tour warns once in the console and does\nnothing.\n\n### `HotspotProvider` props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `hotspots` | `Hotspot[]` | none | The hotspots to render. Ids must be unique. |\n| `children` | `ReactNode` | none | Your application. |\n| `storage` | `GuideStorage` | none | Persists which hotspots have been opened, under `hotspots:seen`. See \"Persistence\". |\n| `translate` | `(key: string) => string` | none | Resolves `titleKey` / `bodyKey` on hotspots. See \"Translations\". |\n| `onEvent` | `(event: GuideEvent) => void` | none | Called for `hotspot:show` and `hotspot:open`. See \"Events\". |\n\n### `useHotspots()`\n\nReturns `{ hotspots, restored, open, startTour, reset, notifyShown }`.\n\n- `hotspots` is `ResolvedHotspot[]`: every hotspot, each carrying its own `seen`, with `title` /\n  `body` already resolved through `translate`. It lists the seen ones too, rather than only the\n  unseen ones, because a renderer that keeps a marker mounted while its own bubble closes needs\n  the seen one as well; filtering to unseen-only is one line at the call site.\n- `restored` is whether the initial read from storage has settled: `true` immediately with no\n  `storage` prop (there is nothing to wait for), and `true` once the read resolves or rejects.\n  Wait for it before drawing any marker, or a hotspot already seen in storage can flash on screen\n  once before the restore lands.\n- `open(hotspotId)` marks a hotspot seen and emits `hotspot:open`.\n- `startTour(hotspotId)` starts the tour named by the hotspot's `tourId`, if it has one.\n- `reset()` clears the seen state for every hotspot.\n- `notifyShown(hotspotId)` is for renderers: call it once a marker is actually drawn on screen, so\n  `hotspot:show` fires once per hotspot per mount. `@apollovisionlabs/guide-mui`'s `Hotspots`\n  already calls it.\n\n### `Hotspots` (`@apollovisionlabs/guide-mui`)\n\nRenders a marker at each unseen hotspot's target; clicking it opens a bubble with the hotspot's\ntitle, body, and, when it names a `tourId`, a button that starts that tour.\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `labels` | `Partial<{ marker, startTour, close }>` | see below | Wording. `marker` is a function of the hotspot's title, not a fixed string, because word order around a name varies by language. |\n| `placement` | `Placement` | `'bottom'` | Where the bubble opens relative to the marker. Overridable per hotspot through `Hotspot.placement`. |\n| `zIndex` | `number` | `theme.zIndex.drawer + 1` | Stacking level of the marker; the bubble sits one above it. |\n\nDefault labels: `` { marker: (title) => `Show what is new: ${title}`, startTour: 'Show me', close: 'Close' } ``.\n\nNo marker is drawn while a tour is running or paused. A hotspot is an ambient hint and must not\ncompete with a guided flow the user is already in: a marker over the element a step points at\nwould take the click meant for that step, and one over a non-interactive step would be drawn\nbright and pulsing yet inert behind the spotlight. The markers come back when the tour ends,\nunchanged: this suppresses them, it does not mark them seen. `Hotspots` reads the tour state\nthrough context and tolerates its absence, so hotspots work with no `GuideProvider` in the tree.\n\n`paused` counts, because a paused tour is waiting for its target rather than finished. Note what\nthat implies at the edge: a tour paused on a target that never appears, the default `wait`\npolicy, draws nothing itself and now hides every hotspot too, for as long as it stays paused,\nwith `Escape` as the only way out and nothing on screen to suggest it. If your steps point at\ntargets that may never mount, prefer the `skip` or `error` missing-target policy over `wait`.\n\nThe default `zIndex` sits below `theme.zIndex.modal`, the level a running tour's spotlight uses,\nso a hotspot whose target lives inside your own modal dialog is covered by it. Raise `zIndex` on\n`Hotspots` to bring the marker above that dialog.\n\nA marker is drawn only for a target that has actual size on screen. An element that is in the DOM\nbut not rendered, `display: none` for instance, measures an all-zero rectangle; that draws no\nmarker and emits no `hotspot:show`, so a hotspot cannot be retired before the user has seen what\nit explains.\n\nClicking a marker whose bubble is already open closes the bubble, and emits no second\n`hotspot:open`.\n\nWith a `storage` prop configured on `HotspotProvider`, `Hotspots` waits for the initial restore to\nsettle (`useHotspots().restored`) before drawing any marker.\n\n## Compatibility\n\n| | Supported |\n| --- | --- |\n| React | 19 |\n| MUI (`@apollovisionlabs/guide-mui` only) | 7, 9 |\n| `@apollovisionlabs/guide-unstyled` | no UI toolkit; peers on `react` and `react-dom` only |\n| Rendering | ESM and CommonJS, with `\"use client\"` for Next's App Router |\n\n## Prior art\n\nThe spotlight-and-popover approach is inspired by [driver.js](https://driverjs.com) (MIT), as are\n[react-joyride](https://github.com/gilbarbara/react-joyride) (MIT) and\n[reactour](https://github.com/elrumordelaluz/reactour) (MIT). `guide` differs from all three mainly\nin splitting the state machine (`@apollovisionlabs/guide-core`) from rendering, which ships as two\npackages, `@apollovisionlabs/guide-mui` and `@apollovisionlabs/guide-unstyled`, so the same logic\nrenders through a design system or through plain DOM. Contributors must also read the licence discipline in\n`CONTRIBUTING.md` before looking at any other tour library.\n\n## Documentation\n\nThis file is the public API reference. Everything else lives in the repository:\n\n- [`ARCHITECTURE.md`](ARCHITECTURE.md): how the packages are layered and how the mechanisms work.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md): prerequisites, commands, conventions, licence discipline.\n- [`INFRA.md`](INFRA.md): build, continuous integration, and the state of the release path.\n- [`SECURITY.md`](SECURITY.md): reporting, supported versions, what the packages touch.\n- [`docs/index.md`](docs/index.md): the full documentation map: playbooks, decisions, references.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","readmeFilename":"README.md"}