Files
portabase/docs/superpowers/specs/2026-06-19-onboarding-theme-progress-design.md
T

3.7 KiB

Onboarding — Theme Persistence & Progress Bar Fix

Date: 2026-06-19
Scope: src/features/onboarding
Status: Approved


Problem Statement

Two independent bugs in the onboarding flow:

  1. Theme not persisted to DB — selecting a theme in StepPreferences applies it visually via next-themes but never calls authClient.updateUser({ theme }), so the preference is lost on page reload unless the user also saves an avatar.

  2. Progress bar starts from current step, not from the beginningOnboardingStepper uses state.progressPercentage, state.currentStepNumber, and state.totalSteps from OnboardJS. When onboarding resumes mid-flow (e.g. at step invite-members), OnboardJS recalculates these values relative to the remaining steps in the current session, not relative to the full 15-step flow. This makes the bar start at 0% even though the user is already 40%+ through.


Fix 1 — Theme Persistence

File: src/features/onboarding/steps/step-preferences.tsx

Current behaviour

const selectTheme = async (theme: ThemeKey) => {
  setTheme(theme);          // ✅ updates next-themes (UI)
  // mettre à jour aussi en db!  ← comment acknowledging the bug
  await updateContext({ ... });
};

Target behaviour

const selectTheme = async (theme: ThemeKey) => {
  setTheme(theme);
  await authClient.updateUser({ theme });   // ← add this line
  await updateContext({ ... });
};

Reference: src/features/profile/profile-appearance.tsx already does this correctly.

Error handling: No change — if updateUser fails, the UI theme is still applied and the user can continue. No toast/error needed at this stage.


Fix 2 — Progress Bar Recalculation

File: src/features/onboarding/onboarding-stepper.tsx

Root cause

OnboardJS computes progressPercentage and currentStepNumber relative to the steps it was initialised with in the current session. When resuming mid-flow, the step list seen by OnboardJS starts at the resume step, not at login.

Solution

Replace all OnboardJS progress values with a manual calculation based on STEP_ORDER (the canonical ordered list of all 15 steps, already defined in src/features/onboarding/constants/steps.ts).

import { STEP_ORDER } from "@/features/onboarding/constants/steps";

const currentId = String(state?.currentStep?.id ?? "");
const currentIndex = Math.max(0, STEP_ORDER.indexOf(currentId)); // guard: -1 → 0
const totalSteps = STEP_ORDER.length;                             // 15
const stepNumber = currentIndex + 1;                              // 1-based
const progress = Math.round((currentIndex / (totalSteps - 1)) * 100);
Step Index stepNumber progress
login 0 1 of 15 0%
preferences 3 4 of 15 21%
invite-members 5 6 of 15 36%
storage 7 8 of 15 50%
finish 14 15 of 15 100%

Edge case: If currentId is not found in STEP_ORDER, indexOf returns -1, clamped to 0 by Math.max → shows step 1 of 15 at 0%. Safe degradation.


Files Changed

File Change
src/features/onboarding/steps/step-preferences.tsx Add authClient.updateUser({ theme }) in selectTheme
src/features/onboarding/onboarding-stepper.tsx Replace OnboardJS progress values with STEP_ORDER-based calculation

No new files, no schema changes, no API changes.


Out of Scope

  • Adding a toast/error on failed theme save
  • Persisting the avatar selection to DB during selection (only on "Continue" — existing behaviour unchanged)
  • Refactoring OnboardJS step initialisation