From 272af4ec98c63dafe94b4486c06233c2d53260ee Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 20 Apr 2026 20:26:45 +0200 Subject: [PATCH] docs(07): UI design contract for setup wizard Co-Authored-By: Claude Sonnet 4.6 --- .../phases/07-setup-wizard/07-UI-SPEC.md | 374 ++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 .planning/phases/07-setup-wizard/07-UI-SPEC.md diff --git a/.planning/phases/07-setup-wizard/07-UI-SPEC.md b/.planning/phases/07-setup-wizard/07-UI-SPEC.md new file mode 100644 index 0000000..bcceda0 --- /dev/null +++ b/.planning/phases/07-setup-wizard/07-UI-SPEC.md @@ -0,0 +1,374 @@ +--- +phase: 7 +slug: setup-wizard +status: draft +shadcn_initialized: true +preset: new-york +created: 2026-04-20 +--- + +# Phase 7 — UI Design Contract + +> Visual and interaction contract for the 3-step setup wizard. Generated by gsd-ui-researcher, verified by gsd-ui-checker. + +--- + +## Design System + +| Property | Value | +|----------|-------| +| Tool | shadcn (new-york style) | +| Preset | new-york, baseColor neutral, cssVariables true | +| Component library | Radix UI (via shadcn/ui) | +| Icon library | Lucide | +| Font | Inter, ui-sans-serif, system-ui, sans-serif | + +--- + +## Spacing Scale + +Declared values (must be multiples of 4): + +| Token | Value | Usage | +|-------|-------|-------| +| xs | 4px | Icon gaps, inline padding | +| sm | 8px | Compact element spacing, checkbox-to-label gap | +| md | 16px | Default element spacing, card internal padding | +| lg | 24px | Section padding, stepper step gaps | +| xl | 32px | Layout gaps, card content vertical spacing | +| 2xl | 48px | Step card top/bottom padding | +| 3xl | 64px | Page-level vertical centering offset | + +Exceptions: Sticky allocation bar uses 12px vertical padding (py-3) for visual compactness within the card. + +--- + +## Typography + +| Role | Size | Weight | Line Height | +|------|------|--------|-------------| +| Body | 14px | 400 (regular) | 1.5 | +| Label | 14px | 500 (medium) | 1.4 | +| Heading | 20px | 600 (semibold) | 1.2 | +| Display | 28px | 600 (semibold) | 1.2 | + +Usage in wizard: +- **Display (28px/600):** Wizard page title "Set up your budget" (step 1 heading area) +- **Heading (20px/600):** Step card titles ("Monthly Income", "Recurring Items", "Review") +- **Label (14px/500):** Category group headers (e.g., "Bills", "Variable Expenses"), stepper step labels, "Remaining to allocate" label, form field labels +- **Body (14px/400):** Preset item names, amounts, helper text, step descriptions + +--- + +## Color + +| Role | Value | Usage | +|------|-------|-------| +| Dominant (60%) | `oklch(0.98 0.01 260)` (--background) | Page background behind wizard card | +| Secondary (30%) | `oklch(1 0 0)` (--card) | Wizard card surface, review summary card | +| Accent (10%) | `oklch(0.55 0.15 260)` (--primary) | Active stepper step indicator, primary CTA buttons, step number circles (active) | +| Destructive | `oklch(0.6 0.2 25)` (--destructive) | Negative "remaining to allocate" text, validation error text | + +Accent reserved for: +- Active step circle in the stepper (filled primary background) +- "Next" and "Complete Setup" primary buttons +- Income input focus ring +- Completed step checkmark icon color + +Additional semantic colors used (existing tokens): +- `--color-income` (`oklch(0.55 0.17 155)`): Income category group header dot and badge +- `--color-bill` (`oklch(0.55 0.17 25)`): Bill category group header dot and badge +- `--color-variable-expense` (`oklch(0.58 0.16 50)`): Variable expense category group header dot and badge +- `--color-debt` (`oklch(0.52 0.18 355)`): Debt category group header dot and badge +- `--color-saving` (`oklch(0.55 0.16 220)`): Saving category group header dot and badge +- `--color-investment` (`oklch(0.55 0.16 285)`): Investment category group header dot and badge +- `--color-on-budget` (`oklch(0.50 0.17 155)`): Positive "remaining to allocate" text (green) + +--- + +## Component Inventory + +### New shadcn Components to Install + +| Component | Purpose | +|-----------|---------| +| `checkbox` | Preset item selection in step 2 | + +### Existing Components Used + +| Component | Where | +|-----------|-------| +| `Card`, `CardHeader`, `CardContent` | Wizard step container, review summary | +| `Button` | Next, Back, Skip, Skip setup, Complete Setup | +| `Input` | Income amount (step 1), per-item amount editing (step 2) | +| `Badge` | Category type indicators next to preset items | +| `Label` | Form field labels | +| `Separator` | Between category groups in step 2 and review step | + +### Custom Components to Build + +| Component | Description | +|-----------|-------------| +| `SetupWizard` | Page-level orchestrator: holds wizard state, renders stepper + active step | +| `WizardStepper` | Horizontal numbered stepper bar (1-2-3) with clickable completed steps | +| `IncomeStep` | Step 1: single income input with currency indicator | +| `RecurringItemsStep` | Step 2: grouped checklist of 19 presets with editable amounts | +| `ReviewStep` | Step 3: read-only summary of selections | +| `AllocationBar` | Sticky bar showing "Remaining to allocate" with live calculation | +| `PresetItemRow` | Single checklist row: checkbox + name + category badge + amount input | +| `CategoryGroupHeader` | Section header with colored dot + category type label + item count | + +--- + +## Layout Contract + +### Page Shell + +``` +Full viewport height, centered content: +- Container: flex min-h-screen items-center justify-center bg-background p-4 +- No sidebar, no app nav — standalone page like login/register +- Content wrapper: w-full max-w-2xl (672px) with vertical flex layout +``` + +### Stepper Bar + +``` +Position: Above the card, inside the content wrapper +Layout: flex items-center justify-center gap-8 +Each step: + - Circle: 32px (w-8 h-8), centered number text (14px/500) + - Active: bg-primary text-primary-foreground + - Completed: bg-primary text-primary-foreground with Check icon (16px) + - Upcoming: bg-muted text-muted-foreground border border-border + - Connector line between steps: h-px w-16 bg-border (completed: bg-primary) + - Step label below circle: text-xs font-medium text-muted-foreground (active: text-foreground) +Clickable: Completed steps and current step only (not future steps) +Margin below stepper: 24px (mb-6) before the card +``` + +### Step Card + +``` +Card: w-full border border-border shadow-sm +CardHeader: pb-2 + - Step title: heading (20px/600) + - Step description: body (14px/400) text-muted-foreground, 1 line +CardContent: space-y-6 + +Bottom navigation row (inside CardContent, at the bottom): + - flex justify-between items-center pt-4 border-t border-border + - Left: Back button (variant="ghost", hidden on step 1) + Skip button (variant="ghost", text-muted-foreground) + - Right: Next button (variant="default", primary) or Complete Setup button (step 3) +``` + +### Step 1 — Income + +``` +Card content: + - Label: "Monthly net income" (14px/500) + - Input row: flex items-center gap-2 + - Input: type="number", w-full, text-right, text-lg (18px), pre-filled "3000" + - Currency suffix: text-muted-foreground text-sm, shows profile currency (e.g., "EUR") + - Helper text below: "Enter your total monthly take-home pay" (14px/400, text-muted-foreground) + - Validation: if empty or <= 0 on Next click, show destructive text below input: "Please enter a positive income amount" +``` + +### Step 2 — Recurring Items + +``` +Allocation bar (sticky): + - Position: sticky top-0 z-10, inside the card content area + - Layout: flex justify-between items-center py-3 px-4 bg-muted border-b border-border + - Left: "Remaining to allocate" (14px/500) + - Right: formatted amount (16px/600) + - Positive or zero: text-on-budget (green) + - Negative: text-destructive (red) + - Updates live on every check/uncheck and amount edit + +Category groups (6 groups, ordered: income, bill, variable_expense, debt, saving, investment): + - CategoryGroupHeader: flex items-center gap-2 pt-4 pb-2 + - Colored dot: w-2.5 h-2.5 (10px) circle using category color token + - Group label: label (14px/500) using categoryLabels from palette.ts + - Item count badge: text-xs text-muted-foreground "(4 items)" + - Separator below header: border-border + + PresetItemRow (per item): + - Layout: flex items-center gap-3 py-2.5 px-1 + - Checkbox: shadcn checkbox (16px square, sharp corners via radius-0) + - Item name: body (14px/400), flex-1 + - Uses i18n key: t(`presets.${type}.${slug}`) + - Category badge: Badge variant="outline", text-xs, styled with category color border-left (3px) + - Amount input: w-24 text-right, type="number" + - Enabled only when checkbox is checked + - Disabled state: bg-muted text-muted-foreground opacity-50 + - Pre-filled with preset defaultAmount + +Default checked state: + - bill (4 items): ALL checked + - variable_expense (5 items): ALL checked + - income, debt, saving, investment: ALL unchecked +``` + +### Step 3 — Review + +``` +Card content: + - Read-only summary, no editable fields + - Income row: flex justify-between py-2 + - "Monthly income" (14px/500) + - Formatted amount (14px/600) + - Separator + - Selected items grouped by category type (same order as step 2) + - CategoryGroupHeader (same component, no checkbox) + - Per item: flex justify-between py-1.5 px-1 + - Item name (14px/400) + - Amount (14px/400, text-right) + - Separator + - Totals section: space-y-1 pt-2 + - "Total expenses" row: flex justify-between, 14px/500 + - "Remaining" row: flex justify-between, 16px/600 + - Positive: text-on-budget + - Negative: text-destructive + + - If no items selected: show muted message "No items selected. You can add items to your template later." +``` + +### Skip Controls + +``` +Per-step skip: Ghost button "Skip" in the bottom-left nav area + - Advances to next step without saving current step data + - On step 3 skip: same as "Skip setup" (exits wizard) + +Global skip: "Skip setup" link/button + - Position: below the card, centered, text-sm text-muted-foreground underline + - Margin top: 16px below card + - Action: clears localStorage, sets profiles.setup_completed = true, redirects to dashboard + - No confirmation dialog (non-destructive — user can still manually set up template) +``` + +--- + +## Interaction Contract + +### Step Navigation + +| Action | Behavior | +|--------|----------| +| Click "Next" (step 1) | Validate income > 0. If valid, transition to step 2. If invalid, show inline error. | +| Click "Next" (step 2) | No validation required (0 items selected is valid). Transition to step 3. | +| Click "Back" | Return to previous step. Preserve all entered data. | +| Click stepper circle (completed) | Navigate to that step. Preserve all data. | +| Click stepper circle (future) | No action. Cursor: default. | +| Click "Complete Setup" (step 3) | Create categories + template items via API. On success: clear localStorage, set setup_completed=true, redirect to dashboard with toast. | +| Click "Skip" (per-step) | Advance to next step without current step data. Step 1 skip: income stays at default or last entered value. Step 2 skip: uncheck all items. | +| Click "Skip setup" (global) | Exit wizard entirely. Clear localStorage. Mark setup_completed=true. Redirect to dashboard. No toast. | +| Page refresh mid-wizard | Restore wizard at the same step with all entered data from localStorage. | + +### State Persistence (localStorage) + +``` +Key: `setup-wizard-${userId}` +Value: JSON object +{ + currentStep: 1 | 2 | 3, + income: number, + selectedItems: Record, + // keyed by preset slug +} +Cleared on: wizard completion OR skip setup +``` + +### Loading & Error States + +| State | Behavior | +|-------|----------| +| Wizard loading (useFirstRunState pending) | Show centered Skeleton matching card dimensions (max-w-2xl, h-64) | +| Completion API in progress | "Complete Setup" button shows spinner + disabled. Back/Skip also disabled. | +| Completion API failure | Toast (sonner, variant destructive): "Could not save your template. Please try again." Button re-enables. Data preserved. | +| Partial completion failure | If categories created but template items fail: toast with "Some items could not be saved. Check your template page." Redirect to dashboard anyway. | + +### Transitions Between Steps + +No animated transitions between steps. Instant swap of step content within the card. The stepper bar updates synchronously. + +--- + +## Copywriting Contract + +All copy must have i18n keys in both `en.json` and `de.json`. Keys live under a `setup` namespace. + +| Element | EN Copy | i18n Key | +|---------|---------|----------| +| Page title (step 1) | Set up your budget | `setup.title` | +| Step 1 title | Monthly Income | `setup.step1.title` | +| Step 1 description | How much do you earn each month? | `setup.step1.description` | +| Step 1 label | Monthly net income | `setup.step1.incomeLabel` | +| Step 1 helper | Enter your total monthly take-home pay | `setup.step1.helper` | +| Step 1 validation | Please enter a positive income amount | `setup.step1.validation` | +| Step 2 title | Recurring Items | `setup.step2.title` | +| Step 2 description | Select your regular monthly expenses | `setup.step2.description` | +| Allocation bar label | Remaining to allocate | `setup.step2.remaining` | +| Step 3 title | Review | `setup.step3.title` | +| Step 3 description | Confirm your budget template | `setup.step3.description` | +| Step 3 income label | Monthly income | `setup.step3.incomeLabel` | +| Step 3 total label | Total expenses | `setup.step3.totalLabel` | +| Step 3 remaining label | Remaining | `setup.step3.remainingLabel` | +| Step 3 empty | No items selected. You can add items to your template later. | `setup.step3.empty` | +| Stepper labels | Income / Items / Review | `setup.steps.1` / `setup.steps.2` / `setup.steps.3` | +| Next button | Next | `setup.next` | +| Back button | Back | `setup.back` | +| Skip button | Skip | `setup.skip` | +| Skip setup link | Skip setup | `setup.skipSetup` | +| Complete button | Complete Setup | `setup.complete` | +| Success toast | Template created! Your first budget will appear automatically. | `setup.toast.success` | +| Error toast | Could not save your template. Please try again. | `setup.toast.error` | +| Partial error toast | Some items could not be saved. Check your template page. | `setup.toast.partialError` | + +--- + +## Registry Safety + +| Registry | Blocks Used | Safety Gate | +|----------|-------------|-------------| +| shadcn official | checkbox | not required | + +No third-party registries declared. + +--- + +## Accessibility Contract + +| Concern | Implementation | +|---------|----------------| +| Stepper semantics | `role="navigation"` with `aria-label="Setup progress"`. Each step: `role="tab"`, `aria-selected` for active, `aria-disabled` for future. | +| Step content | `role="tabpanel"` with `aria-labelledby` pointing to the active step tab. | +| Checkbox group | Each category group is a `fieldset` with `legend` (visually styled as CategoryGroupHeader). | +| Income input | `aria-describedby` pointing to helper text and validation error (when shown). | +| Allocation bar | `aria-live="polite"` so screen readers announce remaining amount changes. | +| Skip links | Visible, keyboard-focusable. Not hidden behind hover. | +| Focus management | On step transition, focus moves to the step card heading. | + +--- + +## Responsive Behavior + +| Breakpoint | Behavior | +|------------|----------| +| >= 768px (md) | max-w-2xl card centered, stepper horizontal with labels below circles | +| < 768px (sm) | Card becomes full-width (mx-4). Stepper collapses: show circles only, hide step labels. Amount inputs remain w-24. | +| < 480px (xs) | PresetItemRow: badge hidden, amount input w-20. Category group headers wrap naturally. | + +--- + +## Checker Sign-Off + +- [ ] Dimension 1 Copywriting: PASS +- [ ] Dimension 2 Visuals: PASS +- [ ] Dimension 3 Color: PASS +- [ ] Dimension 4 Typography: PASS +- [ ] Dimension 5 Spacing: PASS +- [ ] Dimension 6 Registry Safety: PASS + +**Approval:** pending