| name | onboarding-architect |
| description | Expert assistant for the onboarding system in Raamattu Nyt. Two tour modes: showcase (fullscreen modal with images) and guided (spotlight + tooltip on DOM targets). Use when (1) adding new onboarding tours, (2) modifying tour steps, images, or placement, (3) working with the admin panel at /ohjaamo/onboarding, (4) adding tour trigger points (URL params, UserMenu, buttons), (5) managing Supabase Storage onboarding-images bucket, (6) editing i18n keys for onboarding texts.
|
Onboarding Architect
Not FAQ/help content. Writing FAQ questions (faq_items) or help pages belongs to
help-system-architect (see its references/faq-generator.md), not here. This skill is
onboarding tours only.
Architecture
packages/shared-onboarding/ # Reusable engine (no app imports)
├── src/
│ ├── types.ts # Tour, OnboardingStep, Placement, Align
│ ├── OnboardingProvider.tsx # React context: useReducer state machine
│ ├── useOnboarding.ts # Consumer hook: startTour, nextStep, etc.
│ ├── ShowcaseModal.tsx # Fullscreen modal (lazy-loaded)
│ ├── GuidedOverlay.tsx # Spotlight overlay (lazy-loaded)
│ ├── GuidedTooltip.tsx # Radix Popover tooltip (side, align, sideOffset)
│ ├── storage.ts # localStorage: markTourCompleted, isTourCompleted
│ └── index.ts # Public exports
apps/raamattu-nyt/src/onboarding/ # App-level tour definitions
├── tours/
│ ├── showcaseHome.ts # Showcase tour (7 steps, i18n keys + CTA)
│ ├── searchBasics.ts # Guided tour: search basics (4 steps)
│ ├── searchQuestionsTab.ts # Guided tour: Q&A tab (4 steps)
│ └── index.ts # useAllTours() hook: resolves i18n + images + overrides
apps/raamattu-nyt/src/hooks/
└── useOnboardingImages.ts # Fetches images + guided overrides from app_config
apps/raamattu-nyt/src/pages/
└── AdminOnboardingPage.tsx # Admin: tour list, image upload, guided placement
Tour Modes
| Mode | Component | Behavior |
|---|
showcase | ShowcaseModal | Fullscreen modal, image per step, CTA buttons, keyboard/touch nav |
guided | GuidedOverlay + GuidedTooltip | Spotlight on [data-onboard="..."] target, Radix Popover tooltip |
Adding a New Showcase Tour
- Create
apps/raamattu-nyt/src/onboarding/tours/myTour.ts:
import type { Tour } from "@shared-onboarding";
interface ShowcaseStepDef {
id: string;
titleKey: string;
descriptionKey: string;
ctaKey?: string;
ctaHref?: string;
}
const myTourDef: ShowcaseStepDef[] = [
{
id: "step1",
titleKey: "onboarding.showcase.myTour.step1.title",
descriptionKey: "onboarding.showcase.myTour.step1.description",
},
];
export function createMyTour(
images?: Record<string, string>,
t?: (key: string) => string,
): Tour {
const tr = t ?? ((key: string) => key);
return {
id: "my-tour",
mode: "showcase",
steps: myTourDef.map((def) => ({
id: def.id,
: (def.),
: (def.),
: images?.[def.],
: def. ? { : (def.), : def. } : ,
})),
};
}
: = ();
- Add i18n keys to
public/locales/fi/common.json and public/locales/en/common.json
- Register in
tours/index.ts — import, add to useAllTours() return array
- Upload images via admin panel or set in
app_config key onboarding_showcase_images
Adding a New Guided Tour
- Create step definitions with
GuidedStepDef:
import type { GuidedStepDef } from "./searchBasics";
export const myGuidedDef: { id: string; mode: "guided"; steps: GuidedStepDef[] } = {
id: "my-guided-tour",
mode: "guided",
steps: [
{
id: "first-element",
titleKey: "onboarding.guided.myTour.first.title",
descriptionKey: "onboarding.guided.myTour.first.description",
target: '[data-onboard="my-first-element"]',
placement: "bottom",
align: "center",
sideOffset: 12,
},
],
};
- Add
data-onboard="my-first-element" attribute to target DOM elements
- Add i18n keys to locale files
- Register in
tours/index.ts:
- Import def into
guidedTourDefs array
- Add static fallback to
allTours array
Trigger Points
| Trigger | How it works |
|---|
startTour(tourId) | Direct call via useOnboarding() hook |
URL param ?tour=tourId | SearchPage detects param in useEffect, calls startTour with 500ms delay |
| UserMenu dropdown | Items in "Tutustu ominaisuuksiin" section call startTour or navigate |
| Button/link | Navigate to page with ?tour=tourId query param |
To add a URL-triggered tour to any page:
const [searchParams] = useSearchParams();
const { startTour } = useOnboarding();
const tourTriggered = useRef(false);
useEffect(() => {
const tourId = searchParams.get("tour");
if (tourId && !tourTriggered.current) {
tourTriggered.current = true;
setTimeout(() => startTour(tourId), 500);
}
}, []);
Admin Overrides (app_config)
Two app_config keys control dynamic behavior:
| Key | Shape | Purpose |
|---|
onboarding_showcase_images | { "tour-id": { "step-id": "https://url" } } | Showcase step images |
onboarding_guided_overrides | { "tour-id": { "step-id": { placement?, align?, sideOffset? } } } | Guided tooltip positioning |
Admin panel at /ohjaamo/onboarding provides UI for both.
Supabase Storage
Bucket: onboarding-images (public read, authenticated write)
Path pattern: onboarding/{tourId}/{stepId}.webp
Key Types
type TourMode = "showcase" | "guided";
type Placement = "top" | "bottom" | "left" | "right";
type Align = "start" | "center" | "end";
interface OnboardingStep {
id: string;
title: string;
description: string;
image?: string;
target?: string;
placement?: Placement;
align?: Align;
sideOffset?: number;
cta?: { label: string; href?: string; action?: () => void };
beforeStep?: () => void | Promise<void>;
}
interface { : ; : ; : [] }
Resolution Pipeline
useAllTours() in tours/index.ts resolves tours at runtime:
- i18n: Translation keys resolved via
useTranslation("common")
- Images:
useOnboardingImages(tourId) merges app_config images into showcase steps
- Overrides:
useGuidedOverrides() merges app_config placement/align/sideOffset into guided steps
Code defaults are always fallback — admin overrides win when present.
State Machine
idle → START(tourId) → active { tourId, stepIndex: 0 }
→ NEXT → stepIndex++ (or COMPLETE if last)
→ PREV → stepIndex-- (min 0)
→ SKIP/COMPLETE → completed { tourId } → idle
Completion persisted to localStorage: onboarding_completed:{tourId}.
References
- architecture.md — Complete file map, data flow, existing tours, React Query keys