docs(07): UI design contract for setup wizard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 20:26:45 +02:00
parent fd068fb552
commit 272af4ec98

View File

@@ -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<string, { checked: boolean, amount: number }>,
// 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