本文へ移動
cccskills
無料GitHub で公開

add-tour

Add or change an in-app feature tour (a view's guided flyover) in the web app. Use when adding a tour to a view, adding or editing tour steps, pointing a step at a new control, or when a step's target needs something opened first.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Add or change an in-app tour

Frontend only. Two layers:

LayerWhereWhat
Primitiveapps/web/src/components/ui/components/Tour/Tour.* parts, defineTourTargets, tourTarget. Generic; no app knowledge.
Appapps/web/src/features/tours/ViewTour (desktop gate, per-user dismissal, connector/video CTAs), defineViewTour.
Per viewapps/web/src/features/<view>/tour.tsThe view's targets and its tour definition.

Read an existing tour.ts first. channels-view/tour.ts and agents-view/tour.ts show entries; calendar-view/tour.ts shows the sidebar toggle entry.

1. Declare targets

In the view's tour.ts:

export const REPORTS_TOUR = defineTourTargets('reports', ['filters', 'chart']);
  • Ids are namespace.name. Use the view's name as the namespace.
  • Targets are view-scoped: they resolve only inside the tour's split. Pass { scope: 'app' } only for shared chrome outside any split (see command/sidebar/tour.ts).
  • Reuse shared targets before adding new ones:
    • VIEW_SHELL_TOUR (@app/components/view-shell/tour): aside, main, topBar, sidebarToggle. Every ViewShell registers these.
    • SOUP_TOUR (features/next-soup/tour.ts): list.
    • CHANNEL_TOUR (features/channel/tour.ts): messages, composer, call.
    • APP_TOUR (features/command/sidebar/tour.ts): createMenu.

Never target with selectors, data-* attributes, or aria-label lookups. If the control you want isn't registered, register it (step 2).

2. Attach targets

<div ref={tourTarget(REPORTS_TOUR.filters)}>…</div>
  • The element must have a box. display: contents wrappers never resolve; attach to a real element.
  • Never write a conditional ref like ref={cond ? tourTarget(X) : undefined}. The Solid compiler silently drops it, and the target never registers. Create the ref in the component body and decide inside a callback: const target = tourTarget(X); then ref={(el) => { if (cond) target(el); }}.
  • If the element already has a ref, call both: ref={(el) => { existing(el); target(el); }} with const target = tourTarget(...) created in the component body, or use mergeRefs from @solid-primitives/refs.
  • For a component that doesn't forward ref, add a ref prop that it passes to its root (see SidebarCreateButton).
  • A lower-level feature may import targets from the view that owns the tour (for example, features/calendar imports CALENDAR_TOUR).

3. Define the tour

export const reportsTour = defineViewTour({
  id: 'reports',            // dismissal key: never reuse or rename casually
  title: 'Reports',
  connector: { kind: 'mcp', label: 'Linear', tools: ['Linear'] }, // optional
  video: { youtubeId: '…', title: '…', duration: '1:23' },          // optional
  steps: [
    { target: REPORTS_TOUR.filters, title: '…', description: '…' },
    { target: [REPORTS_TOUR.chart, VIEW_SHELL_TOUR.main], title: '…', description: '…' },
  ],
});
  • target may be a list; the first shown one wins. Use this for features that aren't always rendered (behind a flag, a connection, or data such as "more than one calendar"): list the real control first and the nearest always-present surface after it, and set missingHint. The hint shows whenever the step falls back.
  • Before relying on a target, check every branch that renders it: touch vs desktop headers, preview vs workspace, empty states. Register the same target in each branch that shows the control.
  • A step without target floats in the split.
  • missingHint explains how to reach or enable the feature when the step floats or falls back.
  • placement (a floating-ui Placement) overrides the default right-start.

4. Never navigate for the user: use an entry

If a step's target is behind something (a collapsed sidebar, another page, an unopened item), don't open it from the tour. Give the step an entry:

{
  target: REPORTS_TOUR.chart,
  entry: VIEW_SHELL_TOUR.sidebarToggle,
  entryLabel: 'Open the sidebar to continue the tour',
  title: '…',
  description: '…',
}

While the target is missing and the entry is shown, the card hides, a beacon marks the entry, and a small card beside it shows entryLabel with Skip. The beacon stays until the target appears, however the user gets there. Register the entry control as a target like any other.

  • Keep entryLabel short and imperative: "Open a channel or DM to continue".
  • Chain entries when the entry itself can be hidden. The first shown one wins, so list the real control first and the sidebar toggle last: entry: [CHANNELS_TOUR.conversation, VIEW_SHELL_TOUR.sidebarToggle]. With the sidebar collapsed the beacon marks the toggle; once it opens, the beacon moves to the row.
  • ViewTour appends VIEW_SHELL_TOUR.sidebarToggle to every step that has a target, so a step whose target is in a collapsed sidebar always marks the toggle. You still need it explicitly only as the last link of a chain you write yourself.
  • A view that collapses its sidebar itself instead of through ViewShell (like Customers' NavigationToggle) must register its own expand control as VIEW_SHELL_TOUR.sidebarToggle, or those steps will float.
  • Point entries at a specific control, not a large container. When many elements share a target (every conversation row), the top-most one on screen is used.

5. Mount it

Mount once, inside the view's split (usually in ViewShell.Main or next to the list):

<ViewTour tour={reportsTour} />
<ViewTour tour={tasksTour} actions={<ImportLinearAction />} />  // extra CTAs
  • For SoupView-based routes, pass it through the slot: <SoupView … tour={<ViewTour tour={callsTour} />} />.
  • Don't put it inside a <Show> branch that remounts on navigation, or the tour restarts at step 1.
  • Don't gate it on desktop or on a flag yourself; ViewTour already checks both. All tours sit behind the enableInAppTours flag (enable-in-app-tours in PostHog). To see tours on the local dev server, start it with VITE_ENABLE_IN_APP_TOURS=true.
  • Don't pass step callbacks that change pages or open panels.

6. Test and document

  • Unit behavior of the primitive lives in Tour/Tour.test.tsx and app behavior in features/tours/ViewTour.test.tsx; add cases there when you change them.
  • The primitive's gallery page is Tour/Tour.docs.tsx.
  • Progress is saved per user in local storage under macro:tour:<id> (completed, dismissed, or the active step). Changing a tour's steps doesn't reset it for users who finished it; bump the id only if everyone must see the new version.
  • Check it in a browser with VITE_ENABLE_IN_APP_TOURS=true: on localhost the tour reopens on every mount. Walk every step, including one that waits on an entry, once with the sidebar open and once collapsed.
  • Update docs/AGENT_GUIDE/view-tours.md when a view gains a tour or its steps change.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Wrap a new backend endpoint in the TypeScript SDK (packages/sdk), or record it as skipped. Use when `just coverage` fails, or after adding an endpoint to a Rust service.

日本語の概要は準備中です。原文の説明を表示しています。

macro-inc/macro4,6362026年10月12日 更新

Enforce hexagonal architecture in the Rust backend. Use before modifying crates or Rust services, especially inbound axum/tool/listener adapters, domain services/ports, outbound adapters, authorization, permissions, database access, or external clients.

日本語の概要は準備中です。原文の説明を表示しています。

macro-inc/macro4,6362026年10月12日 更新

Enforce hexagonal architecture in the Rust backend. Use before modifying crates/ or Rust services, especially inbound axum/tool/listener adapters, domain services/ports, outbound adapters, authorization, permissions, database access, or external clients.

日本語の概要は準備中です。原文の説明を表示しています。

macro-inc/macro4,6362026年10月12日 更新

Use when adding collaborative markdown, notes, or descriptions to a feature with collab surfaces. Covers parent entities, service entry points, authorization, content storage, and lifecycle.

日本語の概要は準備中です。原文の説明を表示しています。

macro-inc/macro4,6362026年10月12日 更新

Build a new AI tool end-to-end — Rust implementation, toolset wiring, infra, schema generation, and frontend UI.

日本語の概要は準備中です。原文の説明を表示しています。

macro-inc/macro4,6362026年10月12日 更新

Use when building a feature on reusable database storage or UI, such as CRM, or deciding whether it needs a Macro database entity. Covers shared storage, a host-independent database UI and API contract, ownership, authorization, and the _entities naming convention.

日本語の概要は準備中です。原文の説明を表示しています。

macro-inc/macro4,6362026年10月12日 更新

macro-inc のスキルをすべて見る

このスキルの問題を報告する