vue-tour-kit
v1.0.1
Published
A modern, production-ready guided tour library for Vue 3
Maintainers
Readme
Vue Guided Tour
A modern, production-ready guided tour library for Vue 3 applications. Built with TypeScript, following SOLID principles and clean architecture patterns.
Table of Contents
- Installation
- Quick Start
- Core Concepts
- API Reference
- Configuration Options
- Tour Steps
- Hooks and Events
- Customization
- Theming
- Slots
- CSS Variables
- Keyboard Navigation
- Accessibility
- Architecture
- TypeScript Types
Installation
# npm
npm install vue-tour-kit
# yarn
yarn add vue-tour-kit
# pnpm
pnpm add vue-tour-kitImport the library and its styles in your main entry file:
import { createTour } from 'vue-tour-kit'
import 'vue-tour-kit/dist/style.css'Quick Start
Basic Example
<script setup lang="ts">
import { ref } from 'vue'
import { createTour } from 'vue-tour-kit'
import type { TourStep } from 'vue-tour-kit'
import TourRoot from 'vue-tour-kit/components/TourRoot.vue'
import 'vue-tour-kit/dist/style.css'
const tourController = ref<ReturnType<typeof createTour> | null>(null)
const showTour = ref(false)
const steps: TourStep[] = [
{
target: '#welcome-button',
title: 'Welcome',
content: 'This is your first step in the tour.',
position: 'bottom'
},
{
target: '#settings-menu',
title: 'Settings',
content: 'Access your application settings here.',
position: 'left'
},
{
target: '#help-section',
title: 'Need Help?',
content: 'Find documentation and support resources.',
position: 'top'
}
]
function startTour() {
tourController.value = createTour({
steps,
options: {
overlayOpacity: 0.75,
showProgress: true,
keyboardNavigation: true
},
hooks: {
onStart: () => {
showTour.value = true
},
onFinish: () => {
showTour.value = false
},
onSkip: () => {
showTour.value = false
}
}
})
tourController.value.start()
}
</script>
<template>
<button @click="startTour">Start Tour</button>
<TourRoot
v-if="showTour && tourController"
:state="tourController.state"
:options="tourController.getOptions()"
:current-step="tourController.getCurrentStep()"
:position="calculatedPosition"
:target-rect="tourController.state.targetRect"
:can-go-next="tourController.canGoNext()"
:can-go-prev="tourController.canGoPrev()"
@next="tourController.next()"
@prev="tourController.prev()"
@close="tourController.stop()"
/>
</template>Core Concepts
Tour Controller
The createTour function returns a controller object that manages the entire tour lifecycle. The controller provides methods for navigation, state access, and tour control.
const controller = createTour({
steps: TourStep[],
options?: TourOptions,
hooks?: TourHooks
})Controller Methods
| Method | Return Type | Description |
|--------|-------------|-------------|
| start() | void | Begins the tour at step 0 |
| stop() | void | Ends the tour and cleans up resources |
| next() | Promise<void> | Advances to the next step |
| prev() | Promise<void> | Returns to the previous step |
| goTo(index) | Promise<void> | Jumps to a specific step by index |
| canGoNext() | boolean | Returns true if not on the last step |
| canGoPrev() | boolean | Returns true if not on the first step |
| getCurrentStep() | TourStep \| null | Returns the current step object |
| getOptions() | RequiredTourOptions | Returns merged options with defaults |
| getPositionCalculator() | PositionCalculator | Returns the positioning system instance |
| destroy() | void | Cleans up all resources and listeners |
Controller State
The controller exposes a reactive state object:
interface TourState {
isActive: boolean // Whether the tour is currently running
currentStep: number // Zero-based index of current step
totalSteps: number // Total number of steps in the tour
status: TourStatus // 'idle' | 'running' | 'paused' | 'completed'
targetElement: HTMLElement | null // The current target DOM element
targetRect: DOMRect | null // Bounding rectangle of target element
}API Reference
createTour Function
function createTour(config: {
steps: TourStep[]
options?: TourOptions
hooks?: TourHooks
}): TourControllerParameters:
steps(required): Array of tour step definitionsoptions(optional): Configuration options for tour behaviorhooks(optional): Callback functions for tour events
Returns: A TourController instance with full control over the tour.
Configuration Options
TourOptions Interface
interface TourOptions {
overlayOpacity?: number // Default: 0.75
zIndex?: number // Default: 9999
scrollBehavior?: ScrollBehavior // Default: 'smooth'
scrollOffset?: number // Default: 20
keyboardNavigation?: boolean // Default: true
closeOnOverlayClick?: boolean // Default: true
showProgress?: boolean // Default: true
debug?: boolean // Default: false
}Option Details
overlayOpacity
- Type:
number - Default:
0.75 - Range:
0to1 - Description: Opacity of the dark overlay that covers the page. A value of 0 makes it fully transparent, 1 makes it fully opaque.
zIndex
- Type:
number - Default:
9999 - Description: The z-index of the tour elements. Should be higher than any other elements in your application.
scrollBehavior
- Type:
'smooth' | 'instant' | 'auto' - Default:
'smooth' - Description: How the page scrolls when moving to a step target that is outside the viewport.
scrollOffset
- Type:
number - Default:
20 - Description: Extra pixels of padding when scrolling an element into view.
keyboardNavigation
- Type:
boolean - Default:
true - Description: Enables keyboard controls. Arrow keys navigate between steps, Escape closes the tour.
closeOnOverlayClick
- Type:
boolean - Default:
true - Description: Whether clicking on the overlay (outside the tooltip) closes the tour.
showProgress
- Type:
boolean - Default:
true - Description: Shows a progress indicator (dots, bar, or text) in the tooltip footer.
debug
- Type:
boolean - Default:
false - Description: Enables debug logging to the console.
Tour Steps
TourStep Interface
interface TourStep {
target: string | HTMLElement | (() => HTMLElement)
title?: string
content: string
position?: Position
highlightPadding?: number
disableInteraction?: boolean
beforeEnter?: () => void | Promise<void>
afterLeave?: () => void | Promise<void>
}Step Properties
target (required)
- Type:
string | HTMLElement | (() => HTMLElement) - Description: The element to highlight. Can be:
- A CSS selector string (e.g.,
'#my-button','.sidebar-menu') - A direct HTMLElement reference
- A function that returns an HTMLElement (useful for dynamically rendered elements)
- A CSS selector string (e.g.,
title (optional)
- Type:
string - Description: The heading displayed in the tooltip.
content (required)
- Type:
string - Description: The main text content of the tooltip.
position (optional)
- Type:
'top' | 'bottom' | 'left' | 'right' | 'auto' - Default:
'auto' - Description: Preferred position of the tooltip relative to the target element. When set to
'auto', the system calculates the best position based on available viewport space.
highlightPadding (optional)
- Type:
number - Default:
8 - Description: Extra padding around the highlighted element in pixels.
disableInteraction (optional)
- Type:
boolean - Default:
false - Description: When true, prevents user interaction with the target element during this step.
beforeEnter (optional)
- Type:
() => void | Promise<void> - Description: Callback executed before entering this step. Can be async. Useful for preparing the UI (opening menus, loading data).
afterLeave (optional)
- Type:
() => void | Promise<void> - Description: Callback executed after leaving this step. Can be async. Useful for cleanup operations.
Step Examples
const steps: TourStep[] = [
// Basic step with selector
{
target: '#submit-button',
title: 'Submit Form',
content: 'Click here to submit your changes.',
position: 'top'
},
// Step with async preparation
{
target: '#dropdown-menu',
title: 'Menu Options',
content: 'Access additional features from this menu.',
position: 'bottom',
beforeEnter: async () => {
// Open the dropdown before showing the step
document.getElementById('dropdown-trigger')?.click()
await new Promise(resolve => setTimeout(resolve, 300))
},
afterLeave: () => {
// Close the dropdown after leaving
document.getElementById('dropdown-trigger')?.click()
}
},
// Step with function target for dynamic elements
{
target: () => document.querySelector('.dynamic-element') as HTMLElement,
title: 'Dynamic Content',
content: 'This element is rendered dynamically.',
position: 'auto'
},
// Step with increased highlight padding
{
target: '#small-icon',
title: 'Small Element',
content: 'This small icon has extra highlight padding for visibility.',
highlightPadding: 16
}
]Hooks and Events
TourHooks Interface
interface TourHooks {
onStart?: () => void
onFinish?: () => void
onStepChange?: (step: TourStep, index: number) => void
onSkip?: () => void
}Hook Descriptions
onStart
- Triggered: When
controller.start()is called - Use case: Initialize UI state, show tour container, track analytics
onFinish
- Triggered: When the tour completes normally (user clicks Finish on last step)
- Use case: Clean up UI, save completion status, show success message
onStepChange
- Triggered: After each step transition (including the first step)
- Parameters: Current step object and its zero-based index
- Use case: Track progress, update external UI, log analytics
onSkip
- Triggered: When user closes the tour before completion (overlay click, Escape key, close button)
- Use case: Track abandonment, save partial progress, show alternative help
Hook Examples
const tour = createTour({
steps,
hooks: {
onStart: () => {
console.log('Tour started')
analytics.track('tour_started')
},
onFinish: () => {
console.log('Tour completed')
localStorage.setItem('tourCompleted', 'true')
analytics.track('tour_completed')
},
onStepChange: (step, index) => {
console.log(`Now on step ${index + 1}: ${step.title}`)
analytics.track('tour_step_viewed', { step: index, title: step.title })
},
onSkip: () => {
console.log('Tour skipped')
analytics.track('tour_skipped')
}
}
})Customization
TourTooltip Props
The TourTooltip component accepts these customization props:
interface TooltipProps {
step: TourStep
position: PositionResult
currentIndex: number
totalSteps: number
showProgress: boolean
canGoNext: boolean
canGoPrev: boolean
zIndex: number
theme?: 'default' | 'dark' | 'minimal' | 'colorful'
showCloseButton?: boolean
showArrow?: boolean
prevLabel?: string
nextLabel?: string
finishLabel?: string
progressVariant?: 'dots' | 'bar' | 'text'
}Customization Options
theme
- Type:
'default' | 'dark' | 'minimal' | 'colorful' - Default:
'default' - Description: Predefined visual theme for the tooltip
showCloseButton
- Type:
boolean - Default:
true - Description: Whether to show the X close button in the header
showArrow
- Type:
boolean - Default:
true - Description: Whether to show the arrow pointing to the target element
prevLabel
- Type:
string - Default:
'Previous' - Description: Text for the Previous button
nextLabel
- Type:
string - Default:
'Next' - Description: Text for the Next button
finishLabel
- Type:
string - Default:
'Finish' - Description: Text for the Finish button (shown on last step)
progressVariant
- Type:
'dots' | 'bar' | 'text' - Default:
'dots' - Description: Style of the progress indicator
Theming
Available Themes
default
Light theme with white background, blue accent colors.
dark
Dark theme with dark gray background, light text.
minimal
Subtle theme with reduced shadows and border styling.
colorful
Gradient background with purple-blue colors.
Using Themes
<TourTooltip
:step="currentStep"
:position="position"
theme="dark"
...
/>Theme CSS Variables
Each theme sets these CSS custom properties:
--tour-tooltip-bg /* Tooltip background color */
--tour-title-color /* Title text color */
--tour-content-color /* Content text color */
--tour-close-color /* Close button color */
--tour-close-hover-bg /* Close button hover background */
--tour-btn-primary-bg /* Primary button background */
--tour-btn-primary-color /* Primary button text color */
--tour-btn-primary-hover-bg /* Primary button hover background */
--tour-btn-secondary-bg /* Secondary button background */
--tour-btn-secondary-color /* Secondary button text color */
--tour-btn-secondary-hover-bg /* Secondary button hover background */
--tour-progress-dot-bg /* Inactive progress dot color */
--tour-progress-dot-active /* Active progress dot color */
--tour-border-color /* Border color for dividers */Slots
The TourTooltip component provides slots for complete customization.
Available Slots
default
Complete control over entire tooltip content.
<TourTooltip ...>
<template #default="{ step, index }">
<div class="my-custom-tooltip">
<h1>{{ step.title }}</h1>
<p>{{ step.content }}</p>
<span>Step {{ index + 1 }}</span>
</div>
</template>
</TourTooltip>header
Customize the header section (title and close button).
<TourTooltip ...>
<template #header="{ step, index, close }">
<div class="custom-header">
<span class="step-badge">{{ index + 1 }}</span>
<h3>{{ step.title }}</h3>
<button @click="close">X</button>
</div>
</template>
</TourTooltip>content
Customize the main content area.
<TourTooltip ...>
<template #content="{ step, index }">
<div class="custom-content" v-html="step.content"></div>
</template>
</TourTooltip>footer
Customize the entire footer (progress and actions).
<TourTooltip ...>
<template #footer="{ step, index, canGoNext, canGoPrev, next, prev, close }">
<div class="custom-footer">
<button v-if="canGoPrev" @click="prev">Back</button>
<button v-if="canGoNext" @click="next">Continue</button>
<button v-else @click="close">Done</button>
</div>
</template>
</TourTooltip>progress
Customize only the progress indicator.
<TourTooltip ...>
<template #progress="{ current, total }">
<div class="custom-progress">
{{ current + 1 }} of {{ total }}
</div>
</template>
</TourTooltip>actions
Customize only the navigation buttons.
<TourTooltip ...>
<template #actions="{ canGoNext, canGoPrev, next, prev, close }">
<div class="custom-actions">
<button v-if="canGoPrev" @click="prev">Previous</button>
<button v-if="canGoNext" @click="next">Next</button>
<button v-else @click="close">Finish</button>
</div>
</template>
</TourTooltip>Slot Props Reference
| Slot | Props |
|------|-------|
| default | { step: TourStep, index: number } |
| header | { step: TourStep, index: number, close: () => void } |
| content | { step: TourStep, index: number } |
| footer | { step: TourStep, index: number, canGoNext: boolean, canGoPrev: boolean, next: () => void, prev: () => void, close: () => void } |
| progress | { current: number, total: number } |
| actions | { canGoNext: boolean, canGoPrev: boolean, next: () => void, prev: () => void, close: () => void } |
CSS Variables
Animation Variables
:root {
--tour-animation-duration: 300ms;
--tour-animation-easing: ease-out;
}Tooltip Variables
:root {
--tour-tooltip-bg: #ffffff;
--tour-tooltip-radius: 8px;
--tour-tooltip-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
--tour-tooltip-max-width: 340px;
--tour-font-family: system-ui, -apple-system, sans-serif;
}Color Variables
:root {
--tour-title-color: #111827;
--tour-content-color: #4b5563;
--tour-close-color: #6b7280;
--tour-close-hover-bg: #f3f4f6;
--tour-close-hover-color: #111827;
--tour-highlight-color: #3b82f6;
--tour-focus-color: #3b82f6;
--tour-hint-color: #9ca3af;
--tour-border-color: #e5e7eb;
}Button Variables
:root {
--tour-btn-primary-bg: #3b82f6;
--tour-btn-primary-color: #ffffff;
--tour-btn-primary-hover-bg: #2563eb;
--tour-btn-secondary-bg: #f3f4f6;
--tour-btn-secondary-color: #374151;
--tour-btn-secondary-hover-bg: #e5e7eb;
}Progress Variables
:root {
--tour-progress-dot-bg: #e5e7eb;
--tour-progress-dot-active: #3b82f6;
--tour-progress-bar-bg: #e5e7eb;
--tour-progress-bar-fill: #3b82f6;
}Customizing with CSS Variables
/* Custom theme example */
.my-app {
--tour-tooltip-bg: #1a1a2e;
--tour-title-color: #eaeaea;
--tour-content-color: #a0a0a0;
--tour-btn-primary-bg: #e94560;
--tour-highlight-color: #e94560;
--tour-animation-duration: 400ms;
}Keyboard Navigation
When keyboardNavigation is enabled (default):
| Key | Action |
|-----|--------|
| Right Arrow | Go to next step |
| Left Arrow | Go to previous step |
| Escape | Close the tour |
| Enter or Space | Go to next step or finish if on last step |
Accessibility
ARIA Attributes
The tooltip uses proper ARIA attributes:
role="dialog"on the tooltip containeraria-modal="true"to indicate modal behavioraria-labelfor close and navigation buttonsaria-hidden="true"on decorative elements (arrow, SVG icons)
Focus Management
- Focus is trapped within the tooltip when active
- Previous focus is restored when the tour ends
- Tab key cycles through interactive elements within the tooltip
Screen Reader Support
- Step titles are announced via heading elements
- Progress is communicated via text alternatives
- Button labels clearly describe their actions
Architecture
Component Hierarchy
TourRoot
TourOverlay (dark backdrop with spotlight cutout)
TourHighlight (pulsing border around target)
TourTooltip (content bubble with navigation)
TourProgress (step indicator)Module Structure
src/
core/
createTour.ts # Factory function
TourController.ts # Main orchestrator
TourState.ts # Reactive state management
TourNavigator.ts # Step navigation logic
TourEventBus.ts # Event pub/sub system
KeyboardHandler.ts # Keyboard controls
types.ts # TypeScript definitions
dom/
DomResolver.ts # Element resolution with retries
ScrollManager.ts # Scroll-into-view handling
FocusManager.ts # Focus trap management
positioning/
PositionCalculator.ts # Main positioning logic
CollisionDetector.ts # Viewport boundary detection
strategies/
TopStrategy.ts
BottomStrategy.ts
LeftStrategy.ts
RightStrategy.ts
AutoStrategy.ts # Smart auto-positioning
renderer/
components/
TourRoot.vue
TourOverlay.vue
TourTooltip.vue
TourHighlight.vue
TourProgress.vue
composables/
useTour.ts # Main tour control composable
useTourStep.ts # Dynamic step registration
useTourState.ts # State access composablePositioning System
The positioning system uses a strategy pattern:
- Calculate preferred position based on step configuration
- Check for viewport collisions
- If collision detected and position is not
'auto', try auto-positioning - Auto-positioning evaluates available space in all directions
- Choose position with most available space
- Calculate arrow position for visual connection to target
TypeScript Types
Core Types
type Position = 'top' | 'bottom' | 'left' | 'right' | 'auto'
type ScrollBehavior = 'smooth' | 'instant' | 'auto'
type TourStatus = 'idle' | 'running' | 'paused' | 'completed'
type ArrowSide = 'top' | 'bottom' | 'left' | 'right'Step and Options Types
interface TourStep {
target: string | HTMLElement | (() => HTMLElement)
title?: string
content: string
position?: Position
highlightPadding?: number
disableInteraction?: boolean
beforeEnter?: () => void | Promise<void>
afterLeave?: () => void | Promise<void>
}
interface TourOptions {
overlayOpacity?: number
zIndex?: number
scrollBehavior?: ScrollBehavior
scrollOffset?: number
keyboardNavigation?: boolean
closeOnOverlayClick?: boolean
showProgress?: boolean
debug?: boolean
}State and Position Types
interface TourState {
isActive: boolean
currentStep: number
totalSteps: number
status: TourStatus
targetElement: HTMLElement | null
targetRect: DOMRect | null
}
interface PositionResult {
top: number
left: number
arrowPosition: {
side: ArrowSide
offset: number
}
}Event Types
type TourEventType =
| 'tour:start'
| 'tour:stop'
| 'tour:complete'
| 'tour:step-change'
| 'tour:step-enter'
| 'tour:step-leave'
| 'tour:error'
interface TourEventPayloads {
'tour:start': undefined
'tour:stop': undefined
'tour:complete': undefined
'tour:step-change': { step: TourStep; index: number }
'tour:step-enter': { step: TourStep; index: number }
'tour:step-leave': { step: TourStep; index: number }
'tour:error': { error: Error; context?: string }
}Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Requires Vue 3.3.0 or higher.
License
MIT License
