@nextsparkjs/plugin-walkme 0.1.0-beta.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.env.example +23 -0
  2. package/LICENSE +21 -0
  3. package/README.md +625 -0
  4. package/components/WalkmeBeacon.tsx +64 -0
  5. package/components/WalkmeControls.tsx +111 -0
  6. package/components/WalkmeModal.tsx +144 -0
  7. package/components/WalkmeOverlay.tsx +107 -0
  8. package/components/WalkmeProgress.tsx +53 -0
  9. package/components/WalkmeProvider.tsx +674 -0
  10. package/components/WalkmeSpotlight.tsx +188 -0
  11. package/components/WalkmeTooltip.tsx +152 -0
  12. package/examples/basic-tour.ts +38 -0
  13. package/examples/conditional-tour.ts +56 -0
  14. package/examples/cross-window-tour.ts +54 -0
  15. package/hooks/useTour.ts +52 -0
  16. package/hooks/useTourProgress.ts +38 -0
  17. package/hooks/useTourState.ts +146 -0
  18. package/hooks/useWalkme.ts +52 -0
  19. package/jest.config.cjs +27 -0
  20. package/lib/conditions.ts +113 -0
  21. package/lib/core.ts +323 -0
  22. package/lib/plugin-env.ts +87 -0
  23. package/lib/positioning.ts +172 -0
  24. package/lib/storage.ts +203 -0
  25. package/lib/targeting.ts +186 -0
  26. package/lib/triggers.ts +127 -0
  27. package/lib/validation.ts +122 -0
  28. package/messages/en.json +21 -0
  29. package/messages/es.json +21 -0
  30. package/package.json +18 -0
  31. package/plugin.config.ts +26 -0
  32. package/providers/walkme-context.ts +17 -0
  33. package/tests/lib/conditions.test.ts +172 -0
  34. package/tests/lib/core.test.ts +514 -0
  35. package/tests/lib/positioning.test.ts +43 -0
  36. package/tests/lib/storage.test.ts +232 -0
  37. package/tests/lib/targeting.test.ts +191 -0
  38. package/tests/lib/triggers.test.ts +198 -0
  39. package/tests/lib/validation.test.ts +249 -0
  40. package/tests/setup.ts +52 -0
  41. package/tests/tsconfig.json +32 -0
  42. package/tsconfig.json +47 -0
  43. package/types/walkme.types.ts +316 -0
