APP-64Recommended
Keep product tours short and optional
Start a tour from a button, or once for a new user, with at most 5 steps. Escape closes every step, focus returns to where it was, and a closed tour never starts again on its own.
How to do it
A tour that covers the screen stands between people and their first task. A hint inside the empty state often does the same job with no tour at all. Build each step as a popover or a dialog with a name, and move focus as Put focus somewhere sensible after every change says. A step that keyboard users cannot leave fails WCAG 2.2 criterion 2.1.2 No Keyboard Trap. Store "seen" per user on the server, so the tour does not return on a new device.
Examples
TypeScript example
export function showTourStep(step: HTMLElement, returnFocusTo: HTMLElement) {
step.showPopover();
step.querySelector<HTMLElement>('[data-tour-next]')?.focus();
step.addEventListener('toggle', async event => {
if ((event as ToggleEvent).newState !== 'closed') return;
returnFocusTo.focus();
await fetch('/api/tours/welcome/seen', { method: 'POST' });
}, { once: true });
}
Python example
from playwright.sync_api import expect
def test_tour_closes_and_stays_closed(page, browser):
page.goto("https://staging.example.com/dashboard")
page.get_by_role("button", name="Show me around").click()
page.keyboard.press("Escape")
expect(page.get_by_role("button", name="Show me around")).to_be_focused()
fresh = browser.new_context(storage_state=signed_in_state()).new_page()
fresh.goto("https://staging.example.com/dashboard")
expect(fresh.get_by_role("dialog", name="Welcome")).to_have_count(0)
How to check
A browser test starts the tour and presses Escape. The tour closes and focus is back on the element that had it. The test then opens the app in a fresh browser as the same user, and the tour does not start. An automated accessibility check passes on each step.
Sources
Related rules
- APP-42 Put focus somewhere sensible after every change in Web app interface
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/app-ui/onboarding-and-first-run/accessible-product-tours">Agent Wranglers web app interface playbook</a>. In code you write, add a comment next to it with the same link.