Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/rules/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Adapters let you swap the underlying implementation without changing your applic
2. `<DocsPageFeatures :frontmatter />` — renders badges from frontmatter
3. `<DocsBrowserSupport>` — optional, for native API features
4. **Usage** — brief intro + code fence (not a live example)
5. **Anatomy** — Vue template tree in `` ```vue playground collapse `` `` code fence
5. **Anatomy** — Vue template tree in `` ```vue Anatomy playground `` `` code fence. **Show only component hierarchy** — no props, no slot bindings, no text content, no directives (`v-if`, `v-slot`, `@click`). Use self-closing tags (`<Tour.Title />` not `<Tour.Title>Title</Tour.Title>`)
6. **Architecture** — optional Mermaid diagram
7. **Examples** — `::: example` blocks, each with 2+ files
8. **Recipes** — code fences or single-file `::: example` blocks
Expand Down
133 changes: 133 additions & 0 deletions apps/docs/src/examples/components/tour/basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<script setup lang="ts">
import { createTour, Tour } from '@vuetify/v0'
import { provide } from 'vue'

const tour = createTour()
provide('v0:tour', tour)

tour.steps.onboard([
{ id: 'welcome', type: 'dialog' },
{ id: 'search' },
{ id: 'profile' },
])
</script>

<template>
<div class="flex flex-col gap-4">
<div class="flex items-center gap-4">
<Tour.Activator step="search">
<input
class="px-3 py-2 border border-divider rounded-lg bg-surface text-on-surface text-sm w-48"
placeholder="Search..."
type="text"
>
</Tour.Activator>

<Tour.Activator step="profile">
<div class="w-8 h-8 rounded-full bg-primary text-on-primary flex items-center justify-center text-sm font-medium">
JL
</div>
</Tour.Activator>
</div>

<button
class="px-4 py-2 bg-primary text-on-primary rounded-lg text-sm font-medium w-fit"
@click="tour.start()"
>
Start Tour
</button>

<Tour.Highlight :padding="8" />

<!-- Step 1: Welcome -->
<Tour.Root step="welcome">
<Tour.Content>
<div class="bg-surface border border-divider rounded-xl p-6 max-w-sm shadow-lg pointer-events-auto">
<Tour.Title class="text-lg font-semibold text-on-surface">
Welcome!
</Tour.Title>

<Tour.Description class="text-sm text-on-surface-variant mt-1">
Let us show you around the interface.
</Tour.Description>

<div class="flex justify-end gap-2 mt-4">
<Tour.Skip
class="px-3 py-1.5 text-sm text-on-surface-variant"
@skip="tour.stop()"
>
Skip
</Tour.Skip>

<Tour.Next class="px-3 py-1.5 text-sm bg-primary text-on-primary rounded-md">
Next
</Tour.Next>
</div>
</div>
</Tour.Content>
</Tour.Root>

<!-- Step 2: Search -->
<Tour.Root step="search">
<Tour.Content>
<div class="bg-surface border border-divider rounded-xl p-4 max-w-xs shadow-lg">
<Tour.Title class="text-base font-semibold text-on-surface">
Search
</Tour.Title>

<Tour.Description class="text-sm text-on-surface-variant mt-1">
Find anything with the search bar.
</Tour.Description>

<div class="flex items-center justify-between mt-3">
<Tour.Progress class="text-xs text-on-surface-variant" />

<div class="flex gap-2">
<Tour.Prev class="px-3 py-1.5 text-sm border border-divider rounded-md disabled:opacity-40">
Back
</Tour.Prev>

<Tour.Next class="px-3 py-1.5 text-sm bg-primary text-on-primary rounded-md">
Next
</Tour.Next>
</div>
</div>
</div>
</Tour.Content>
</Tour.Root>

<!-- Step 3: Profile -->
<Tour.Root step="profile">
<Tour.Content>
<div class="bg-surface border border-divider rounded-xl p-4 max-w-xs shadow-lg">
<Tour.Title class="text-base font-semibold text-on-surface">
Your Profile
</Tour.Title>

<Tour.Description class="text-sm text-on-surface-variant mt-1">
Access settings and preferences here.
</Tour.Description>

<div class="flex items-center justify-between mt-3">
<Tour.Progress class="text-xs text-on-surface-variant" />

<div class="flex gap-2">
<Tour.Prev class="px-3 py-1.5 text-sm border border-divider rounded-md disabled:opacity-40">
Back
</Tour.Prev>

<button
class="px-3 py-1.5 text-sm bg-primary text-on-primary rounded-md"
@click="tour.complete()"
>
Done
</button>
</div>
</div>
</div>
</Tour.Content>
</Tour.Root>

<Tour.Keyboard />
</div>
</template>
65 changes: 65 additions & 0 deletions apps/docs/src/examples/composables/use-tour/basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { createTour } from '@vuetify/v0'
import { provide } from 'vue'

