EN
Getting started
50 · Extended UI

Tour

Walks the user through the important parts of the page step by step. At each step one element is highlighted, the rest of the screen dims, and a small popover opens next to the element with a title, a description, a step counter, and Back / Next step buttons. It suits introducing the panel on first sign-in or showing off a new feature.

PRO acun:tour
01

Basic usage

Every tour has a unique name value. Each step in the steps array takes a target (a CSS selector), a title, and a text. The content you place inside the component is within the tour's scope; a button there starts the tour with start(). On the last step, "Finish" appears instead of "Next step".

LIVE EXAMPLE
<acun:tour name="order-tour" :steps="[
    ['target' => '#order-search', 'title' => 'Order search', 'text' => 'Find orders by number, customer name, or phone number.'],
    ['target' => '#order-add', 'title' => 'New order', 'text' => 'Record an order that came in by phone here.'],
    ['target' => '#order-summary', 'title' => 'Monthly summary', 'text' => 'Follow the shipments and pending returns for this month here.', 'placement' => 'top'],
]">
    <acun:button variant="white" x-on:click="start()">Start the tour</acun:button>
</acun:tour>

<div class="mt-6 space-y-4">
    <div class="flex flex-wrap items-center gap-3">
        <div id="order-search" class="w-64"><acun:input name="search" placeholder="Search orders…" /></div>
        <acun:button id="order-add">New order</acun:button>
    </div>
    <div id="order-summary" class="grid grid-cols-2 gap-3">
        <acun:stat-card label="Shipped this month" value="148" />
        <acun:stat-card label="Pending returns" value="23" />
    </div>
</div>
PropValues
nameUnique name (required)
steps[['target' => '#selector', 'title' => …, 'text' => …], …]
start()Starts the tour from inside the component
02

Placement

placement chooses which side of the target the popover opens on; the default is bottom. If there is no room on the chosen side, the popover first moves to the opposite side, then to the other two sides, and is shifted so that it does not overflow the screen. The last step shows this: it wants to open on top, but because there is no room above the button, it moves below. The component's placement is the default for steps that have no direction of their own.

LIVE EXAMPLE
<acun:tour name="placement-tour" :steps="[
    ['target' => '#placement-order', 'title' => 'On the right', 'text' => 'right: the popover opens to the right of the order card.', 'placement' => 'right'],
    ['target' => '#placement-courier', 'title' => 'On the left', 'text' => 'left: the popover opens to the left of the courier card.', 'placement' => 'left'],
    ['target' => '#placement-note', 'title' => 'On top', 'text' => 'top: the popover opens above the delivery note.', 'placement' => 'top'],
    ['target' => '#placement-start', 'title' => 'No room', 'text' => 'This step wanted to open on top; since there was no room there, it moved below.', 'placement' => 'top'],
]">
    <acun:button id="placement-start" variant="white" x-on:click="start()">Show placements</acun:button>
</acun:tour>

<div class="mt-6 flex items-start justify-between gap-6">
    <div id="placement-order" class="w-40 rounded-sm bg-white p-4 text-sm shadow-sm dark:bg-zinc-900">
        <p class="text-xs text-gray-500 dark:text-zinc-400">Order</p>
        <p class="font-semibold text-gray-900 dark:text-zinc-100">#10482</p>
    </div>
    <div id="placement-courier" class="w-40 rounded-sm bg-white p-4 text-sm shadow-sm dark:bg-zinc-900">
        <p class="text-xs text-gray-500 dark:text-zinc-400">Courier</p>
        <p class="font-semibold text-gray-900 dark:text-zinc-100">Mehmet Kaya</p>
    </div>
</div>
<p id="placement-note" class="mt-32 rounded-sm bg-white p-4 text-sm shadow-sm dark:bg-zinc-900">Delivery note: the building entrance is on the back street; the package can be left with the doorman.</p>
PropValues
steps.*.placementtop | bottom | left | right
placementDefault direction of the steps; default bottom
03

Starting from anywhere

The tour can be started from anywhere on the page by dispatching the start-tour event with its name; the button does not need to be inside the component. A step without a target opens in the middle of the screen, which suits opening the tour with a welcome. A step whose target is not on the page (or is hidden) is skipped and not counted: that is why the chart step does not appear in the tour below, and the counter shows 3 steps.

LIVE EXAMPLE
<div class="flex flex-wrap items-center justify-between gap-3">
    <h3 class="text-base font-semibold text-gray-900 dark:text-zinc-100">Monthly report</h3>
    <acun:button variant="ghost" size="sm" x-data x-on:click="$dispatch('start-tour', { name: 'report-tour' })">How do I use this page?</acun:button>
</div>
<div class="mt-4 flex flex-wrap items-center gap-3">
    <div id="report-period" class="w-48"><acun:select name="period" :options="['2026-09' => 'September 2026', '2026-08' => 'August 2026']" /></div>
    <acun:button id="report-export" variant="white">Export to Excel</acun:button>
</div>