package/.env.example ADDED
@@ -0,0 +1,23 @@
1
+ # ============================================
2
+ # WALKME PLUGIN CONFIGURATION
3
+ # ============================================
4
+ # Copy this file to .env
5
+ # Priority: Plugin .env > Root .env > Defaults
6
+
7
+ # Enable/disable the plugin
8
+ WALKME_ENABLED=true
9
+
10
+ # Debug mode (logs extra info to console)
11
+ WALKME_DEBUG=false
12
+
13
+ # Auto-start tours on first visit
14
+ WALKME_AUTO_START=true
15
+
16
+ # Delay before starting auto tours (ms)
17
+ WALKME_AUTO_START_DELAY=1000
18
+
19
+ # Persist state in localStorage
20
+ WALKME_PERSIST_STATE=true
21
+
22
+ # Analytics integration (emit events)
23
+ WALKME_ANALYTICS_ENABLED=false
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 NextSpark
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,625 @@
1
+ # @nextsparkjs/plugin-walkme
2
+
3
+ Guided tours and onboarding system for NextSpark applications. Supports declarative tour definitions, multiple step types (tooltip, modal, spotlight, beacon), cross-page navigation, conditional triggers, localStorage persistence, full keyboard accessibility, and i18n.
4
+
5
+ ## Installation
6
+
7
+ The plugin is available as an npm package or can be copied directly into your project.
8
+
9
+ ### npm (recommended)
10
+
11
+ ```bash
12
+ pnpm add @nextsparkjs/plugin-walkme
13
+ ```
14
+
15
+ ### Manual
16
+
17
+ Copy the `plugins/walkme/` directory to `contents/plugins/walkme/` in your NextSpark project.
18
+
19
+ ### Register the plugin
20
+
21
+ Add `'walkme'` to your theme configuration:
22
+
23
+ ```typescript
24
+ // contents/themes/<your-theme>/config/theme.config.ts
25
+ export const themeConfig: ThemeConfig = {
26
+ plugins: ['walkme'],
27
+ }
28
+ ```
29
+
30
+ Rebuild the registry:
31
+
32
+ ```bash
33
+ node core/scripts/build/registry.mjs
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ### 1. Define a tour
39
+
40
+ ```typescript
41
+ import type { Tour } from '@nextsparkjs/plugin-walkme/types/walkme.types'
42
+
43
+ const onboardingTour: Tour = {
44
+ id: 'getting-started',
45
+ name: 'Getting Started',
46
+ trigger: { type: 'onFirstVisit', delay: 1000 },
47
+ steps: [
48
+ {
49
+ id: 'welcome',
50
+ type: 'modal',
51
+ title: 'Welcome!',
52
+ content: 'Let us show you around the application.',
53
+ actions: ['next', 'skip'],
54
+ },
55
+ {
56
+ id: 'sidebar',
57
+ type: 'tooltip',
58
+ target: '[data-cy="sidebar-nav"]',
59
+ title: 'Navigation',
60
+ content: 'Use the sidebar to navigate between sections.',
61
+ position: 'right',
62
+ actions: ['next', 'prev', 'skip'],
63
+ },
64
+ {
65
+ id: 'create',
66
+ type: 'spotlight',
67
+ target: '[data-cy="create-button"]',
68
+ title: 'Create New Item',
69
+ content: 'Click here to create your first item.',
70
+ actions: ['complete', 'prev'],
71
+ },
72
+ ],
73
+ }
74
+ ```
75
+
76
+ ### 2. Wrap your app with the provider
77
+
78
+ ```tsx
79
+ import { WalkmeProvider } from '@nextsparkjs/plugin-walkme/components/WalkmeProvider'
80
+
81
+ export default function Layout({ children }: { children: React.ReactNode }) {
82
+ return (
83
+ <WalkmeProvider tours={[onboardingTour]} autoStart>
84
+ {children}
85
+ </WalkmeProvider>
86
+ )
87
+ }
88
+ ```
89
+
90
+ ### 3. (Optional) Control tours programmatically
91
+
92
+ ```tsx
93
+ import { useWalkme } from '@nextsparkjs/plugin-walkme/hooks/useWalkme'
94
+
95
+ function HelpButton() {
96
+ const { startTour, isActive } = useWalkme()
97
+
98
+ return (
99
+ <button onClick={() => startTour('getting-started')} disabled={isActive}>
100
+ Start Tour
101
+ </button>
102
+ )
103
+ }
104
+ ```
105
+
106
+ ## Configuration
107
+
108
+ ### Tour
109
+
110
+ | Property | Type | Required | Description |
111
+ |---|---|---|---|
112
+ | `id` | `string` | Yes | Unique identifier |
113
+ | `name` | `string` | Yes | Human-readable name |
114
+ | `description` | `string` | No | Tour description |
115
+ | `trigger` | `TourTrigger` | Yes | When and how the tour activates |
116
+ | `conditions` | `TourConditions` | No | Conditions that must be met to show the tour |
117
+ | `steps` | `TourStep[]` | Yes | Ordered list of steps (minimum 1) |
118
+ | `onComplete` | `() => void` | No | Callback on tour completion |
119
+ | `onSkip` | `() => void` | No | Callback on tour skip |
120
+ | `priority` | `number` | No | Auto-trigger ordering (lower = higher priority) |
121
+
122
+ ### TourStep
123
+
124
+ | Property | Type | Required | Description |
125
+ |---|---|---|---|
126
+ | `id` | `string` | Yes | Unique step identifier |
127
+ | `type` | `StepType` | Yes | `'tooltip'` \| `'modal'` \| `'spotlight'` \| `'beacon'` \| `'floating'` |
128
+ | `title` | `string` | Yes | Step title |
129
+ | `content` | `string` | Yes | Step body text |
130
+ | `target` | `string` | Conditional | CSS selector or `data-walkme-target` value. Required for tooltip, spotlight, beacon |
131
+ | `route` | `string` | No | Route path for cross-page steps |
132
+ | `position` | `StepPosition` | No | `'top'` \| `'bottom'` \| `'left'` \| `'right'` \| `'auto'`. Default: `'auto'` |
133
+ | `actions` | `StepAction[]` | Yes | Available actions: `'next'`, `'prev'`, `'skip'`, `'complete'`, `'close'` |
134
+ | `delay` | `number` | No | Delay in ms before showing |
135
+ | `autoAdvance` | `number` | No | Auto-advance after this many ms |
136
+ | `beforeShow` | `() => void` | No | Callback before the step renders |
137
+ | `afterShow` | `() => void` | No | Callback after the step renders |
138
+
139
+ ### Step Types
140
+
141
+ - **`modal`** - Centered overlay modal. No target element required. Use for welcome screens and informational messages.
142
+ - **`tooltip`** - Anchored tooltip positioned next to a target element. Requires `target`.
143
+ - **`spotlight`** - Overlay with a cutout around the target element plus a tooltip. Requires `target`.
144
+ - **`beacon`** - Pulsing indicator on a target element that expands on click. Requires `target`.
145
+ - **`floating`** - Same as modal. Alias for centered content without a target.
146
+
147
+ ### TourTrigger
148
+
149
+ | Property | Type | Required | Description |
150
+ |---|---|---|---|
151
+ | `type` | `TriggerType` | Yes | `'onFirstVisit'` \| `'onRouteEnter'` \| `'onEvent'` \| `'manual'` \| `'scheduled'` |
152
+ | `delay` | `number` | No | Delay in ms before activating |
153
+ | `route` | `string` | No | Route pattern for `onRouteEnter` (supports `*` and `**` wildcards) |
154
+ | `event` | `string` | No | Event name for `onEvent` trigger |
155
+ | `afterVisits` | `number` | No | Activate after N visits (for `scheduled`) |
156
+ | `afterDays` | `number` | No | Activate after N days since first visit (for `scheduled`) |
157
+
158
+ **Trigger types:**
159
+
160
+ - `onFirstVisit` - Fires on the user's first page visit (visitCount === 1).
161
+ - `onRouteEnter` - Fires when the user navigates to a matching route. Supports exact matches (`/dashboard`), wildcard (`/admin/*`), and glob (`/docs/**`).
162
+ - `onEvent` - Fires when a custom event is emitted via `emitEvent()`.
163
+ - `manual` - Never auto-triggers. Start programmatically with `startTour(tourId)`.
164
+ - `scheduled` - Fires after a number of visits (`afterVisits`) or days since first visit (`afterDays`).
165
+
166
+ ### TourConditions
167
+
168
+ All conditions use AND logic. Every specified condition must pass.
169
+
170
+ | Property | Type | Description |
171
+ |---|---|---|
172
+ | `userRole` | `string[]` | User must have one of these roles |
173
+ | `featureFlags` | `string[]` | All specified flags must be active |
174
+ | `completedTours` | `string[]` | All specified tours must be completed first |
175
+ | `notCompletedTours` | `string[]` | None of these tours should be completed |
176
+ | `custom` | `(ctx: ConditionContext) => boolean` | Custom condition function |
177
+
178
+ ## WalkmeProvider Props
179
+
180
+ | Prop | Type | Default | Description |
181
+ |---|---|---|---|
182
+ | `tours` | `Tour[]` | Required | Array of tour definitions (validated with Zod at runtime) |
183
+ | `children` | `ReactNode` | Required | Application content |
184
+ | `debug` | `boolean` | `false` | Enable debug logging to console |
185
+ | `autoStart` | `boolean` | `true` | Auto-start eligible tours based on triggers |
186
+ | `autoStartDelay` | `number` | `1000` | Default delay before auto-starting tours (ms) |
187
+ | `persistState` | `boolean` | `true` | Persist tour state in localStorage |
188
+ | `onTourStart` | `(event: TourEvent) => void` | - | Callback when a tour starts |
189
+ | `onTourComplete` | `(event: TourEvent) => void` | - | Callback when a tour completes |
190
+ | `onTourSkip` | `(event: TourEvent) => void` | - | Callback when a tour is skipped |
191
+ | `onStepChange` | `(event: TourEvent) => void` | - | Callback when the active step changes |
192
+ | `conditionContext` | `Partial<ConditionContext>` | - | External context for condition evaluation (userRole, featureFlags) |
193
+
194
+ ## Hooks
195
+
196
+ ### `useWalkme()`
197
+
198
+ Main hook for controlling tours. Must be used within a `<WalkmeProvider>`.
199
+
200
+ ```tsx
201
+ const {
202
+ // Tour control
203
+ startTour, // (tourId: string) => void
204
+ pauseTour, // () => void
205
+ resumeTour, // () => void
206
+ skipTour, // () => void
207
+ completeTour, // () => void
208
+ resetTour, // (tourId: string) => void
209
+ resetAllTours, // () => void
210
+
211
+ // Step navigation
212
+ nextStep, // () => void
213
+ prevStep, // () => void
214
+ goToStep, // (stepIndex: number) => void
215
+
216
+ // State
217
+ isActive, // boolean - whether any tour is active
218
+ activeTourId, // string | null
219
+ currentStepIndex, // number
220
+
221
+ // Queries
222
+ getActiveTour, // () => Tour | null
223
+ getActiveStep, // () => TourStep | null
224
+ isTourCompleted, // (tourId: string) => boolean
225
+ isTourActive, // (tourId: string) => boolean
226
+
227
+ // Events
228
+ emitEvent, // (eventName: string) => void
229
+ } = useWalkme()
230
+ ```
231
+
232
+ ### `useTour(tourId)`
233
+
234
+ Hook for tracking the state of a specific tour.
235
+
236
+ ```tsx
237
+ const {
238
+ tour, // Tour | null - full tour definition
239
+ isActive, // boolean
240
+ isCompleted, // boolean
241
+ isSkipped, // boolean
242
+ currentStep, // number (-1 if not active)
243
+ totalSteps, // number
244
+ progress, // number (0-100)
245
+ start, // () => void - start this tour
246
+ reset, // () => void - reset this tour
247
+ } = useTour('getting-started')
248
+ ```
249
+
250
+ ### `useTourProgress()`
251
+
252
+ Hook for tracking global completion progress across all tours.
253
+
254
+ ```tsx
255
+ const {
256
+ completedTours, // number
257
+ totalTours, // number
258
+ percentage, // number (0-100)
259
+ completedTourIds, // string[]
260
+ skippedTourIds, // string[]
261
+ remainingTours, // number
262
+ } = useTourProgress()
263
+ ```
264
+
265
+ ## Examples
266
+
267
+ ### Single-Page Tour
268
+
269
+ A basic onboarding flow on one page:
270
+
271
+ ```typescript
272
+ const basicTour: Tour = {
273
+ id: 'getting-started',
274
+ name: 'Getting Started',
275
+ trigger: { type: 'onFirstVisit', delay: 1000 },
276
+ steps: [
277
+ {
278
+ id: 'welcome',
279
+ type: 'modal',
280
+ title: 'Welcome!',
281
+ content: 'Let us show you around.',
282
+ actions: ['next', 'skip'],
283
+ },
284
+ {
285
+ id: 'sidebar',
286
+ type: 'tooltip',
287
+ target: '[data-cy="sidebar-nav"]',
288
+ title: 'Navigation',
289
+ content: 'Use the sidebar to navigate.',
290
+ position: 'right',
291
+ actions: ['next', 'prev', 'skip'],
292
+ },
293
+ {
294
+ id: 'create',
295
+ type: 'spotlight',
296
+ target: '[data-cy="create-button"]',
297
+ title: 'Create Item',
298
+ content: 'Click here to create your first item.',
299
+ actions: ['complete', 'prev'],
300
+ },
301
+ ],
302
+ }
303
+ ```
304
+
305
+ ### Cross-Page Tour
306
+
307
+ Navigate users between pages during a tour:
308
+
309
+ ```typescript
310
+ const crossPageTour: Tour = {
311
+ id: 'explore-app',
312
+ name: 'Explore the App',
313
+ trigger: { type: 'manual' },
314
+ conditions: { completedTours: ['getting-started'] },
315
+ steps: [
316
+ {
317
+ id: 'dashboard',
318
+ type: 'tooltip',
319
+ target: '[data-cy="dashboard-stats"]',
320
+ title: 'Your Stats',
321
+ content: 'Key metrics at a glance.',
322
+ position: 'bottom',
323
+ route: '/dashboard',
324
+ actions: ['next', 'skip'],
325
+ },
326
+ {
327
+ id: 'profile',
328
+ type: 'spotlight',
329
+ target: '[data-cy="profile-form"]',
330
+ title: 'Your Profile',
331
+ content: 'Complete your profile.',
332
+ route: '/settings/profile',
333
+ actions: ['complete', 'prev'],
334
+ },
335
+ ],
336
+ }
337
+ ```
338
+
339
+ When the tour reaches a step with a `route` that differs from the current page, the provider automatically navigates using `router.push()` and waits for the target element to appear.
340
+
341
+ ### Conditional Tour
342
+
343
+ Show tours only to specific user roles or when feature flags are active:
344
+
345
+ ```typescript
346
+ const adminTour: Tour = {
347
+ id: 'admin-features',
348
+ name: 'Admin Features',
349
+ priority: 10,
350
+ trigger: { type: 'onRouteEnter', route: '/admin/*', delay: 500 },
351
+ conditions: {
352
+ userRole: ['admin', 'superadmin'],
353
+ completedTours: ['getting-started'],
354
+ featureFlags: ['admin-panel-v2'],
355
+ },
356
+ steps: [
357
+ {
358
+ id: 'admin-welcome',
359
+ type: 'modal',
360
+ title: 'Admin Dashboard',
361
+ content: 'Here are the key admin features.',
362
+ actions: ['next', 'skip'],
363
+ },
364
+ // ...more steps
365
+ ],
366
+ }
367
+ ```
368
+
369
+ Pass the user context to the provider:
370
+
371
+ ```tsx
372
+ <WalkmeProvider
373
+ tours={[adminTour]}
374
+ conditionContext={{
375
+ userRole: currentUser.role,
376
+ featureFlags: activeFlags,
377
+ }}
378
+ >
379
+ <App />
380
+ </WalkmeProvider>
381
+ ```
382
+
383
+ ### Programmatic Tour Control
384
+
385
+ Start tours on demand and track completion:
386
+
387
+ ```tsx
388
+ function OnboardingDashboard() {
389
+ const { startTour } = useWalkme()
390
+ const { completedTours, totalTours, percentage } = useTourProgress()
391
+ const intro = useTour('getting-started')
392
+
393
+ return (
394
+ <div>
395
+ <h2>Onboarding Progress: {percentage}%</h2>
396
+ <p>{completedTours} of {totalTours} tours completed</p>
397
+
398
+ {!intro.isCompleted && (
399
+ <button onClick={intro.start}>Start Getting Started Tour</button>
400
+ )}
401
+
402
+ <button onClick={() => startTour('advanced-features')}>
403
+ Show Advanced Features
404
+ </button>
405
+ </div>
406
+ )
407
+ }
408
+ ```
409
+
410
+ ### Analytics Integration
411
+
412
+ Track tour events for analytics:
413
+
414
+ ```tsx
415
+ <WalkmeProvider
416
+ tours={tours}
417
+ onTourStart={(event) => {
418
+ analytics.track('tour_started', {
419
+ tourId: event.tourId,
420
+ tourName: event.tourName,
421
+ })
422
+ }}
423
+ onTourComplete={(event) => {
424
+ analytics.track('tour_completed', {
425
+ tourId: event.tourId,
426
+ totalSteps: event.totalSteps,
427
+ })
428
+ }}
429
+ onTourSkip={(event) => {
430
+ analytics.track('tour_skipped', {
431
+ tourId: event.tourId,
432
+ stepIndex: event.stepIndex,
433
+ })
434
+ }}
435
+ onStepChange={(event) => {
436
+ analytics.track('step_changed', {
437
+ tourId: event.tourId,
438
+ stepId: event.stepId,
439
+ stepIndex: event.stepIndex,
440
+ })
441
+ }}
442
+ >
443
+ ```
444
+
445
+ ## Customization
446
+
447
+ ### Element Targeting
448
+
449
+ Steps can target elements using:
450
+
451
+ 1. **CSS selectors** - `#my-id`, `.my-class`, `[data-cy="value"]`
452
+ 2. **data-walkme-target** - Add `data-walkme-target="name"` to any element, then reference as `target: "name"` (plain string without special CSS chars)
453
+ 3. **data-cy** - Standard test selectors: `target: '[data-cy="create-button"]'`
454
+
455
+ If the target element is not found within 5 seconds, the step renders without an anchor and a warning is logged in debug mode.
456
+
457
+ ### Internationalization
458
+
459
+ The plugin ships with English and Spanish translations under `plugins/walkme/messages/`. To add more languages, create a JSON file following the same structure:
460
+
461
+ ```json
462
+ {
463
+ "walkme": {
464
+ "next": "Next",
465
+ "prev": "Previous",
466
+ "skip": "Skip tour",
467
+ "complete": "Complete",
468
+ "close": "Close",
469
+ "progress": "Step {current} of {total}",
470
+ "tourAvailable": "Tour available",
471
+ "beaconLabel": "Click to start guided tour",
472
+ "modalTitle": "Guided Tour",
473
+ "tooltipLabel": "Tour step",
474
+ "spotlightLabel": "Highlighted element",
475
+ "keyboardHint": "Press Arrow Right for next step, Escape to skip",
476
+ "tourCompleted": "Tour completed!",
477
+ "tourSkipped": "Tour skipped",
478
+ "errorTargetNotFound": "Element not found, skipping step",
479
+ "resumeTour": "Resume tour",
480
+ "restartTour": "Restart tour"
481
+ }
482
+ }
483
+ ```
484
+
485
+ ### CSS Variables (Theming)
486
+
487
+ All walkme components use CSS custom properties for styling. Override them in your global CSS or scoped styles:
488
+
489
+ ```css
490
+ :root {
491
+ --walkme-bg: #ffffff;
492
+ --walkme-text: #111827;
493
+ --walkme-text-muted: #6b7280;
494
+ --walkme-primary: #3b82f6;
495
+ --walkme-border: #e5e7eb;
496
+ --walkme-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
497
+ --walkme-beacon-color: #3b82f6;
498
+ --walkme-overlay-color: rgba(0, 0, 0, 0.5);
499
+ }
500
+
501
+ /* Dark mode */
502
+ .dark {
503
+ --walkme-bg: #1f2937;
504
+ --walkme-text: #f9fafb;
505
+ --walkme-text-muted: #9ca3af;
506
+ --walkme-primary: #60a5fa;
507
+ --walkme-border: #374151;
508
+ --walkme-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
509
+ --walkme-beacon-color: #60a5fa;
510
+ --walkme-overlay-color: rgba(0, 0, 0, 0.7);
511
+ }
512
+ ```
513
+
514
+ ### Keyboard Navigation
515
+
516
+ When a tour is active:
517
+
518
+ | Key | Action |
519
+ |---|---|
520
+ | Arrow Right | Next step |
521
+ | Arrow Left | Previous step |
522
+ | Escape | Skip tour |
523
+ | Tab | Cycle focus within modals (focus trap) |
524
+
525
+ ### Accessibility
526
+
527
+ - All interactive elements have ARIA labels and roles
528
+ - Focus is trapped within modals
529
+ - Focus is restored to the previously focused element when a tour ends
530
+ - Progress bar uses `role="progressbar"` with `aria-valuenow`/`aria-valuemin`/`aria-valuemax`
531
+ - Keyboard navigation is fully supported
532
+
533
+ ## Environment Variables
534
+
535
+ | Variable | Default | Description |
536
+ |---|---|---|
537
+ | `WALKME_ENABLED` | `true` | Enable/disable the plugin |
538
+ | `WALKME_DEBUG` | `false` | Debug mode |
539
+ | `WALKME_AUTO_START` | `true` | Auto-start tours on first visit |
540
+ | `WALKME_AUTO_START_DELAY` | `1000` | Delay before auto-starting (ms) |
541
+ | `WALKME_PERSIST_STATE` | `true` | Persist state in localStorage |
542
+ | `WALKME_ANALYTICS_ENABLED` | `false` | Enable analytics event emission |
543
+
544
+ ## Troubleshooting
545
+
546
+ ### Tour does not start automatically
547
+
548
+ 1. Check that `autoStart` is `true` on the provider (default).
549
+ 2. Verify the trigger type matches the situation (e.g., `onFirstVisit` only fires when `visitCount === 1`).
550
+ 3. Check that all conditions are met (role, feature flags, completed tours).
551
+ 4. Open debug mode (`debug={true}`) to see console logs.
552
+ 5. If using `persistState`, the tour may already be marked as completed or skipped in localStorage. Call `resetTour(tourId)` or clear `walkme-state` from localStorage.
553
+
554
+ ### Target element not found
555
+
556
+ 1. Ensure the target selector is correct and the element exists in the DOM.
557
+ 2. For dynamically rendered elements, the plugin waits up to 5 seconds using MutationObserver + polling.
558
+ 3. Use `data-walkme-target="name"` for elements that are hard to select with CSS.
559
+ 4. Enable debug mode to see warnings about missing targets.
560
+
561
+ ### Cross-page navigation not working
562
+
563
+ 1. Verify the `route` property on each step matches the actual path.
564
+ 2. The provider uses `router.push()` from `next/navigation`. Ensure you are using the App Router.
565
+ 3. If navigation fails, the step is automatically skipped and the tour advances.
566
+
567
+ ### State not persisting
568
+
569
+ 1. Check that `persistState` is `true` (default).
570
+ 2. Verify localStorage is available (not in private browsing mode or incognito with storage disabled).
571
+ 3. The state is stored under the key `walkme-state` in localStorage.
572
+
573
+ ## File Structure
574
+
575
+ ```
576
+ plugins/walkme/
577
+ plugin.config.ts # Plugin configuration
578
+ package.json # Dependencies and metadata
579
+ .env.example # Environment variable template
580
+ types/
581
+ walkme.types.ts # All TypeScript type definitions
582
+ lib/
583
+ core.ts # Pure-function state machine (reducer + helpers)
584
+ validation.ts # Zod schemas for tour config validation
585
+ storage.ts # localStorage persistence adapter
586
+ targeting.ts # DOM element targeting (CSS, data-walkme, data-cy)
587
+ positioning.ts # @floating-ui/react wrapper for smart positioning
588
+ triggers.ts # Tour trigger evaluation
589
+ conditions.ts # Tour condition evaluation (AND logic)
590
+ plugin-env.ts # Environment variable loader
591
+ hooks/
592
+ useWalkme.ts # Main public hook (tour control + navigation)
593
+ useTour.ts # Per-tour state hook
594
+ useTourProgress.ts # Global completion progress hook
595
+ useTourState.ts # Internal: state machine + localStorage sync
596
+ providers/
597
+ walkme-context.ts # React Context definition
598
+ components/
599
+ WalkmeProvider.tsx # Main provider (state, triggers, rendering)
600
+ WalkmeOverlay.tsx # Semi-transparent backdrop overlay
601
+ WalkmeTooltip.tsx # Positioned tooltip step
602
+ WalkmeModal.tsx # Centered modal step with focus trap
603
+ WalkmeSpotlight.tsx # Overlay with cutout + tooltip
604
+ WalkmeBeacon.tsx # Pulsing indicator
605
+ WalkmeProgress.tsx # Progress bar
606
+ WalkmeControls.tsx # Navigation buttons (Next/Prev/Skip/Complete)
607
+ messages/
608
+ en.json # English translations
609
+ es.json # Spanish translations
610
+ examples/
611
+ basic-tour.ts # Single-page tour example
612
+ cross-window-tour.ts # Multi-page tour example
613
+ conditional-tour.ts # Role-based conditional tour example
614
+ tests/
615
+ setup.ts # Jest test setup
616
+ tsconfig.json # Test-specific TypeScript config
617
+ lib/ # Unit tests for all lib modules (165 tests)
618
+ jest.config.cjs # Jest configuration
619
+ ```
620
+
621
+ ## Dependencies
622
+
623
+ - **`@floating-ui/react`** ^0.27.0 - Smart positioning for tooltips and popovers
624
+
625
+ Peer dependencies (provided by the host project): `react`, `react-dom`, `next`, `zod`, `next-intl`, `lucide-react`, `class-variance-authority`, `clsx`, `tailwind-merge`.