const tour = createTour()
provide('v0:tour', tour)

tour.steps.onboard([
{ id: 'step-1' },
{ id: 'step-2' },
{ id: 'step-3', type: 'wait' },
])
</script>

<template>
<div class="flex flex-col gap-4">
<div class="flex gap-2 flex-wrap">
<button
class="px-3 py-1.5 text-sm bg-primary text-on-primary rounded-md"
@click="tour.start()"
>
Start
</button>

<button
class="px-3 py-1.5 text-sm border border-divider rounded-md"
:disabled="!tour.isActive.value"
@click="tour.prev()"
>
Prev
</button>

<button
class="px-3 py-1.5 text-sm border border-divider rounded-md"
:disabled="!tour.isActive.value"
@click="tour.next()"
>
Next
</button>

<button
class="px-3 py-1.5 text-sm border border-divider rounded-md"
:disabled="!tour.isActive.value"
@click="tour.stop()"
>
Stop
</button>

<button
v-if="!tour.isReady.value && tour.isActive.value"
class="px-3 py-1.5 text-sm bg-success text-on-success rounded-md"
@click="tour.ready()"
>
Mark Ready
</button>
</div>

<div class="text-sm space-y-1 text-on-surface-variant">
<div>Active: <span class="font-mono text-on-surface">{{ tour.isActive.value }}</span></div>
<div>Step: <span class="font-mono text-on-surface">{{ tour.selectedId.value ?? '—' }}</span></div>
<div>Ready: <span class="font-mono text-on-surface">{{ tour.isReady.value }}</span></div>
<div>Complete: <span class="font-mono text-on-surface">{{ tour.isComplete.value }}</span></div>
</div>
</div>
</template>
83 changes: 83 additions & 0 deletions apps/docs/src/pages/components/disclosure/tour.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: Tour - Guided Tour Component for Vue 3
meta:
- name: description
content: Headless guided tour component for Vue 3 with step navigation, validation gates, keyboard support, and activator highlighting. Fully customizable and accessible.
- name: keywords
content: tour, guided tour, onboarding, walkthrough, tooltip, Vue 3, headless, accessibility
features:
category: Component
label: 'C: Tour'
github: /components/Tour/
renderless: false
level: 2
related:
- /components/disclosure/dialog
- /components/disclosure/popover
- /composables/plugins/use-tour
---

# Tour

A headless guided tour component for building onboarding flows, feature walkthroughs, and contextual help.

<DocsPageFeatures :frontmatter />

## Usage

The Tour component composes step navigation, activator tracking, and overlay management into a compound component pattern. Install the plugin, register steps, and wrap target elements with activators.

::: example
/components/tour/basic
:::

## Anatomy

```vue Anatomy playground
<script setup lang="ts">
import { Tour } from '@vuetify/v0'
</script>

<template>
<Tour.Highlight />

<Tour.Root>
<Tour.Content>
<Tour.Title />

<Tour.Description />

<Tour.Progress />

<Tour.Prev />

<Tour.Next />

<Tour.Skip />
</Tour.Content>
</Tour.Root>

<Tour.Activator />

<Tour.Keyboard />
</template>
```

## Step Types

| Type | Behavior |
| - | - |
| `tooltip` | Anchored to an activator element (default) |
| `dialog` | Centered overlay, no activator needed |
| `floating` | Positioned freely, no activator anchoring |
| `wait` | Blocks navigation until `tour.ready()` is called |

## Accessibility

- Tour content uses `role="dialog"` with `aria-modal="true"`
- Title and description linked via `aria-labelledby` and `aria-describedby`
- Navigation buttons have descriptive `aria-label` attributes
- Progress uses `role="status"` for screen reader announcements
- Keyboard navigation via `TourKeyboard` (arrow keys + escape)

<DocsApi />
1 change: 1 addition & 0 deletions apps/docs/src/pages/components/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,6 @@ Components for showing/hiding content.
| [ExpansionPanel](/components/disclosure/expansion-panel) | Accordion-style collapsible panels |
| [Popover](/components/disclosure/popover) | CSS anchor-positioned popup content |
| [Tabs](/components/disclosure/tabs) | Tab panel navigation with keyboard support and lazy content rendering |
| [Tour](/components/disclosure/tour) | Guided tour with step navigation, validation gates, and keyboard support |
| [Treeview](/components/disclosure/treeview) | Hierarchical tree with nested selection and expand/collapse |

1 change: 1 addition & 0 deletions apps/docs/src/pages/composables/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ Application-level features installable via Vue plugins.
| [useRules](/composables/plugins/use-rules) | Validation rule aliases with locale-aware messages |
| [useStorage](/composables/plugins/use-storage) | Reactive browser storage interface |
| [useTheme](/composables/plugins/use-theme) | Theme management with CSS custom properties |
| [useTour](/composables/plugins/use-tour) | Guided tour orchestration with step navigation and validation gates |

## Data

Expand Down
Loading
Loading