docs(07): UI design contract for setup wizard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
374
.planning/phases/07-setup-wizard/07-UI-SPEC.md
Normal file
374
.planning/phases/07-setup-wizard/07-UI-SPEC.md
Normal 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
|
||||||
Reference in New Issue
Block a user