This commit is contained in:
2026-03-15 17:29:43 +01:00
parent d8668ed742
commit 65d9842831
187 changed files with 0 additions and 26541 deletions

View File

@@ -1,292 +0,0 @@
# Technology Stack
**Project:** SimpleFinanceDash — UI Polish Milestone
**Researched:** 2026-03-11
**Confidence:** HIGH (stack already locked; all versions confirmed from package.json and source files)
---
## Current Stack (Already Installed — Do Not Change)
| Technology | Version | Role |
|------------|---------|------|
| React | 19.2.0 | UI runtime |
| TypeScript | 5.9.3 | Type safety |
| Vite | 7.3.1 | Build tool |
| Tailwind CSS | 4.2.1 | Utility styling |
| shadcn/ui | 4.0.0 | Component library |
| radix-ui | 1.4.3 | Headless primitives (single package, not individual `@radix-ui/*`) |
| Recharts | 2.15.4 | Charts |
| tw-animate-css | 1.4.0 | CSS animation utilities |
| Geist Variable | 5.2.8 | Primary font (already loaded, set as `--font-sans`) |
| lucide-react | 0.577.0 | Icon set |
**Critical context:** The project is on the new shadcn v4 architecture (single `radix-ui` package, `@theme inline` in CSS, `oklch` color space). All recommendations below are scoped to this exact setup.
---
## Recommended Stack — UI Polish Layer
### 1. CSS Variable Strategy for Pastel Theme
**Technique: Replace oklch values in `:root` inside `index.css`**
The current `index.css` uses default shadcn black/white tokens:
- `--background: oklch(1 0 0)` → pure white
- `--primary: oklch(0.205 0 0)` → near-black
- `--secondary: oklch(0.97 0 0)` → near-white gray
**Replace with pastel-tinted equivalents.** oklch is ideal for pastel work because the `C` (chroma) channel controls saturation independently of lightness. A pastel is high-L, low-C:
```
Soft lavender background: oklch(0.97 0.015 280)
Soft blue primary: oklch(0.55 0.12 250)
Rose secondary: oklch(0.94 0.025 350)
Amber accent: oklch(0.94 0.04 85)
Soft green: oklch(0.94 0.04 145)
```
**Finance category color tokens** (add as custom vars alongside shadcn defaults):
```css
:root {
/* Category semantic tokens — used in chart fills and row backgrounds */
--color-income: oklch(0.88 0.08 145); /* soft green */
--color-bill: oklch(0.88 0.07 250); /* soft blue */
--color-expense: oklch(0.90 0.08 85); /* soft amber */
--color-debt: oklch(0.90 0.08 15); /* soft red/rose */
--color-saving: oklch(0.88 0.07 280); /* soft violet */
--color-investment: oklch(0.90 0.07 320); /* soft pink */
}
```
Expose them in the `@theme inline` block so Tailwind generates utility classes (`bg-income`, `bg-bill`, etc.).
**Confidence: HIGH** — This is how shadcn v4 + Tailwind v4 theming works. The `@theme inline` bridging pattern is confirmed by the existing codebase structure.
---
### 2. shadcn/ui Component Customization Strategy
**Pattern: Variant extension via `class-variance-authority` (CVA) — already available**
Do not fork shadcn component source files unless necessary. Instead:
1. **CSS variable changes** cover 80% of polish (background, border, primary, radius).
2. **className overrides at the call site** via `cn()` cover the remaining 20%.
3. For components that need a recurring custom style (e.g., Card with always-pastel header), wrap in a thin local component: `PastelCard`, `StatCard`. This avoids touching `ui/card.tsx`.
**Radius token:** The current `--radius: 0.625rem` is fine. For a softer look, increase to `0.75rem` or `1rem`.
**Border softness:** Replace `--border: oklch(0.922 0 0)` with a tinted, slightly lower-opacity token like `oklch(0.88 0.02 250 / 60%)` for a barely-there border that reads as "pastel spreadsheet cell divider."
**Confidence: HIGH**
---
### 3. Chart Customization with Recharts + shadcn ChartContainer
The codebase already has shadcn's `ChartContainer` / `ChartTooltipContent` wrapper in `ui/chart.tsx`. This is the correct foundation.
**Current gap:** `ExpenseBreakdown.tsx` uses raw `<PieChart>` without `ChartContainer`. It should be migrated to use `ChartContainer` + `ChartConfig` so chart colors come from CSS variables rather than hardcoded hex strings.
**Pattern to follow for all charts:**
```typescript
const chartConfig = {
bills: { label: 'Bills', color: 'var(--color-bill)' },
expenses: { label: 'Expenses', color: 'var(--color-expense)' },
savings: { label: 'Savings', color: 'var(--color-saving)' },
investments: { label: 'Investments', color: 'var(--color-investment)' },
} satisfies ChartConfig
```
Then use `fill="var(--color-bills)"` on Recharts `<Cell>` elements. The `ChartStyle` component injects these as scoped CSS vars so dark mode works automatically.
**Tooltip customization:** Use `<ChartTooltipContent>` with a `formatter` prop to render currency-formatted values. The existing `ChartTooltipContent` already handles the indicator dot pattern — just pass `formatter={(value) => formatCurrency(value, currency)}`.
**Recharts animation:** Recharts has built-in entrance animations on charts (`isAnimationActive={true}` is the default). The animation duration can be tuned via `animationDuration` (prop on `<Bar>`, `<Pie>`, `<Line>`). Set to `600``800ms` for a polished feel. Do NOT disable animations.
**Chart types in use / recommended:**
- `PieChart` — expense breakdown (already exists, needs ChartContainer migration)
- `BarChart` — budget vs actual comparison (recommended addition for FinancialOverview)
- `RadialBarChart` — AvailableBalance progress indicator (recommended to replace plain number display)
**Confidence: HIGH** — Recharts 2.x API is stable and well-understood. The ChartContainer pattern is confirmed from the existing `ui/chart.tsx` source.
---
### 4. Animation and Transition Strategy
**Primary tool: `tw-animate-css` (already installed)**
`tw-animate-css` provides Tailwind v4-compatible utility classes wrapping Animate.css keyframes. Use it for:
- Page entrance animations: `animate-fade-in` on dashboard sections
- Dialog/sheet enters: `animate-in slide-in-from-bottom-4` (shadcn already applies these via `data-[state=open]` variants)
- Loading skeletons: `animate-pulse` (Tailwind built-in, no extra library needed)
**Do NOT add Motion/Framer Motion for this milestone.** Reason: the interactions needed (inline edit toggle, form submission feedback, card entrance) are all achievable with CSS transitions and `tw-animate-css`. Framer Motion adds ~45KB to the bundle and its React 19 compatibility had rough edges in early 2025. Revisit for a future milestone if complex drag-and-drop or physics animations are needed.
**Inline edit transitions** (e.g., BillsTracker `InlineEditRow`): Use CSS `transition-all duration-150` on the toggle between display and input state. This is zero-dependency and already idiomatic in the codebase.
**Recommended micro-interaction patterns:**
- Input focus: `focus-visible:ring-2 focus-visible:ring-primary/40` for a soft glow
- Row hover: `hover:bg-muted/60 transition-colors duration-150`
- Button press: shadcn Button already has `active:scale-[0.98]` equivalent via its CVA variants — verify in `ui/button.tsx`
- Number updates (balance changing): CSS `@keyframes` count-up is overkill; a simple `transition-opacity` flash on value change via a `key` prop reset is sufficient
**Confidence: MEDIUM** — tw-animate-css is confirmed installed. Framer Motion React 19 compatibility claim is based on training data; the recommendation to skip it is defensive but well-justified on bundle size grounds alone.
---
### 5. Typography
**Current state:** Geist Variable is already installed (`@fontsource-variable/geist`) and set as `--font-sans`. This is the correct font for this project.
**Geist Variable characteristics:** Clean, geometric, excellent for data/numbers, neutral without being cold. Used by Vercel's dashboard products. Well-suited for finance UIs.
**Do NOT add a second typeface.** The spreadsheet aesthetic is served by a single well-chosen sans. A display font pairing would clash with the data-dense layout.
**Typography scale recommendations:**
```css
/* Add to @theme inline */
--font-mono: 'Geist Mono Variable', ui-monospace, monospace;
```
Install `@fontsource-variable/geist-mono` alongside the existing font. Use `font-mono tabular-nums` on all currency amounts. The existing `ChartTooltipContent` already does this (`font-mono tabular-nums` class on values) — extend this pattern to table cells showing currency.
**Text hierarchy for finance:**
- Card titles: `text-sm font-semibold tracking-wide text-muted-foreground uppercase` — reads as a spreadsheet column header
- Primary numbers (balance): `text-3xl font-bold tabular-nums`
- Secondary numbers (line items): `text-sm tabular-nums`
- Labels: `text-xs text-muted-foreground`
**Confidence: HIGH** — Font is already installed. Geist Mono is the natural companion and from the same @fontsource-variable namespace.
---
### 6. Icon Usage (lucide-react)
**Already installed at 0.577.0.** lucide-react is the correct choice — it's the shadcn default and provides consistent stroke-width icons that match the component style.
**Pattern for finance icons:**
| Concept | Lucide Icon |
|---------|-------------|
| Income | `TrendingUp` |
| Bills | `Receipt` |
| Variable expenses | `ShoppingCart` |
| Debt | `CreditCard` |
| Savings | `PiggyBank` |
| Investments | `BarChart2` |
| Settings | `Settings2` |
| Categories | `Tag` |
| Budget period | `CalendarDays` |
Use `size={16}` consistently for inline icons, `size={20}` for standalone icon buttons.
**Confidence: HIGH**
---
### 7. Layout Pattern for Dashboard
The current `DashboardPage.tsx` uses a flat `flex flex-col gap-6 p-6` layout. For polish, the recommended evolution is:
**Header area:** Sticky top bar with budget selector + month navigation + key stat (available balance). Removes the selector from inline flow.
**Grid approach (keep existing):** The `lg:grid-cols-3` + `lg:col-span-2` pattern for FinancialOverview + AvailableBalance is correct. Continue using CSS Grid for two-column chart sections.
**Card anatomy standard:**
- `CardHeader`: Always has a soft background gradient using two adjacent pastel tokens
- `CardTitle`: Uses the uppercase label style (see Typography section)
- `CardContent`: `p-0` for tables, `pt-4` for charts/other content
This pattern already exists in the codebase (BillsTracker, FinancialOverview headers use `bg-gradient-to-r from-blue-50 to-indigo-50`). The task is to make it consistent and driven by category-semantic tokens rather than hardcoded Tailwind color names.
**Do NOT use Tailwind's `bg-blue-50` directly.** Reason: when the CSS variable values are updated for theming, hardcoded `bg-blue-50` won't change. Use `bg-(--color-bill)/20` syntax to reference semantic tokens.
**Confidence: HIGH**
---
## Alternatives Considered
| Category | Recommended | Alternative | Why Not |
|----------|-------------|-------------|---------|
| Animation | tw-animate-css (installed) | Framer Motion | Bundle cost (~45KB), React 19 edge cases, overkill for this scope |
| Animation | tw-animate-css (installed) | react-spring | Same — overkill, no benefit over CSS transitions for this UI |
| Charts | Recharts (installed) | Victory / Nivo | Recharts is already embedded, ChartContainer wrapper is done, switching has zero upside |
| Charts | Recharts (installed) | D3 directly | Too low-level; maintenance cost far exceeds polish benefit |
| Fonts | Geist Variable (installed) | Inter | Geist is already there; Inter would be a downgrade in distinctiveness |
| Fonts | Single font | Font pairing | Finance dashboards read better with single-family discipline |
| Color format | oklch (current) | HSL / HEX | oklch is perceptually uniform, better for generating harmonious pastel palettes |
---
## What NOT to Use
**Do NOT install:**
- `framer-motion` — React 19 had compatibility issues in early 2025; bundle cost not justified
- `@radix-ui/*` individual packages — The project is on the new `radix-ui` unified package; mixing them causes version conflicts
- `react-select` / custom dropdown libraries — shadcn's `Select` covers the need
- A CSS-in-JS solution — Tailwind v4 + CSS variables is the correct pattern; CSS-in-JS would fight the build setup
- A color picker library — custom themes are explicitly out of scope
- `date-fns` or `dayjs` — only needed if date formatting becomes complex; standard `Intl.DateTimeFormat` is sufficient
- `react-hook-form` — the forms in scope (login, budget creation) are simple enough for controlled inputs; adding a form library is premature
---
## Key Tailwind v4 Syntax Notes
**Important: Tailwind v4 changed syntax.** The codebase correctly uses the new forms but contributors must be aware:
| v3 syntax | v4 syntax |
|-----------|-----------|
| `bg-opacity-50` | `bg-black/50` or `oklch(.../50%)` |
| `text-opacity-75` | `text-foreground/75` |
| `bg-[--color-bill]` | `bg-(--color-bill)` (parentheses, not brackets) |
| `@apply border-border` | Same (works in v4) |
| Plugin-based config | `@theme inline` in CSS |
Use `bg-(--color-bill)/20` to reference a CSS variable with opacity. This is the correct v4 syntax.
**Confidence: HIGH** — Confirmed by reading the existing `index.css` which uses the `@theme inline` pattern correctly.
---
## Installation
No new packages are required for the core polish milestone. All tools are already installed.
**Optional — only if currency formatting needs locale support beyond what exists:**
```bash
# Nothing to add — Intl.NumberFormat is native
```
**If Geist Mono is desired (recommended for tabular numbers):**
```bash
cd frontend && bun add @fontsource-variable/geist-mono
```
Then in `index.css`:
```css
@import "@fontsource-variable/geist-mono";
```
And in `@theme inline`:
```css
--font-mono: 'Geist Mono Variable', ui-monospace, monospace;
```
---
## Sources
- Confirmed from codebase: `/frontend/package.json`, `/frontend/src/index.css`, `/frontend/src/components/ui/chart.tsx`
- shadcn v4 `@theme inline` + `oklch` color pattern: confirmed from existing `index.css` structure (HIGH confidence)
- Tailwind v4 CSS variable reference syntax (`bg-(--var)`): confirmed from Tailwind v4 docs in training data, consistent with codebase patterns (HIGH confidence)
- Recharts ChartContainer pattern: confirmed from existing `ui/chart.tsx` implementation (HIGH confidence)
- tw-animate-css v4 compatibility: confirmed from import in `index.css` (`@import "tw-animate-css"`) (HIGH confidence)
- Framer Motion React 19 compatibility concerns: training data (MEDIUM confidence — may have improved in later 2025 patch releases, but avoidance recommendation stands on bundle-size grounds alone)