Agent Skills: Onboarding Architect

>

UncategorizedID: Spectaculous-Code/raamattu-nyt/onboarding-architect

Install this agent skill to your local

pnpm dlx add-skill https://github.com/Spectaculous-Code/raamattu-nyt/tree/HEAD/.claude/skills/onboarding-architect

Skill Files

Browse the full folder contents for onboarding-architect.

Download Skill

Loading file tree…

.claude/skills/onboarding-architect/SKILL.md

Skill Metadata

Name
onboarding-architect
Description
>

Onboarding Architect

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

  1. 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,
      title: tr(def.titleKey),
      description: tr(def.descriptionKey),
      image: images?.[def.id],
      cta: def.ctaKey ? { label: tr(def.ctaKey), href: def.ctaHref } : undefined,
    })),
  };
}

export const myTour: Tour = createMyTour();
  1. Add i18n keys to public/locales/fi/common.json and public/locales/en/common.json
  2. Register in tours/index.ts — import, add to useAllTours() return array
  3. Upload images via admin panel or set in app_config key onboarding_showcase_images

Adding a New Guided Tour

  1. 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,
    },
  ],
};
  1. Add data-onboard="my-first-element" attribute to target DOM elements
  2. Add i18n keys to locale files
  3. 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

// packages/shared-onboarding/src/types.ts
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;           // Showcase only
  target?: string;          // Guided only: CSS selector
  placement?: Placement;    // Guided only
  align?: Align;            // Guided only
  sideOffset?: number;      // Guided only (default 12)
  cta?: { label: string; href?: string; action?: () => void };
  beforeStep?: () => void | Promise<void>;
}

interface Tour { id: string; mode: TourMode; steps: OnboardingStep[] }

Resolution Pipeline

useAllTours() in tours/index.ts resolves tours at runtime:

  1. i18n: Translation keys resolved via useTranslation("common")
  2. Images: useOnboardingImages(tourId) merges app_config images into showcase steps
  3. 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