Onboarding & Malet Creation Architecture
The Mallnline onboarding system is a guided "first visit to first value" flow that segments users by intent and routes them to the right platform vertical. For Malet Owners, this extends into a 5-step Malet creation wizard that transforms intent into a live storefront. This document covers the architecture, components, state management, and extension patterns.
Overview
Every new visitor to Mallnline arrives at the Lobby (/lobby), where an OnboardingHero presents two primary intents: Buy or Sell. Clicking either launches the /onboarding wizard, which walks the user through persona identification, interest/offering selection, and (for business owners) reach sizing. The final step delivers a personalized recommendation with direct CTAs.
User Journey
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ LOBBY (/lobby) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ OnboardingHero (dual-mode) โ โ
โ โ Anonymous: gradient mesh + buy/sell CTAs โ โ
โ โ Authenticated: greeting + quick-access โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ IntentBar (quick-actions) โ โ
โ โ Browse โ Book โ Explore โ Search โ Business โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ... feeds, products, floors ... โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Bottom CTA: "Take the Guided Tour" โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WIZARD (/onboarding) โ
โ Step 1: Who are you? (Visitor/Murchaser/Owner) โ
โ Step 2: What are you looking for? / offering? โ
โ Step 3: What's your reach? (owners only) โ
โ Step 4: Here's your path (recommendation) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Components
OnboardingHero
Path: src/lib/components/onboarding/OnboardingHero.svelte
A dual-mode hero component that adapts based on authentication:
| Mode | Content |
|---|---|
| Anonymous | Animated gradient mesh blobs, "Discover your next favorite" heading, "I'm Looking to Buy" / "I'm Looking to Sell" CTAs, "Just browsing?" hint |
| Authenticated | Time-of-day greeting (Good morning/afternoon/evening), quick-access action buttons (Orders, Manage Malet, Create Malet, Search) |
Props:
isAuthenticated: booleanโ Whether the user is logged inuserName: stringโ Display name for greetinghasMalets: booleanโ Whether user owns any Malets (shows manage vs create CTA)
OnboardingWizard
Path: src/lib/components/onboarding/OnboardingWizard.svelte
A reusable step wizard shell with:
- Animated gradient progress bar
- Dot step indicator (active dot extends width)
- CSS-only slide-in animations (forward/backward direction)
- Back / Continue / Get Started navigation
- "Skip for now" link
Props:
currentStep,totalSteps,progressโ Progress trackingcanAdvance,canGoBack,isLastStepโ Navigation controlonAdvance,onBack,onCompleteโ CallbacksstepContent: Snippetโ Svelte 5 Snippet for current step content
PersonaCard
Path: src/lib/components/onboarding/PersonaCard.svelte
Selectable card with glassmorphism border on selection, animated checkmark badge, and hover-scale icon. Renders an icon, label, and description.
IntentBar
Path: src/lib/components/onboarding/IntentBar.svelte
Horizontal scrolling quick-action bar with gradient icon backgrounds and staggered reveal animation. Supports snap-scroll on mobile and centered wrap on desktop.
State Management
OnboardingState
Path: src/stores/onboardingStore.svelte.ts
A Svelte 5 reactive class (using $state() and getter-based derived) that manages:
class OnboardingState {
persona: Persona | null // 'visitor' | 'murchaser' | 'owner'
interest: MurchaserInterest | null // 'products' | 'services' | 'events' | 'not_sure'
offering: OwnerOffering | null // 'physical' | 'digital' | 'professional' | 'events_exp' | 'other'
reach: BusinessReach | null // 'solo' | 'small_team' | 'organization'
step: number // Current wizard step (0-based)
// Derived
get totalSteps() // Adapts: visitor=2, murchaser=3, owner=4
get progress() // Percentage (0-100)
get canAdvance() // True when current step's selection is made
get canGoBack() // True when step > 0
get isLastStep() // True at final step
get recommendedVerticals() // Maps selections to vertical catalog entries
// Actions
advance() / back() / reset() // Step navigation
complete() // Sets localStorage completion flag
seedFromIntent(intent) // Pre-fills persona from URL ?intent= param
}
Exported singleton: export const onboarding = new OnboardingState()
Persistence
localStorage('mall_onboarding_done')โ Returning users bypass onboarding- URL seeding โ
?intent=murchaseror?intent=ownerpre-fills persona to skip Step 1
Wizard Route
Path: src/routes/onboarding/+page.svelte
The wizard page uses the OnboardingWizard shell with a {#snippet stepContent()} that conditionally renders each step based on onboarding.step.
Step Flow by Persona
| Persona | Step 0 | Step 1 | Step 2 | Step 3 |
|---|---|---|---|---|
| Visitor | Who? | Recommendation | โ | โ |
| Murchaser | Who? | What interests you? | Recommendation | โ |
| Owner | Who? | What do you offer? | What's your reach? | Recommendation |
Recommendation Panel
The final step renders:
- A "Perfect match" badge
- Recommended vertical chips (from
onboarding.recommendedVerticals) - Primary CTA (e.g., "Browse Products" for murchasers, "Explore For Business" for owners)
- Secondary CTA ("Back to Lobby")
Data Strategy
Current (Static Demo)
Vertical catalog and recommendation mapping are hardcoded in onboardingStore.svelte.ts. This enables prototyping without backend dependencies.
Production Path
Replace static mappings with GraphQL queries:
// Future: wire to real data
get recommendedVerticals() {
// Query: verticals(filter: { interests: $interest }) { name, slug, maletCount }
return this.queryVerticals(this.interest);
}
Extending the Wizard
Adding a New Step
- Increment logic in
get totalSteps()onOnboardingState - Add state field (e.g.,
budget = $state<Budget | null>(null)) - Add step rendering in the
{#snippet stepContent()}block - Add
canAdvancelogic for the new step
Adding a New Persona
- Extend the
Personatype union - Add
PersonaCardin Step 0 - Define step flow in
get totalSteps() - Add recommendation mapping in
get recommendedVerticals()
Related
- Mall Sections & Taxonomy โ How verticals are organized
- Tag Registry โ Auto-registering tag system used by the creation wizard
- E2E Testing Infrastructure โ Playwright tests covering the Malet creation wizard
- Component Library โ Reusable UI patterns
- Settings Architecture โ Modular component patterns
- Management Dashboard โ Malet Owner admin interfaces
- Owner Destinations โ
/owner/registeronboarding funnel with dual-path Solo Creator / Business & Team chooser routing into the creation wizard - Frontend Debugging Playbook โ Diagnosing silent rendering failures in wizard components
Malet Creation Wizard
The Malet Creation Wizard is a 5-step flow at /create-malet that transforms the onboarding intent into a live Malet. It is the primary path for new Malet Owners arriving from the /for-business page or the onboarding wizard's "Sell" CTA.
User Journey
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WIZARD (/create-malet) โ
โ Step 1: Vertical Picker (multi-select) โ
โ Step 2: Identity (name, handle, tagline, email) โ
โ Step 3: Vertical Config + Discovery Tags โ
โ Step 4: Tier Selection (Starter/Pro/Enterprise) โ
โ Step 5: Review & Launch โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Entry Points
| Source | Behavior |
|---|---|
/for-business pricing CTAs |
Links to /create-malet?tier=starter|pro|enterprise โ pre-selects tier |
/profile page |
Redirects to /create-malet (legacy form removed) |
| OnboardingHero authenticated | "Create Malet" quick-action CTA |
Components
VerticalPicker
Path: src/lib/components/create-malet/VerticalPicker.svelte
Multi-select vertical card grid. Users can toggle multiple categories to create a multi-vertical Malet. Shows a "Multi-vertical" hint when 2+ are selected. Includes a "Not sure yet" skip option.
IdentityForm
Path: src/lib/components/create-malet/IdentityForm.svelte
Name, handle (with real-time debounced availability check via checkHandleAvailability query), tagline, and contact email fields. Handle is auto-slugified from the name with manual override.
VerticalConfig
Path: src/lib/components/create-malet/VerticalConfig.svelte
Dynamic config fields driven by the selected vertical(s) โ select, multi-select, toggle, and text inputs. Below the config fields, a Discovery Tags section offers curated tag suggestions (from SECTION_TAGS) plus a custom tag input with slug format validation. Custom tags are auto-registered in the backend registry.
TierPicker
Path: src/lib/components/create-malet/TierPicker.svelte
Three-tier pricing cards (Starter free / Pro $29/mo / Enterprise custom) with feature comparison. The ?tier= URL param auto-selects a tier on entry.
ReviewLaunch
Path: src/lib/components/create-malet/ReviewLaunch.svelte
Summary card with gradient header, vertical icon, handle preview, plan badge, and tags list. Handles the createOneMalet mutation, assigns owner role, and navigates to the new Malet on success.
State Management
CreateMaletState
Path: src/stores/createMaletStore.svelte.ts
A Svelte 5 reactive class (using $state() and getter-based derived) that manages the entire wizard flow:
class CreateMaletState {
step: number; // Current wizard step (0-4)
name: string; // Malet display name
handle: string; // Malet URL handle (m|handle)
handleAvailable: boolean | null; // Real-time availability check result
tagline: string; // Short description
contactEmail: string; // Contact email
verticalTypes: string[]; // Multi-vertical selection (array)
sectionTags: string[]; // Discovery tags (curated + custom)
verticalConfig: Record<string, unknown>; // Per-vertical config fields
tier: 'starter' | 'pro' | 'enterprise'; // Selected pricing tier
submitting: boolean; // Submission loading state
error: string; // Error message
// Derived
get selectedSection(); // First selected vertical's section metadata
get selectedTier(); // Full tier config object
get verticalConfigFields(); // Merged config fields from all selected verticals
get previewUrl(); // Full Malet URL preview
get canAdvanceStep(); // Validation gate per step
// Actions
toMaletInput(); // Builds CreateMaletInput payload
checkHandleAvailability(); // Debounced GraphQL availability check
reset(); // Resets all state to defaults
}
Exported singleton: export const createMaletState = new CreateMaletState()
Multi-Vertical Support
Malets can belong to multiple verticals simultaneously. This is a key architectural feature:
- Backend:
Malet.verticalTypes: string[]โ first entry is the primary vertical,verticalTypeis kept for backwards compatibility - Frontend:
VerticalPickertoggles entries in the array;VerticalConfigmerges config fields from all selected verticals (deduped by key) - Discovery: Multi-vertical Malets appear in every section they belong to
Open Tag System
The discovery tags in Step 3 use a suggest-and-extend model:
- Curated suggestions from
SECTION_TAGSare shown as toggleable chips grouped by category - Custom tag input allows Malet Owners to type their own tags (slug format:
lowercase-with-hyphens, max 40 chars) - Backend auto-registration:
SectionTagService.validateAndRegister()creates newSectionTagrecords for valid custom slugs withcategory: "User" - Race-condition safe: Duplicate key errors are silently ignored (another request may have registered the same tag)
See Tag Registry for full details.
Related
- Malet-First Ownership Architecture โ Payment-first provisioning for Pro/Enterprise tiers with atomic Organization + Malet creation
- Org Subscription Bootstrap & Ownership Picker โ Auto-provisioning of Starter subscriptions and the OwnershipPicker component
- Mall Sections & Taxonomy โ Vertical taxonomy and section configuration
- User Guide: Creating Your Malet โ User-facing guide to the creation wizard