docs: complete project research

This commit is contained in:
2026-03-11 18:51:09 +01:00
parent fd3be097b7
commit 30424adfb3
5 changed files with 1328 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
# Project Research Summary
**Project:** SimpleFinanceDash — UI Polish Milestone
**Domain:** Personal finance dashboard (pastel spreadsheet aesthetic)
**Researched:** 2026-03-11
**Confidence:** HIGH
## Executive Summary
SimpleFinanceDash is a functional, fully-deployed personal finance dashboard built on Go + React + shadcn/ui. The backend is complete. This milestone is a pure frontend polish pass: making an app that already works look and feel intentionally designed. The core problem is that the existing UI has all the structural scaffolding for a pastel theme — correct gradient classes on card headers, a Tailwind 4 `@theme inline` token bridge, and shadcn components that read from CSS variables — but the CSS variables themselves have zero chroma (neutral grey/white values). A single targeted change to `index.css` unlocks the entire visual identity across every shadcn component simultaneously.
The recommended approach is strictly layered: define the pastel token system first (Phase 1), then polish structural layout surfaces (Phase 2), then address interaction quality and empty states (Phase 3), then finalize charts (Phase 4). This order is non-negotiable because later phases depend on earlier ones — chart colors must reference the semantic token system, and component polish cannot happen consistently until shared components (InlineEditCell, StatCard, EmptyState) are extracted first. Skipping the token foundation and going component-by-component is the most common failure mode for this type of polish project.
The key risk is color fragmentation: the codebase already has three conflicting color systems (hardcoded hex in Recharts fills, raw Tailwind palette classes like `bg-emerald-50`, and the CSS variable token system). If the pastel token system is added as a fourth system without migrating the existing approaches, the app will look inconsistent regardless of effort spent. The mitigation is a `lib/palette.ts` file as the single source of truth, and a strict rule that no color value other than a CSS variable reference belongs in a component file.
---
## Key Findings
### Recommended Stack
The stack is already locked — no new dependencies are required for the core milestone. All tools are present: React 19 + TypeScript 5, Tailwind CSS 4.2.1, shadcn/ui 4.0.0 (on the new single `radix-ui` package architecture), Recharts 2.15.4, tw-animate-css, lucide-react, and Geist Variable font. The one optional addition is `@fontsource-variable/geist-mono` for tabular currency numbers (one `bun add` command).
The project is on the new shadcn v4 architecture which uses `@theme inline` in CSS and `oklch` color space. This is the correct foundation for a pastel palette — oklch's independent chroma channel (C) makes pastel generation straightforward: high L, low C values produce clean pastels without the perceptual distortion of HSL.
**Core technologies:**
- `Tailwind CSS 4.2.1`: Utility styling via `@theme inline` — all tokens live in `index.css`, zero `tailwind.config.js` needed
- `shadcn/ui 4.0.0`: Component library that reads 100% from CSS variables — theme changes flow through tokens automatically
- `radix-ui 1.4.3`: Single unified package (not individual `@radix-ui/*`); mixing them causes version conflicts
- `Recharts 2.15.4`: Charts via shadcn's `ChartContainer` wrapper; `fill` props require string values, not CSS variables — requires `getComputedStyle` or a `lib/palette.ts` constant map
- `tw-animate-css 1.4.0`: Already imported in `index.css`; sufficient for all loading states and micro-interactions without Framer Motion
- `lucide-react 0.577.0`: Icon library matching shadcn's stroke-width style; consistent `size={16}` / `size={20}` discipline needed
- Geist Variable (already installed): Single typeface; do not add a second. Geist Mono companion is the only optional addition.
### Expected Features
The FEATURES.md analysis is based on direct codebase inspection and identifies a concrete, ordered list of gaps.
**Must have (table stakes):**
- Pastel CSS variable system — all other polish depends on it; currently `--primary` and `--accent` have zero chroma
- Branded login/register background — first impression is white card on white screen
- App wordmark/logo treatment on login and sidebar — no visual identity currently
- Active nav item color — `isActive` is wired but unstyled due to missing `--sidebar-primary` value
- Card hover states — `hover:bg-muted` is invisible because `--muted` has no chroma
- Loading spinner on form submit buttons — `disabled` state only, no visual motion
- Page-level loading skeletons styled to match section colors
- Empty state for first-time dashboard (no budgets) and CategoriesPage
- Consistent section header palette — currently each card picks ad-hoc gradient colors
- Positive/negative amount coloring applied uniformly across all tables
**Should have (differentiators):**
- Pencil icon affordance on inline-editable rows (currently only a background hover hint)
- Save confirmation flash after inline edit (brief green background, 100ms)
- Chart tooltips formatted with `formatCurrency` (currently raw numbers)
- Budget health badge (green/amber/red) near the budget selector
- Donut chart center label showing month + year context
- Month navigator (prev/next arrows) alongside budget selector
- Progress bars in BillsTracker for actual vs. budgeted amount
- Collapsible sidebar toggle for smaller screens
**Defer to v2+:**
- Dark mode pastel palette — define light mode fully first; dark pastel is a distinct design problem
- Custom color picker / theme selector — out of scope per PROJECT.md
- Route-level animated page transitions — adds Framer Motion weight for marginal benefit
- Toast notifications for saves — noisy in a dashboard with rapid sequential edits; use contextual row flash instead
- Drag-to-reorder categories on dashboard
- New chart types (multi-month trend, sparklines)
- Onboarding wizard / guided tour
- Category creation inline from dashboard — high complexity, separate milestone
### Architecture Approach
The design system follows a strict three-tier layered architecture: Token layer (all CSS custom properties in `index.css`) → Variant layer (CVA-based component variants in `components/ui/`) → Composition layer (domain-aware wrapper components in `components/`). Styling flows strictly downward; data flows upward from hooks through pages into components via props. The component boundary rule is critical: `components/ui/` must not import domain types from `lib/api.ts`.
**Major components:**
1. `index.css` — single source of all CSS tokens, `@theme inline` Tailwind bridge, and base styles; no other CSS files
2. `lib/palette.ts` (new) — single source of truth for category-to-color mapping, used by both Tailwind classes and Recharts `fill` strings
3. `lib/colors.ts` (new) — `getCategoryColor()` utility for runtime CSS variable resolution in chart components
4. `components/category-badge.tsx` (new) — domain badge using CVA `categoryVariants` as single source of category-to-color mapping
5. `components/stat-card.tsx` (new) — financial metric display card wrapping shadcn Card
6. `components/progress-bar.tsx` (new) — budget vs. actual visual with color-coded threshold states
7. `components/empty-state.tsx` (new) — consistent empty state for all conditional sections
8. `components/InlineEditCell.tsx` (extract) — extract from three duplicated local definitions in BillsTracker, VariableExpenses, DebtTracker
### Critical Pitfalls
1. **Hardcoded color values bypassing the token system** — The codebase has hex strings in Recharts fills, raw Tailwind palette classes (`bg-emerald-50`), and per-component `PASTEL_COLORS` arrays alongside the CSS variable system. Adding a fourth system (the pastel tokens) without migrating the existing approaches produces a visually inconsistent result regardless of effort. Prevention: `lib/palette.ts` as the single source of truth; ban inline hex values in component files.
2. **Editing shadcn source files directly**`src/components/ui/` is effectively vendor code. Editing it risks overwrite by future `shadcn add` commands and makes the diff from upstream invisible. Prevention: customize exclusively via CSS variables in `index.css`; create wrapper components (`stat-card.tsx`, `category-badge.tsx`) for domain logic rather than modifying `card.tsx`, `badge.tsx`.
3. **InlineEditRow duplicated in three components**`BillsTracker.tsx`, `VariableExpenses.tsx`, and `DebtTracker.tsx` each contain a local `InlineEditRow` function. The polish pass will make them diverge further if not extracted first. Prevention: extract to `components/InlineEditCell.tsx` before any visual work touches these tables.
4. **Chart colors not connected to the semantic color system**`AvailableBalance` donut slice colors and `FinancialOverview` table row colors use different approaches for the same category types. A user sees two different blues for "Bills" on the same screen. Prevention: all chart `fill` props must derive from `CATEGORY_COLORS` in `lib/palette.ts`, keyed by category type — never index-based arrays.
5. **Spacing inconsistency overlooked in favor of color work**`CardContent` padding varies (`p-0` vs `pt-4` vs `pt-6`) with no governing rule. Even correct colors will feel amateur if layout rhythm is inconsistent. Prevention: define spacing standards early (dashboard section gaps, card anatomy) and treat auth pages as first-class design surfaces rather than an afterthought.
---
## Implications for Roadmap
Based on combined research, a four-phase structure is strongly indicated by the dependency graph in FEATURES.md and the build order in ARCHITECTURE.md.
### Phase 1: Design Token Foundation
**Rationale:** Everything else depends on this. The CSS variable system must have real pastel values before any component work produces consistent results. Establishing `lib/palette.ts` here prevents the color fragmentation pitfall from propagating through all subsequent phases.
**Delivers:** Pastel color system live across all shadcn components, category semantic tokens available as Tailwind utilities, `lib/palette.ts` and `lib/colors.ts` in place, `InlineEditCell.tsx` extracted (dependency clearance before Phase 2).
**Addresses:** Table stakes #1 (pastel CSS variable system); Pitfalls 1, 3, 4.
**Avoids:** Starting component polish before the token layer is stable — the most common failure mode for this type of project.
### Phase 2: Layout and Brand Identity
**Rationale:** Once tokens exist, the surfaces users see on every page load (login, sidebar, card headers, page headers) can be polished. These are high-visibility, high-leverage changes that establish the overall visual tone before touching individual feature components.
**Delivers:** Branded login/register page, polished sidebar with visual hierarchy and colored active nav, consistent card header palette, typography hierarchy on dashboard (FinancialOverview + AvailableBalance as hero row), budget selector with proper page header framing.
**Uses:** Pastel tokens from Phase 1, Geist Mono for tabular numbers.
**Implements:** `page-header.tsx`, `stat-card.tsx` composition components.
**Addresses:** Table stakes #25 (login background, logo, sidebar, active nav, typography).
**Avoids:** Pitfall 5 (spacing inconsistency) — establish spacing standards here.
### Phase 3: Interaction Quality and Completeness
**Rationale:** With color and layout established, the focus shifts to states and interactions: loading, empty, error, and edit feedback. These guard against the "unfinished" feeling that persists even in visually polished apps.
**Delivers:** Loading spinners on form submit buttons, empty states for dashboard first-run and CategoriesPage, inline edit pencil icon affordance, save confirmation flash, positive/negative amount coloring uniformly applied, delete confirmation dialog on CategoriesPage, i18n keys for all new text.
**Implements:** `empty-state.tsx`, interaction patterns on `InlineEditCell.tsx`.
**Addresses:** Table stakes #614; Pitfalls 6 (i18n), 8 (empty states), 9 (delete confirmation).
**Avoids:** New feature scope creep — defer month navigator, progress bars, budget health badge to optional stretch if foundation is solid.
### Phase 4: Chart Polish
**Rationale:** Charts come last because they depend on both the token system (Phase 1) and the domain components (Phase 2-3) being stable. Polishing charts before tokens are final means redoing the work.
**Delivers:** `ExpenseBreakdown` migrated to `ChartContainer` pattern, all chart fills referencing `CATEGORY_COLORS` from `lib/palette.ts`, custom `ChartTooltipContent` with `formatCurrency` formatting, donut center label with month/year context, `formatCurrency` locale bug fixed.
**Uses:** `ChartContainer`, `ChartTooltipContent` from existing `ui/chart.tsx`.
**Implements:** Recharts color consistency via `getCategoryColor()`.
**Addresses:** Differentiators (chart tooltips, donut context label); Pitfalls 7 (Recharts tooltip styling), 10 (formatCurrency locale).
### Phase Ordering Rationale
- Phase 1 must precede all others because CSS token values are consumed by every subsequent change. Polish done without live tokens requires rework.
- Phase 2 before Phase 3 because layout surfaces (sidebar, login, card headers) are always visible and set the perceptual quality bar; interaction states are noticed only when triggered.
- Phase 3 before Phase 4 because empty states (Phase 3) affect what charts render against; fixing chart styling before the empty-state context is designed produces mismatched polish.
- Extraction of `InlineEditCell.tsx` is placed at the start of Phase 1 (before any visual work) to prevent the three-component divergence pitfall.
### Research Flags
Phases with standard patterns (research not needed):
- **Phase 1:** Tailwind 4 `@theme inline` token system is well-documented; patterns confirmed directly from codebase inspection. No research needed.
- **Phase 2:** shadcn layout patterns are established and fully confirmed. No research needed.
- **Phase 3:** All interaction patterns (loading spinner, row flash, empty state) are straightforward CSS transitions and existing shadcn Dialog usage. No research needed.
- **Phase 4:** Recharts + `ChartContainer` integration pattern is confirmed from existing `ui/chart.tsx`. No research needed.
No phases require `/gsd:research-phase` — this is a well-scoped polish pass on an existing functional codebase with a fully locked, known stack.
---
## Confidence Assessment
| Area | Confidence | Notes |
|------|------------|-------|
| Stack | HIGH | All versions confirmed from `package.json`; no new dependencies needed |
| Features | HIGH (table stakes) / MEDIUM (differentiators) | Table stakes from direct code inspection; differentiators from design domain experience without external benchmark sources |
| Architecture | HIGH | Confirmed from direct inspection of `index.css`, `components.json`, `button.tsx`, `FinancialOverview.tsx`, `ui/chart.tsx` |
| Pitfalls | HIGH | All pitfalls grounded in observed code — specific files and line-level patterns cited |
**Overall confidence:** HIGH
### Gaps to Address
- **formatCurrency locale bug (Pitfall 10):** The `de-DE` hardcode in `lib/format.ts` is a correctness issue, not just cosmetic. Confirm the `preferred_locale` field is available on the settings API response before implementing the fix in Phase 4.
- **Delete cascade behavior on categories (Pitfall 9):** Before adding a confirmation dialog in Phase 3, verify what the Go backend does when a category with associated budget items is deleted. The confirmation copy should reflect the actual impact.
- **Differentiator priority (budget health badge, month navigator, progress bars):** These are MEDIUM-confidence additions based on design judgment. Treat them as stretch goals within Phase 3 rather than committed scope. Re-evaluate after Phase 2 is complete.
- **Geist Mono availability:** Confirm `@fontsource-variable/geist-mono` is available on the deployment host or can be bundled before adding the `bun add` step.
---
## Sources
### Primary (HIGH confidence)
- Direct codebase inspection: `/frontend/src/index.css` — confirms Tailwind 4 `@theme inline`, oklch color space, shadcn CSS variable architecture
- Direct codebase inspection: `/frontend/package.json` — all version numbers confirmed
- Direct codebase inspection: `/frontend/components.json` — confirms shadcn v4, `cssVariables: true`, no `tailwind.config.ts`
- Direct codebase inspection: `/frontend/src/components/ui/button.tsx`, `chart.tsx` — confirms CVA pattern, `ChartContainer` wrapper
- Direct codebase inspection: `/frontend/src/components/FinancialOverview.tsx`, `AvailableBalance.tsx`, `ExpenseBreakdown.tsx` — identifies hardcoded color anti-patterns
- Direct codebase inspection: `/frontend/src/` all pages and components — identifies `InlineEditRow` duplication, missing empty states, inconsistent amount coloring
### Secondary (MEDIUM confidence)
- Framer Motion React 19 compatibility: training data — recommendation to avoid is defensible on bundle-size grounds alone (~45KB) regardless of compatibility status
- Differentiator features (budget health badge, progress bars, animated counters): domain experience with personal finance UI patterns; no external benchmark sources verified in this session
### Tertiary (LOW confidence)
- None — all findings are grounded in codebase or framework documentation patterns
---
*Research completed: 2026-03-11*
*Ready for roadmap: yes*