Tour
The Tour component guides users through a sequence of steps, highlighting a target element with a spotlight and showing a popover next to it. Steps are declared with Tour Item components. It is controlled through v-model:active and v-model:step. While active, the rest of the page is dimmed and not interactive and Escape skips the tour.
TIP
Each step's target is a CSS selector or a function returning an element. Make sure the target exists in the DOM when its step becomes active.
Props
mask-padding?: number
The padding in pixels around the highlighted element.
Default: 8
root?: string | HTMLElement | (() => HTMLElement | null)
Container the step targets are resolved within. Useful when multiple tours share selectors on one page. Defaults to the document.
Emits
finish: []
Triggered when the last step is completed.
skip: []
Triggered when the tour is skipped or dismissed with Escape.
next: [number]
Triggered when advancing to the next step, with the new step index.
prev: [number]
Triggered when going back a step, with the new step index.
Slots
default
The steps of the tour, provided as Tour Item components.
Tour Item
Each step of the tour is a FluxTourItem. It targets an element through the target prop, optionally shows a title, and renders its default slot as the step content.
| Prop | Type | Description |
|---|---|---|
target | string | (() => HTMLElement | null) | A CSS selector or getter for the element this step highlights. |
title | string | The optional title shown at the top of the step. |
position | FluxTourPosition | The optional position of the popover relative to the target. Defaults to bottom. |
The default slot holds the step content.
Examples
Basic
A three-step product tour.
<template>
<div
ref="root"
style="display: flex; flex-flow: column; gap: 18px; align-items: flex-start">
<div style="display: flex; gap: 9px">
<span id="tour-create">
<FluxPrimaryButton label="Create"/>
</span>
<span id="tour-search">
<FluxSecondaryButton label="Search"/>
</span>
<span id="tour-settings">
<FluxSecondaryButton label="Settings"/>
</span>
</div>
<FluxSecondaryButton
label="Start tour"
@click="start"/>
<FluxTour
v-model:active="active"
v-model:step="step"
:root="root">
<FluxTourItem
target="#tour-create"
title="Create">
Start by creating a new item here.
</FluxTourItem>
<FluxTourItem
target="#tour-search"
title="Search">
Quickly find anything in your workspace.
</FluxTourItem>
<FluxTourItem
target="#tour-settings"
title="Settings">
Tweak your preferences anytime.
</FluxTourItem>
</FluxTour>
</div>
</template>
<script
setup
lang="ts">
import { FluxPrimaryButton, FluxSecondaryButton, FluxTour, FluxTourItem } from '@flux-ui/components';
import { ref } from 'vue';
const root = ref<HTMLElement>();
const active = ref(false);
const step = ref(0);
function start(): void {
step.value = 0;
active.value = true;
}
</script>Positions
Control where each popover appears with the position prop.
<template>
<div style="display: flex; flex-flow: column; gap: 18px; align-items: flex-start">
<div style="display: flex; gap: 9px; flex-wrap: wrap">
<span id="pos-bottom">
<FluxSecondaryButton label="Bottom"/>
</span>
<span id="pos-top">
<FluxSecondaryButton label="Top"/>
</span>
<span id="pos-right">
<FluxSecondaryButton label="Right"/>
</span>
<span id="pos-left">
<FluxSecondaryButton label="Left"/>
</span>
</div>
<FluxSecondaryButton
label="Start tour"
@click="start"/>
<FluxTour
v-model:active="active"
v-model:step="step">
<FluxTourItem
target="#pos-bottom"
title="Bottom"
position="bottom">
The popover appears below the target.
</FluxTourItem>
<FluxTourItem
target="#pos-top"
title="Top"
position="top">
The popover appears above the target.
</FluxTourItem>
<FluxTourItem
target="#pos-right"
title="Right"
position="right">
The popover appears to the right of the target.
</FluxTourItem>
<FluxTourItem
target="#pos-left"
title="Left"
position="left">
The popover appears to the left of the target.
</FluxTourItem>
</FluxTour>
</div>
</template>
<script
setup
lang="ts">
import { FluxSecondaryButton, FluxTour, FluxTourItem } from '@flux-ui/components';
import { ref } from 'vue';
const active = ref(false);
const step = ref(0);
function start(): void {
step.value = 0;
active.value = true;
}
</script>Custom content
Render any component inside a step through its default slot.
<template>
<div style="display: flex; flex-flow: column; gap: 18px; align-items: flex-start">
<div style="display: flex; gap: 9px">
<span id="custom-create">
<FluxPrimaryButton
icon-leading="plus"
label="New project"/>
</span>
<span id="custom-search">
<FluxSecondaryButton
icon-leading="magnifying-glass"
label="Search"/>
</span>
<span id="custom-invite">
<FluxSecondaryButton
icon-leading="user-plus"
label="Invite"/>
</span>
</div>
<FluxSecondaryButton
label="Start tour"
@click="start"/>
<FluxTour
v-model:active="active"
v-model:step="step">
<FluxTourItem
target="#custom-create"
title="Create a project">
<div style="display: flex; gap: 12px; align-items: flex-start">
<FluxBoxedIcon
color="primary"
name="rocket"/>
<div style="display: flex; flex-flow: column; gap: 9px; align-items: flex-start">
<span>Spin up a new project and start collaborating right away.</span>
<FluxChip
icon-leading="bolt"
label="Shortcut: C"/>
</div>
</div>
</FluxTourItem>
<FluxTourItem
target="#custom-search"
title="Find anything">
<div style="display: flex; flex-flow: column; gap: 9px; align-items: flex-start">
<span>Search across projects, files and people from a single place.</span>
<div style="display: flex; gap: 6px">
<FluxBadge
color="info"
icon="file"
label="Files"/>
<FluxBadge
color="success"
icon="users"
label="People"/>
</div>
</div>
</FluxTourItem>
<FluxTourItem
target="#custom-invite"
title="Invite your team">
<div style="display: flex; flex-flow: column; gap: 12px; align-items: flex-start">
<span>Bring your teammates on board to work together in real time.</span>
<FluxLink
href="#"
icon="circle-info"
label="Learn about roles"/>
</div>
</FluxTourItem>
</FluxTour>
</div>
</template>
<script
setup
lang="ts">
import { FluxBadge, FluxBoxedIcon, FluxChip, FluxLink, FluxPrimaryButton, FluxSecondaryButton, FluxTour, FluxTourItem } from '@flux-ui/components';
import { ref } from 'vue';
const active = ref(false);
const step = ref(0);
function start(): void {
step.value = 0;
active.value = true;
}
</script>Rich onboarding
Combine progress, avatars and actions for a richer tour.
<template>
<div style="display: flex; flex-flow: column; gap: 18px; align-items: flex-start">
<div style="display: flex; gap: 9px">
<span id="rich-dashboard">
<FluxSecondaryButton
icon-leading="gauge-high"
label="Dashboard"/>
</span>
<span id="rich-team">
<FluxSecondaryButton
icon-leading="users"
label="Team"/>
</span>
<span id="rich-upgrade">
<FluxPrimaryButton
icon-leading="sparkles"
label="Upgrade"/>
</span>
</div>
<FluxSecondaryButton
label="Start tour"
@click="start"/>
<FluxTour
v-model:active="active"
v-model:step="step">
<FluxTourItem
target="#rich-dashboard"
title="Welcome aboard">
<div style="display: flex; flex-flow: column; gap: 12px">
<span>Let's finish setting up your workspace. Here's how far you've come.</span>
<FluxProgressBar
:value="40"
status="2 of 5 steps complete"/>
</div>
</FluxTourItem>
<FluxTourItem
target="#rich-team"
title="Your team">
<div style="display: flex; flex-flow: column; gap: 12px; align-items: flex-start">
<span>You're already collaborating with a few people.</span>
<FluxAvatarGroup
:max="4"
:size="32">
<FluxAvatar
fallback="colorized"
fallback-initials="AB"/>
<FluxAvatar
fallback="colorized"
fallback-initials="CD"/>
<FluxAvatar
fallback="colorized"
fallback-initials="EF"/>
</FluxAvatarGroup>
</div>
</FluxTourItem>
<FluxTourItem
target="#rich-upgrade"
title="Unlock more">
<div style="display: flex; flex-flow: column; gap: 12px; align-items: flex-start">
<div style="display: flex; gap: 12px; align-items: flex-start">
<FluxBoxedIcon
color="warning"
name="star"/>
<span>Upgrade to Pro for unlimited projects and priority support.</span>
</div>
<FluxPrimaryButton
icon-leading="sparkles"
label="See plans"
@click="active = false"/>
</div>
</FluxTourItem>
</FluxTour>
</div>
</template>
<script
setup
lang="ts">
import { FluxAvatar, FluxAvatarGroup, FluxBoxedIcon, FluxPrimaryButton, FluxProgressBar, FluxSecondaryButton, FluxTour, FluxTourItem } from '@flux-ui/components';
import { ref } from 'vue';
const active = ref(false);
const step = ref(0);
function start(): void {
step.value = 0;
active.value = true;
}
</script>