<acun:tour name="report-tour" :steps="[
    ['title' => 'Monthly report', 'text' => 'This page summarizes the sales figures for the month you select. Here is a quick look.'],
    ['target' => '#report-period', 'title' => 'Period', 'text' => 'Choose which month the report covers here.'],
    ['target' => '#report-chart', 'title' => 'Weekly chart', 'text' => 'How the sales are spread across the weeks.'],
    ['target' => '#report-export', 'title' => 'Export', 'text' => 'Download the rows of the selected period as an Excel file.'],
]" />
PropValues
start-tour$dispatch('start-tour', { name: '…' })
steps.*.targetEmpty: opens in the middle · not found: the step is skipped
04

Autostart and remembering

autostart starts the tour when the page has loaded (images included). With remember, a tour that was finished or skipped does not start on its own again in this browser: this is stored in localStorage under acun-tour: followed by the tour's name (acun-tour:first-login below). start() and start-tour ignore it, so the tour can always be replayed manually. To turn autostart back on, delete the key: localStorage.removeItem('acun-tour:first-login'). If the browser does not allow storage (e.g. in some private windows), the tour simply does not remember; it does not throw an error. Because this example would take focus as soon as the page opens, it is not shown live here.

BLADE
<acun:tour name="first-login" autostart remember :steps="[
    ['title' => 'Welcome', 'text' => 'A few steps to introduce the main parts of the panel.'],
    ['target' => '#main-menu', 'title' => 'Menu', 'text' => 'Reach orders, customers, and reports from here.', 'placement' => 'right'],
    ['target' => '#notifications', 'title' => 'Notifications', 'text' => 'Support tickets assigned to you are listed here.'],
]" />

{{-- Can be started manually even if it has been remembered. --}}
<acun:button variant="ghost" x-data x-on:click="$dispatch('start-tour', { name: 'first-login' })">Replay the introduction</acun:button>
PropValues
autostarttrue | false
remembertrue | false · key: 'acun-tour:' + name
05

With Livewire

Dispatch the start-tour event from the server. When the tour is finished or skipped, the tour-ended event is dispatched with { name, finished } (finished is false when skipped); listen for it to record on the user that the tour has been seen, and tie the autostart value to that record. That way the tour does not open again even when the browser changes. Extra attributes you pass to the component, such as x-on:, are added to the root element.

PHP
// <acun:tour
//     name="first-login"
//     :steps="$tourSteps"
//     :autostart="! auth()->user()->tour_seen"
//     x-on:tour-ended.window="$event.detail.name === 'first-login' && $wire.markTourSeen()"
// />

public function markTourSeen(): void
{
    auth()->user()->forceFill(['tour_seen' => true])->save();
}

public function showHelp(): void
{
    $this->dispatch('start-tour', name: 'first-login');
}
PropValues
tour-ended{ name, finished } · finished: false = skipped
06

Keyboard and accessibility

The tour can be used with the keyboard alone. At each step focus moves to the popover, and screen readers read the title, the description, and the counter. Tab and Shift+Tab cycle through the popover's buttons, → moves to the next step and ← to the previous one, and Esc ends the tour (it counts as skipped). When the tour closes, focus returns to the element that started it. The popover has role="dialog" and aria-modal="true"; it takes its name from the title and its description from the text and the counter. While the tour is open, the page behind it cannot be clicked. If the operating system asks for reduced motion, the page scrolls to the target without animation. The button texts (Back, Next step, Finish, Skip tour) and the counter format (:current / :total) are translated with __().

LIVE EXAMPLE
<acun:tour name="form-tour" :steps="[
    ['target' => '#customer-name', 'title' => 'Full name', 'text' => 'Enter the customer name as it should appear on invoices.'],
    ['target' => '#customer-phone', 'title' => 'Phone', 'text' => 'Enter a mobile number the courier can reach.'],
    ['target' => '#customer-save', 'title' => 'Save', 'text' => 'Saved customers appear in the customer list.', 'placement' => 'right'],
]">
    <acun:button variant="white" x-on:click="start()">Tour the form</acun:button>
</acun:tour>

<div class="mt-6 max-w-sm space-y-4">
    <div id="customer-name"><acun:input name="name" label="Full name" /></div>
    <div id="customer-phone"><acun:input name="phone" type="tel" label="Phone" /></div>
    <acun:button id="customer-save">Save</acun:button>
</div>
PropValues
Tab / Shift+TabBetween the popover's buttons
→ / ←Next / previous step
EscEnds the tour
API

Props and slots

The values the component accepts.

PropDefaultDescription
name — Required. The name used in the start-tour event; the remember key is also derived from it
steps[]Array of steps; each step takes target, title, text, and an optional placement
placementbottomFor steps without a direction of their own: top · bottom · left · right
autostartfalseStarts when the page loads
rememberfalseA finished or skipped tour does not start on its own again in this browser (localStorage: 'acun-tour:' + name)
steps.*.targetnullCSS selector of the target. If empty, the popover opens in the middle of the screen; if the element is not found or is hidden, the step is skipped
steps.*.title''Popover title; also the accessible name of the dialog
steps.*.text''Description; written as plain text (HTML is not rendered)
steps.*.placementbottomtop · bottom · left · right; if there is no room, it moves to another side and stays on screen
Slots: default: content within the tour's scope, e.g. a button that calls start() (optional). Events: start-tour { name } starts the tour; when the tour closes, tour-ended { name, finished } is dispatched.
Acun UI · TourDetailed usage and examples