678 lines
23 KiB
Markdown
678 lines
23 KiB
Markdown
# Phase 5: Design System Token Rework - Pattern Map
|
|
|
|
**Mapped:** 2026-04-20
|
|
**Files analyzed:** 14 files (1 CSS + 2 chart components + 1 shared component + 9 pages + 1 component)
|
|
**Analogs found:** 14 / 14 (all files are modifications of existing code — no new files created)
|
|
|
|
---
|
|
|
|
## File Classification
|
|
|
|
| Modified File | Role | Data Flow | Closest Analog / Self | Match Quality |
|
|
|---|---|---|---|---|
|
|
| `src/index.css` | config (CSS tokens) | transform | self — no analog needed | self |
|
|
| `src/components/dashboard/charts/SpendBarChart.tsx` | component (chart) | transform | `src/components/dashboard/charts/IncomeBarChart.tsx` | exact |
|
|
| `src/components/dashboard/charts/IncomeBarChart.tsx` | component (chart) | transform | `src/components/dashboard/charts/SpendBarChart.tsx` | exact |
|
|
| `src/components/dashboard/charts/ExpenseDonutChart.tsx` | component (chart) | transform | `SpendBarChart.tsx` / `IncomeBarChart.tsx` | role-match |
|
|
| `src/components/shared/PageShell.tsx` | component (layout) | request-response | self | self |
|
|
| `src/components/dashboard/DashboardSkeleton.tsx` | component (skeleton) | request-response | `CategoriesPage.tsx` skeleton block | role-match |
|
|
| `src/components/dashboard/CategorySection.tsx` | component (list item) | request-response | `BudgetDetailPage.tsx` group heading | role-match |
|
|
| `src/components/dashboard/charts/ChartEmptyState.tsx` | component (empty state) | request-response | self | self |
|
|
| `src/components/QuickAddPicker.tsx` | component (picker) | request-response | `BudgetDetailPage.tsx` select label dot | role-match |
|
|
| `src/pages/DashboardPage.tsx` | page | request-response | self | self |
|
|
| `src/pages/BudgetListPage.tsx` | page | request-response | `DashboardPage.tsx` / `BudgetDetailPage.tsx` | role-match |
|
|
| `src/pages/BudgetDetailPage.tsx` | page | CRUD | `CategoriesPage.tsx` / `TemplatePage.tsx` | exact |
|
|
| `src/pages/TemplatePage.tsx` | page | CRUD | `BudgetDetailPage.tsx` / `CategoriesPage.tsx` | exact |
|
|
| `src/pages/CategoriesPage.tsx` | page | CRUD | `BudgetDetailPage.tsx` / `TemplatePage.tsx` | exact |
|
|
| `src/pages/QuickAddPage.tsx` | page | CRUD | `CategoriesPage.tsx` | role-match |
|
|
| `src/pages/SettingsPage.tsx` | page | request-response | `CategoriesPage.tsx` | role-match |
|
|
| `src/pages/LoginPage.tsx` | page | request-response | `SettingsPage.tsx` | role-match |
|
|
| `src/pages/RegisterPage.tsx` | page | request-response | `SettingsPage.tsx` | role-match |
|
|
|
|
---
|
|
|
|
## Pattern Assignments
|
|
|
|
### `src/index.css` (config, CSS tokens)
|
|
|
|
**Analog:** self — authoritative token source, no external analog needed.
|
|
|
|
**Current `@theme inline` block** (lines 1-99, full file):
|
|
|
|
```css
|
|
@import "tailwindcss";
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
@theme inline {
|
|
--color-background: oklch(0.98 0.005 260); /* → 0.01 chroma (warm) */
|
|
--color-foreground: oklch(0.25 0.02 260);
|
|
|
|
/* ... (base tokens unchanged) ... */
|
|
|
|
/* Category fill tokens — RAISE chroma to 0.22+ */
|
|
--color-income-fill: oklch(0.68 0.19 155); /* → 0.22 */
|
|
--color-bill-fill: oklch(0.65 0.19 25); /* → 0.22 */
|
|
--color-variable-expense-fill: oklch(0.70 0.18 50); /* → 0.22 */
|
|
--color-debt-fill: oklch(0.60 0.20 355); /* → 0.23 */
|
|
--color-saving-fill: oklch(0.68 0.18 220); /* → 0.22 */
|
|
--color-investment-fill: oklch(0.65 0.18 285); /* → 0.22 */
|
|
|
|
/* Chart vars — DELETE these 5 lines entirely */
|
|
--color-chart-1: oklch(0.72 0.14 155);
|
|
--color-chart-2: oklch(0.7 0.14 25);
|
|
--color-chart-3: oklch(0.72 0.14 50);
|
|
--color-chart-4: oklch(0.65 0.16 355);
|
|
--color-chart-5: oklch(0.72 0.14 220);
|
|
|
|
--radius: 0.625rem; /* → 0 */
|
|
}
|
|
|
|
@layer base {
|
|
* { @apply border-border; }
|
|
body {
|
|
@apply bg-background text-foreground;
|
|
font-feature-settings: "rlig" 1, "calt" 1;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
1. Line 7: `--color-background: oklch(0.98 0.005 260)` → `oklch(0.98 0.01 260)`
|
|
2. Lines 65-70: raise all six `--color-*-fill` chroma values to 0.22+ per CONTEXT.md
|
|
3. Lines 52-57: delete `--color-chart-1` through `--color-chart-5` entirely
|
|
4. Line 72: `--radius: 0.625rem` → `--radius: 0`
|
|
5. After `@layer base` block: add CSS override selectors for Recharts and Sonner (see Shared Patterns section below)
|
|
|
|
---
|
|
|
|
### `src/components/dashboard/charts/SpendBarChart.tsx` (component, transform)
|
|
|
|
**Analog:** `src/components/dashboard/charts/IncomeBarChart.tsx`
|
|
|
|
**Current `Bar` radius props** (lines 64-80):
|
|
|
|
```tsx
|
|
<Bar
|
|
dataKey="budgeted"
|
|
fill="var(--color-budgeted)"
|
|
radius={4} /* → radius={0} */
|
|
/>
|
|
<Bar dataKey="actual" radius={4}> /* → radius={0} */
|
|
{data.map((entry, index) => (
|
|
<Cell
|
|
key={index}
|
|
fill={
|
|
entry.actual > entry.budgeted
|
|
? "var(--color-over-budget)"
|
|
: `var(--color-${entry.type}-fill)` /* already correct — no change */
|
|
}
|
|
/>
|
|
))}
|
|
</Bar>
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 67: `radius={4}` → `radius={0}`
|
|
- Line 69: `radius={4}` → `radius={0}`
|
|
- `chartConfig` (lines 31-34): no change needed — `actual` already references `--color-muted-foreground`, `budgeted` references `--color-budget-bar-bg`; neither references deleted `--color-chart-*` vars.
|
|
|
|
---
|
|
|
|
### `src/components/dashboard/charts/IncomeBarChart.tsx` (component, transform)
|
|
|
|
**Analog:** `src/components/dashboard/charts/SpendBarChart.tsx`
|
|
|
|
**Current `Bar` radius props** (lines 54-69):
|
|
|
|
```tsx
|
|
<Bar
|
|
dataKey="budgeted"
|
|
fill="var(--color-budgeted)"
|
|
radius={[4, 4, 0, 0]} /* → radius={0} */
|
|
/>
|
|
<Bar dataKey="actual" radius={[4, 4, 0, 0]}> /* → radius={0} */
|
|
{data.map((entry, index) => (
|
|
<Cell
|
|
key={index}
|
|
fill={
|
|
entry.actual > entry.budgeted
|
|
? "var(--color-over-budget)"
|
|
: "var(--color-income-fill)" /* already correct — no change */
|
|
}
|
|
/>
|
|
))}
|
|
</Bar>
|
|
```
|
|
|
|
**Current `chartConfig`** (lines 26-29):
|
|
|
|
```tsx
|
|
const chartConfig = {
|
|
budgeted: { label: "Budgeted", color: "var(--color-budget-bar-bg)" },
|
|
actual: { label: "Actual", color: "var(--color-income-fill)" }, /* already references fill token */
|
|
} satisfies ChartConfig
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 57: `radius={[4, 4, 0, 0]}` → `radius={0}`
|
|
- Line 59: `radius={[4, 4, 0, 0]}` → `radius={0}`
|
|
- `chartConfig`: no change needed — already references `--color-income-fill`, not a deleted `--color-chart-*` var.
|
|
|
|
---
|
|
|
|
### `src/components/dashboard/charts/ExpenseDonutChart.tsx` (component, transform)
|
|
|
|
**Analog:** self — already uses `var(--color-${entry.type}-fill)` pattern throughout.
|
|
|
|
**The one hardcoded `rounded-full`** (line 141):
|
|
|
|
```tsx
|
|
/* Before */
|
|
<span
|
|
className="inline-block size-3 shrink-0 rounded-full"
|
|
style={{ backgroundColor: `var(--color-${entry.type}-fill)` }}
|
|
/>
|
|
|
|
/* After — remove rounded-full entirely */
|
|
<span
|
|
className="inline-block size-3 shrink-0"
|
|
style={{ backgroundColor: `var(--color-${entry.type}-fill)` }}
|
|
/>
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 141: remove `rounded-full` from className string.
|
|
- No `ChartConfig` changes needed — `ExpenseDonutChart` builds its config dynamically from data using `var(--color-${entry.type}-fill)` (lines 47-51), which is already the correct post-rework pattern.
|
|
|
|
---
|
|
|
|
### `src/components/shared/PageShell.tsx` (component, layout)
|
|
|
|
**Current spacing** (line 15):
|
|
|
|
```tsx
|
|
<div className="flex flex-col gap-6"> /* → gap-8 */
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 15: `gap-6` → `gap-8`
|
|
|
|
This single change propagates the header-to-content gap increase to all 9 pages that use `PageShell`.
|
|
|
|
---
|
|
|
|
### `src/components/dashboard/DashboardSkeleton.tsx` (component, skeleton)
|
|
|
|
**Analog:** Skeleton patterns from `CategoriesPage.tsx` lines 98-114 and `BudgetDetailPage.tsx` lines 284-306.
|
|
|
|
**Hardcoded `rounded-*` locations in this file:**
|
|
|
|
```tsx
|
|
/* Line 35 — chart placeholder */
|
|
<Skeleton className="h-[250px] w-full rounded-md" /> /* remove rounded-md */
|
|
|
|
/* Line 43 — chart placeholder */
|
|
<Skeleton className="h-[250px] w-full rounded-md" /> /* remove rounded-md */
|
|
|
|
/* Line 51 — chart placeholder */
|
|
<Skeleton className="h-[250px] w-full rounded-md" /> /* remove rounded-md */
|
|
|
|
/* Line 59 — collapsible section row */
|
|
<div className="flex items-center gap-3 rounded-md border-l-4 ..."> /* remove rounded-md */
|
|
|
|
/* Lines 63-64 — inline summary badges */
|
|
<Skeleton className="h-5 w-24 rounded-full" /> /* remove rounded-full — CRITICAL: rounded-full is 9999px, not from --radius */
|
|
<Skeleton className="h-5 w-24 rounded-full" /> /* remove rounded-full */
|
|
```
|
|
|
|
**Also: spacing upgrades**:
|
|
|
|
```tsx
|
|
/* Line 20 — top-level container */
|
|
<div className="flex flex-col gap-6"> /* → gap-8 */
|
|
|
|
/* Line 22 — summary cards grid */
|
|
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> /* → gap-6 */
|
|
|
|
/* Line 29 — chart grid */
|
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> /* → gap-8 */
|
|
```
|
|
|
|
---
|
|
|
|
### `src/components/dashboard/CategorySection.tsx` (component, list item)
|
|
|
|
**Analog:** Group heading divs in `BudgetDetailPage.tsx` lines 352-356, `CategoriesPage.tsx` lines 134-137, `TemplatePage.tsx` lines 292-295.
|
|
|
|
**The hardcoded `rounded-md`** (line 73):
|
|
|
|
```tsx
|
|
/* Before */
|
|
<button
|
|
className="group flex w-full items-center gap-3 rounded-md border-l-4 bg-card px-4 py-3 text-left hover:bg-muted/40 transition-colors"
|
|
style={{ borderLeftColor: categoryColors[type] }}
|
|
>
|
|
|
|
/* After — remove rounded-md */
|
|
<button
|
|
className="group flex w-full items-center gap-3 border-l-4 bg-card px-4 py-3 text-left hover:bg-muted/40 transition-colors"
|
|
style={{ borderLeftColor: categoryColors[type] }}
|
|
>
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 73: remove `rounded-md` from the button's className.
|
|
|
|
---
|
|
|
|
### `src/components/dashboard/charts/ChartEmptyState.tsx` (component, empty state)
|
|
|
|
**Current `rounded-lg`** (line 12):
|
|
|
|
```tsx
|
|
/* Before */
|
|
className={cn(
|
|
"flex min-h-[250px] w-full items-center justify-center rounded-lg border border-dashed ...",
|
|
className
|
|
)}
|
|
|
|
/* After — remove rounded-lg */
|
|
className={cn(
|
|
"flex min-h-[250px] w-full items-center justify-center border border-dashed ...",
|
|
className
|
|
)}
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 12: remove `rounded-lg` from the className string.
|
|
|
|
---
|
|
|
|
### `src/components/QuickAddPicker.tsx` (component, picker)
|
|
|
|
**Analog:** Same pattern as `BudgetDetailPage.tsx` line 496 and `TemplatePage.tsx` line 385 — `size-2 rounded-full` color swatch dots used as SelectLabel decorators.
|
|
|
|
**Hardcoded `rounded-*` locations:**
|
|
|
|
```tsx
|
|
/* Line 156 — picker item button */
|
|
className="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 ..."
|
|
/* → remove rounded-sm */
|
|
|
|
/* Line 201 — SelectLabel category dot */
|
|
<div
|
|
className="size-2 rounded-full"
|
|
style={{ backgroundColor: categoryColors[type] }}
|
|
/>
|
|
/* → remove rounded-full */
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 156: remove `rounded-sm` from className.
|
|
- Line 201: remove `rounded-full` from className.
|
|
|
|
---
|
|
|
|
### `src/pages/DashboardPage.tsx` (page, request-response)
|
|
|
|
**Current spacing** (lines 186, 207):
|
|
|
|
```tsx
|
|
/* Line 186 — DashboardContent top-level wrapper */
|
|
<div className="space-y-6"> /* → space-y-8 */
|
|
|
|
/* Line 207 — chart grid */
|
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> /* → gap-8 */
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 186: `space-y-6` → `space-y-8`
|
|
- Line 207: `gap-6` → `gap-8`
|
|
|
|
No hardcoded `rounded-*` classes in this file.
|
|
|
|
---
|
|
|
|
### `src/pages/BudgetListPage.tsx` (page, CRUD)
|
|
|
|
**Analog:** `BudgetDetailPage.tsx` lines 242-256 — same `rounded-md border p-3` container pattern.
|
|
|
|
**Hardcoded `rounded-md`** (line 243):
|
|
|
|
```tsx
|
|
/* Before */
|
|
<div className="flex items-center gap-3 rounded-md border p-3">
|
|
|
|
/* After */
|
|
<div className="flex items-center gap-3 border p-3">
|
|
```
|
|
|
|
**Spacing:** Check for `gap-4`, `gap-6`, `space-y-*` patterns and apply standard upgrades:
|
|
- `gap-4` → `gap-6`
|
|
- `gap-6` → `gap-8`
|
|
- `space-y-6` → `space-y-8`
|
|
|
|
---
|
|
|
|
### `src/pages/BudgetDetailPage.tsx` (page, CRUD)
|
|
|
|
**Analog:** `CategoriesPage.tsx` and `TemplatePage.tsx` — structurally identical page pattern (group headings, Tables, PageShell).
|
|
|
|
**Hardcoded `rounded-*` locations** (confirmed from RESEARCH.md inventory):
|
|
|
|
| Line | Current | Action |
|
|
|------|---------|--------|
|
|
| 290 | `rounded-sm` on skeleton header div | Remove |
|
|
| 303 | `rounded-md` on `<Skeleton>` className | Remove |
|
|
| 353 | `rounded-sm` on budget item group heading div | Remove |
|
|
| 439 | `rounded-md` on summary totals box | Remove |
|
|
| 497 | `rounded-full` on SelectLabel category dot | Remove |
|
|
|
|
**Line 353 group heading** (current pattern to modify):
|
|
|
|
```tsx
|
|
/* Before */
|
|
<div
|
|
className="mb-2 flex items-center gap-3 rounded-sm border-l-4 bg-muted/30 px-3 py-2"
|
|
style={{ borderLeftColor: categoryColors[type] }}
|
|
>
|
|
|
|
/* After */
|
|
<div
|
|
className="mb-2 flex items-center gap-3 border-l-4 bg-muted/30 px-3 py-2"
|
|
style={{ borderLeftColor: categoryColors[type] }}
|
|
>
|
|
```
|
|
|
|
**Line 439 summary box** (current pattern to modify):
|
|
|
|
```tsx
|
|
/* Before */
|
|
<div className="rounded-md border p-4">
|
|
|
|
/* After */
|
|
<div className="border p-4">
|
|
```
|
|
|
|
**Spacing upgrades:**
|
|
- `space-y-8` already present at line 338 (inner grouped content) — verify no `space-y-6` / `gap-6` remaining
|
|
- Check for `p-4` on card content wrappers → `p-6`
|
|
|
|
---
|
|
|
|
### `src/pages/TemplatePage.tsx` (page, CRUD)
|
|
|
|
**Analog:** `BudgetDetailPage.tsx` — same group heading + Table structure, same skeleton pattern.
|
|
|
|
**Hardcoded `rounded-*` locations** (confirmed from RESEARCH.md inventory):
|
|
|
|
| Line | Current | Action |
|
|
|------|---------|--------|
|
|
| 250 | `rounded-sm` on skeleton group heading div | Remove |
|
|
| 256 | `rounded-full` on `<Skeleton>` className | Remove |
|
|
| 258 | `rounded-md` on `<Skeleton>` className | Remove |
|
|
| 292 | `rounded-sm` on template item group heading div | Remove |
|
|
| 385 | `rounded-full` on SelectLabel category dot | Remove |
|
|
|
|
**Line 292 group heading** (current):
|
|
|
|
```tsx
|
|
/* Before */
|
|
<div
|
|
className="mb-2 flex items-center gap-3 rounded-sm border-l-4 bg-muted/30 px-3 py-2"
|
|
style={{ borderLeftColor: categoryColors[type] }}
|
|
>
|
|
```
|
|
|
|
Same removal pattern as `BudgetDetailPage.tsx` line 353.
|
|
|
|
**Line 385 SelectLabel dot** (current):
|
|
|
|
```tsx
|
|
/* Before — same pattern as BudgetDetailPage line 497 */
|
|
<div
|
|
className="size-2 rounded-full"
|
|
style={{ backgroundColor: categoryColors[type] }}
|
|
/>
|
|
/* After: remove rounded-full */
|
|
```
|
|
|
|
**Spacing upgrades** (current at lines 247, 268, 287):
|
|
- Line 247: `space-y-6` → `space-y-8` (skeleton wrapper)
|
|
- Line 268: `gap-6` → `gap-8` (TemplatePage own flex header, not using PageShell)
|
|
- Line 287: `space-y-6` → `space-y-8` (main content groups)
|
|
|
|
---
|
|
|
|
### `src/pages/CategoriesPage.tsx` (page, CRUD)
|
|
|
|
**Analog:** `BudgetDetailPage.tsx` / `TemplatePage.tsx` — structurally identical.
|
|
|
|
**Hardcoded `rounded-*` locations** (confirmed from RESEARCH.md inventory):
|
|
|
|
| Line | Current | Action |
|
|
|------|---------|--------|
|
|
| 101 | `rounded-sm` on skeleton group heading div | Remove |
|
|
| 107 | `rounded-full` on `<Skeleton>` className | Remove |
|
|
| 108 | `rounded-md` on `<Skeleton>` className | Remove |
|
|
| 134 | `rounded-sm` on category group heading div | Remove |
|
|
|
|
**Line 134 group heading** (current):
|
|
|
|
```tsx
|
|
/* Before */
|
|
<div
|
|
className="mb-2 flex items-center gap-3 rounded-sm border-l-4 bg-muted/30 px-3 py-2"
|
|
style={{ borderLeftColor: categoryColors[type] }}
|
|
>
|
|
/* After: remove rounded-sm */
|
|
```
|
|
|
|
**Spacing upgrades** (current at lines 98, 130):
|
|
- Line 98: `space-y-6` → `space-y-8` (skeleton wrapper)
|
|
- Line 130: `space-y-6` → `space-y-8` (main content groups)
|
|
|
|
---
|
|
|
|
### `src/pages/QuickAddPage.tsx` (page, CRUD)
|
|
|
|
**Analog:** `CategoriesPage.tsx` — same row-list layout, same Skeleton usage.
|
|
|
|
**Hardcoded `rounded-*` locations** (confirmed from RESEARCH.md inventory):
|
|
|
|
| Line | Current | Action |
|
|
|------|---------|--------|
|
|
| 98 | `rounded-full` on `<Skeleton>` className | Remove |
|
|
| 100 | `rounded-md` on `<Skeleton>` className | Remove |
|
|
|
|
**Current skeleton block** (lines 93-105):
|
|
|
|
```tsx
|
|
<div className="space-y-1">
|
|
{[1, 2, 3, 4, 5].map((i) => (
|
|
<div key={i} className="flex items-center gap-4 px-4 py-2.5 border-b border-border">
|
|
<Skeleton className="h-5 w-10 rounded-full" /> /* line 98 — remove rounded-full */
|
|
<Skeleton className="h-4 w-36" />
|
|
<Skeleton className="ml-auto h-7 w-7 rounded-md" /> /* line 100 — remove rounded-md */
|
|
</div>
|
|
))}
|
|
</div>
|
|
```
|
|
|
|
No major spacing changes in this file (list layout, not grid/section-based).
|
|
|
|
---
|
|
|
|
### `src/pages/SettingsPage.tsx` (page, request-response)
|
|
|
|
**Analog:** `CategoriesPage.tsx` — PageShell wrapper, Card with CardContent.
|
|
|
|
**No hardcoded `rounded-*` classes** in this file (Card uses token cascade).
|
|
|
|
**Spacing upgrade** (line 87):
|
|
|
|
```tsx
|
|
/* Before — CardContent internal spacing */
|
|
<CardContent className="space-y-4 pt-6"> /* → space-y-6 */
|
|
```
|
|
|
|
**Changes to make in this file:**
|
|
- Line 87 (and corresponding skeleton line 69): `space-y-4` → `space-y-6` inside `CardContent`
|
|
|
|
---
|
|
|
|
### `src/pages/LoginPage.tsx` and `src/pages/RegisterPage.tsx` (pages, request-response)
|
|
|
|
**Analog:** `SettingsPage.tsx` — Card-based page, no group headings.
|
|
|
|
Per RESEARCH.md: "Card already uses `--radius` token cascade." No hardcoded `rounded-*` classes confirmed. No `--color-chart-*` references.
|
|
|
|
**Minimal changes:** Verify `p-4` → `p-6` in any card content wrappers if present. No spacing grids to upgrade. Treat as low-touch pages in implementation.
|
|
|
|
---
|
|
|
|
## Shared Patterns
|
|
|
|
### Pattern A: `@theme inline` Token Edit (CSS variable cascade)
|
|
|
|
**Source:** `src/index.css` lines 4-79
|
|
**Apply to:** `src/index.css` only — single source of truth
|
|
**Rule:** All shadcn `rounded-*` utility classes (rounded-md, rounded-sm, rounded-lg, rounded-xl) derive from `--radius`. Setting `--radius: 0` makes them all 0px automatically. Only `rounded-full` (9999px) is immune.
|
|
|
|
```css
|
|
/* The pattern: edit token value, cascade propagates everywhere */
|
|
@theme inline {
|
|
--radius: 0; /* single edit cascades to all shadcn components */
|
|
}
|
|
```
|
|
|
|
### Pattern B: Hardcoded `rounded-full` Removal (critical pitfall)
|
|
|
|
**Source:** Every page and component file listed in RESEARCH.md inventory
|
|
**Apply to:** All 17 `rounded-full` class occurrences across 8 files
|
|
**Rule:** `rounded-full` = `border-radius: 9999px` — hardcoded, NOT derived from `--radius`. Must be removed manually from every occurrence.
|
|
|
|
```tsx
|
|
/* Before — still circular after --radius: 0 */
|
|
<Skeleton className="h-5 w-16 rounded-full" />
|
|
<span className="inline-block size-3 shrink-0 rounded-full" ... />
|
|
<div className="size-2 rounded-full" ... />
|
|
|
|
/* After — becomes square from --radius: 0 */
|
|
<Skeleton className="h-5 w-16" />
|
|
<span className="inline-block size-3 shrink-0" ... />
|
|
<div className="size-2" ... />
|
|
```
|
|
|
|
### Pattern C: CSS Override for Third-Party Radius
|
|
|
|
**Source:** To be added to `src/index.css` after `@layer base` block
|
|
**Apply to:** Recharts bars (`SpendBarChart`, `IncomeBarChart`) and Sonner toasts
|
|
**Rule:** Recharts renders bars as `<rect rx="4" ry="4">` SVG — not CSS. Must change `radius` prop directly AND add CSS override. Sonner wires `--border-radius: var(--radius)` (confirmed at `sonner.tsx` line 28) — should auto-propagate, but add CSS override as safety net.
|
|
|
|
```css
|
|
/* Add to src/index.css after the @layer base block */
|
|
|
|
/* Recharts: SVG rect elements for bar charts */
|
|
.recharts-rectangle {
|
|
rx: 0;
|
|
ry: 0;
|
|
}
|
|
|
|
/* Sonner: toast container — safety net if var(--radius) cascade insufficient */
|
|
[data-sonner-toast] {
|
|
border-radius: 0 !important;
|
|
}
|
|
```
|
|
|
|
**Note:** Sonner's `sonner.tsx` already passes `"--border-radius": "var(--radius)"` as an inline style (line 28). When `--radius: 0`, this should cascade automatically. The CSS override is a safety net only — verify in browser after token change before deciding if it is needed.
|
|
|
|
### Pattern D: Group Heading Div (shared across 5 files)
|
|
|
|
**Source:** `src/pages/CategoriesPage.tsx` line 134, `BudgetDetailPage.tsx` line 353, `TemplatePage.tsx` line 292 — all structurally identical
|
|
**Apply to:** CategoriesPage, BudgetDetailPage, TemplatePage, CategorySection, DashboardSkeleton row
|
|
|
|
```tsx
|
|
/* Pattern: border-l-4 accent bar heading — remove rounded-sm entirely */
|
|
<div
|
|
className="mb-2 flex items-center gap-3 border-l-4 bg-muted/30 px-3 py-2"
|
|
style={{ borderLeftColor: categoryColors[type] }}
|
|
>
|
|
<span className="text-sm font-semibold">{label}</span>
|
|
</div>
|
|
```
|
|
|
|
### Pattern E: Spacing Upgrade Map
|
|
|
|
**Source:** `src/pages/DashboardPage.tsx` lines 186, 207; `src/components/shared/PageShell.tsx` line 15
|
|
**Apply to:** All 9 pages and PageShell
|
|
|
|
```
|
|
PageShell.tsx line 15: gap-6 → gap-8 (header-to-content gap)
|
|
DashboardPage.tsx line 186: space-y-6 → space-y-8 (section rhythm)
|
|
DashboardPage.tsx line 207: gap-6 → gap-8 (chart grid)
|
|
DashboardSkeleton.tsx line 22: gap-4 → gap-6 (summary cards grid)
|
|
DashboardSkeleton.tsx line 29: gap-6 → gap-8 (chart grid skeleton)
|
|
CategoriesPage.tsx line 98: space-y-6 → space-y-8 (skeleton wrapper)
|
|
CategoriesPage.tsx line 130: space-y-6 → space-y-8 (main content)
|
|
TemplatePage.tsx line 247: space-y-6 → space-y-8 (skeleton wrapper)
|
|
TemplatePage.tsx line 268: gap-6 → gap-8 (self-managed header flex)
|
|
TemplatePage.tsx line 287: space-y-6 → space-y-8 (main content)
|
|
BudgetDetailPage.tsx: verify no space-y-6/gap-6 remain after existing space-y-8 at line 338
|
|
SettingsPage.tsx line 87: space-y-4 → space-y-6 (CardContent internal)
|
|
```
|
|
|
|
### Pattern F: `ChartConfig` Token Reference (no `--color-chart-*`)
|
|
|
|
**Source:** `src/components/dashboard/charts/IncomeBarChart.tsx` lines 26-29
|
|
**Apply to:** Any future chart components — `SpendBarChart` and `IncomeBarChart` require no ChartConfig change
|
|
**Rule:** After deleting `--color-chart-*` from `index.css`, all chart color references must use `--color-*-fill` tokens.
|
|
|
|
```tsx
|
|
/* Correct pattern post-rework — reference fill tokens directly */
|
|
const chartConfig = {
|
|
budgeted: { label: "Budgeted", color: "var(--color-budget-bar-bg)" },
|
|
actual: { label: "Actual", color: "var(--color-income-fill)" },
|
|
} satisfies ChartConfig
|
|
```
|
|
|
|
---
|
|
|
|
## No Analog Found
|
|
|
|
None — all files are modifications of existing code. No net-new files are created in this phase.
|
|
|
|
---
|
|
|
|
## Implementation Wave Order
|
|
|
|
Per RESEARCH.md recommendation (planner reference):
|
|
|
|
**Wave 1 — Token edit only** (`src/index.css`)
|
|
- Change `--radius: 0`
|
|
- Raise `--color-*-fill` chroma values to 0.22+
|
|
- Warm `--color-background` chroma 0.005 → 0.01
|
|
- Delete `--color-chart-1` through `--color-chart-5`
|
|
- Add `.recharts-rectangle` and `[data-sonner-toast]` CSS overrides
|
|
|
|
**Wave 2 — Chart component updates** (2 files)
|
|
- `SpendBarChart.tsx`: `radius={4}` → `radius={0}` (x2 Bar props)
|
|
- `IncomeBarChart.tsx`: `radius={[4, 4, 0, 0]}` → `radius={0}` (x2 Bar props)
|
|
- `ExpenseDonutChart.tsx`: remove `rounded-full` from legend span (line 141)
|
|
|
|
**Wave 3 — Page and shared component sweep** (12 files)
|
|
- `PageShell.tsx`: `gap-6` → `gap-8`
|
|
- `DashboardSkeleton.tsx`: remove 5 `rounded-*` classes, upgrade 3 spacing values
|
|
- `CategorySection.tsx`: remove `rounded-md` from trigger button
|
|
- `ChartEmptyState.tsx`: remove `rounded-lg` from empty state div
|
|
- `QuickAddPicker.tsx`: remove 2 `rounded-*` classes
|
|
- All 9 pages: spacing upgrades + hardcoded `rounded-*` removals per inventory
|
|
|
|
---
|
|
|
|
## Metadata
|
|
|
|
**Analog search scope:** `src/pages/`, `src/components/dashboard/`, `src/components/shared/`, `src/components/ui/`, `src/index.css`
|
|
**Files read for pattern extraction:** 16 (index.css, PageShell, DashboardPage, DashboardSkeleton, SpendBarChart, IncomeBarChart, ExpenseDonutChart, ChartEmptyState, CategorySection, QuickAddPicker, SettingsPage, BudgetDetailPage excerpt x3, CategoriesPage excerpt, TemplatePage excerpt, QuickAddPage excerpt, BudgetListPage excerpt, sonner.tsx)
|
|
**Pattern extraction date:** 2026-04-20
|