# Phase 5: Design System Token Rework - Research **Researched:** 2026-04-20 **Domain:** CSS design tokens, Tailwind CSS 4 `@theme inline`, OKLCH color, shadcn/ui, Recharts, Sonner **Confidence:** HIGH --- ## User Constraints (from CONTEXT.md) ### Locked Decisions - All elements get 0px border radius — truly sharp corners everywhere - Implementation via single `--radius: 0` token change in `src/index.css` (cascades to all shadcn components) - No exceptions for avatars, badges, or any element - Third-party components (Recharts bars, Sonner toasts) overridden via CSS selectors to force 0 radius - Increase chroma on category fill colors to 0.22+ for visible colorful pop against white backgrounds - Keep the two-tier color system: text colors at ~0.55L for WCAG 4.5:1 contrast, fill colors lighter/more saturated - Slightly warm the base UI colors: background chroma from 0.005→0.01 for subtle warmth - Align chart colors with category fill tokens directly — remove separate `--color-chart-*` variables and use `--color-*-fill` tokens in charts - Increase section gaps: gap-4→gap-6, gap-6→gap-8 across all pages - Increase card internal padding: p-4→p-6 for breathing room - Keep existing max-w-7xl container constraint - Standardize all page headers to mb-6 + section gaps to gap-8 for consistent rhythm ### Claude's Discretion - Exact OKLCH chroma/lightness values for category fills (as long as they're 0.22+ chroma and pass WCAG) - Order of page updates during implementation - Whether to create a spacing utility class or apply changes inline ### Deferred Ideas (OUT OF SCOPE) None — discussion stayed within phase scope --- ## Phase Requirements | ID | Description | Research Support | |----|-------------|------------------| | DS-01 | User sees sharp-edged UI across all pages (no rounded corners) | Single `--radius: 0` token in index.css cascades to all shadcn/ui components; hardcoded `rounded-*` Tailwind classes in pages/components require explicit removal; Recharts bars and Sonner toasts require CSS override selectors | | DS-02 | User sees clear pastel colors that are visibly colorful, not washed out | Raise `--color-*-fill` chroma from 0.18-0.20 to 0.22+; remove `--color-chart-*` variables; point chart components to `--color-*-fill` tokens directly | | DS-03 | User sees a clean, minimal layout with generous whitespace | Upgrade `space-y-6`→`space-y-8`, `gap-6`→`gap-8` across 9 pages; `p-4`→`p-6` for card internals; standardize PageShell gap to `gap-8` | --- ## Summary Phase 5 is a pure design token and spacing rework with zero feature additions. The codebase uses Tailwind CSS 4's `@theme inline` block in `src/index.css` as the single source of truth for all CSS custom properties. Because shadcn/ui components reference `--radius` via Tailwind's `rounded-*` utility classes that are generated from this token, setting `--radius: 0` in `src/index.css` propagates automatically to every shadcn component. No changes to `src/components/ui/` files are needed for the radius. However, hardcoded `rounded-*` Tailwind classes exist in 6 page files and 4 component files — these are not driven by `--radius` and must be removed or changed to `rounded-none` explicitly. Additionally, Recharts bar components pass a `radius` prop (not CSS) and require a CSS selector override in `index.css`. Sonner toasts pass `--border-radius` as an inline style through the Toaster component and additionally need a CSS override. The color work has two parts: (1) editing token values in `src/index.css` only, and (2) removing `--color-chart-*` variables from `index.css` and updating the two chart components that currently reference them via `ChartConfig` objects to instead reference `--color-*-fill` tokens. **Primary recommendation:** Execute as three focused waves — (1) token edit in `index.css` (radius + colors + remove chart vars), (2) chart component updates to reference fill tokens, (3) per-page spacing and hardcoded `rounded-*` sweep across all 9 pages. --- ## Architectural Responsibility Map | Capability | Primary Tier | Secondary Tier | Rationale | |------------|-------------|----------------|-----------| | Design token definition | CSS (index.css) | — | Single `@theme inline` block is the authoritative token source | | shadcn component rounding | CSS token cascade | — | `rounded-*` utilities derive from `--radius`; no component code change needed | | Hardcoded Tailwind class removal | Page/component TSX | — | `rounded-full`, `rounded-md` etc. are inline JSX; must be changed in source files | | Recharts bar radius override | CSS global selector | Recharts prop | CSS `rect` selector overrides SVG radius attribute; prop change is alternative | | Sonner toast radius override | CSS global selector | Sonner component | Inline `--border-radius` style in sonner.tsx; CSS override wins | | Color token values | CSS (index.css) | — | All category and fill colors live in `@theme inline` | | Chart color wiring | Chart TSX components | CSS tokens | `ChartConfig` objects in SpendBarChart and IncomeBarChart must reference fill tokens | | Page spacing | Page TSX files | Shared components | `gap-*`, `space-y-*`, `p-*` are inline classes in page JSX | --- ## Standard Stack ### Core | Library | Version | Purpose | Why Standard | |---------|---------|---------|--------------| | Tailwind CSS | 4.2.1 | Utility CSS + token system (`@theme inline`) | Already installed; `@theme inline` is the v4 way to define CSS variables as Tailwind utilities | | shadcn/ui | new-york preset | Component layer above Radix UI | Already installed; all components in `src/components/ui/` | | Recharts | 2.15.4 | Chart rendering (BarChart, PieChart) | Already installed; SpendBarChart, IncomeBarChart, ExpenseDonutChart use it | | Sonner | 2.0.7 | Toast notifications | Already installed; `src/components/ui/sonner.tsx` wraps it | ### Supporting | Library | Version | Purpose | When to Use | |---------|---------|---------|-------------| | OKLCH (native CSS) | CSS Color Level 4 | Perceptually uniform color space | Already in use; all token edits stay in OKLCH | No new libraries are needed for this phase. **Installation:** None required. --- ## Architecture Patterns ### System Architecture Diagram ``` src/index.css (@theme inline) │ ├── --radius: 0 ──cascade──► all shadcn rounded-* classes ├── --color-background: ... ──cascade──► bg-background utility ├── --color-*-fill: ... ──cascade──► var(--color-*-fill) in chart components └── [--color-chart-* REMOVED] │ ▼ src/components/ui/*.tsx (shadcn) │ Button, Card, Input, Badge, Select, Dialog, Sheet, Sidebar, ... │ All use rounded-* utilities → auto-sharp from --radius: 0 ▼ src/components/dashboard/charts/*.tsx │ SpendBarChart, IncomeBarChart: update ChartConfig + Bar radius prop → 0 │ ExpenseDonutChart: already uses var(--color-*-fill); no ChartConfig change needed ▼ src/pages/*.tsx (9 pages) │ Remove hardcoded rounded-* classes │ Upgrade spacing: space-y-6→space-y-8, gap-6→gap-8, p-4→p-6 ▼ src/index.css (@layer base or global) └── CSS overrides for third-party radius ├── .recharts-rectangle { rx: 0; ry: 0; border-radius: 0 } └── [data-sonner-toast] { border-radius: 0 } ``` ### Recommended Project Structure No structural changes. Files modified: ``` src/ ├── index.css # Token edits: --radius, --color-*-fill, remove --color-chart-*; CSS overrides ├── components/ │ └── dashboard/ │ └── charts/ │ ├── SpendBarChart.tsx # Update ChartConfig + Bar radius prop │ └── IncomeBarChart.tsx # Update ChartConfig + Bar radius prop └── pages/ ├── DashboardPage.tsx # Spacing: space-y-6→space-y-8, gap-6→gap-8 ├── BudgetListPage.tsx # Spacing + remove rounded-md from row div ├── BudgetDetailPage.tsx # Spacing + remove rounded-* classes ├── TemplatePage.tsx # Spacing + remove rounded-sm, rounded-full ├── CategoriesPage.tsx # Spacing + remove rounded-sm, rounded-full ├── QuickAddPage.tsx # Remove rounded-full, rounded-md ├── SettingsPage.tsx # Spacing: space-y-4→space-y-6 in card content ├── LoginPage.tsx # Card already uses --radius token cascade └── RegisterPage.tsx # Card already uses --radius token cascade ``` ### Pattern 1: Tailwind 4 `@theme inline` Token Edit **What:** All CSS variables in the `@theme inline` block are automatically available as Tailwind utility classes. Changing a value in this block propagates everywhere the utility is used. **When to use:** Changing `--radius`, color tokens, any design-system-level property. **Example:** ```css /* src/index.css */ @theme inline { --radius: 0; /* was 0.625rem — single change, cascades everywhere */ /* Fill colors: raise chroma to 0.22+ */ --color-income-fill: oklch(0.72 0.22 155); --color-bill-fill: oklch(0.70 0.22 25); --color-variable-expense-fill: oklch(0.74 0.22 50); --color-debt-fill: oklch(0.66 0.23 355); --color-saving-fill: oklch(0.72 0.22 220); --color-investment-fill: oklch(0.68 0.22 285); /* Background: warm slightly */ --color-background: oklch(0.98 0.01 260); /* Remove --color-chart-1 through --color-chart-5 entirely */ } ``` [VERIFIED: codebase — src/index.css lines 1-99] ### Pattern 2: CSS Selector Override for Third-Party Radius **What:** When a third-party library applies border-radius via its own SVG attributes or inline styles, a targeted CSS selector override in `@layer base` in `index.css` forces the desired value. **When to use:** Recharts bar SVG rectangles, Sonner toast container. **Example:** ```css /* src/index.css — in @layer base block or after @theme */ /* Recharts: bars are SVG elements; CSS border-radius overrides SVG rx/ry */ .recharts-rectangle { rx: 0; ry: 0; } /* Sonner: targets the toast wrapper element */ [data-sonner-toast] { border-radius: 0 !important; } ``` [ASSUMED — selector names from Recharts/Sonner DOM inspection convention; verify in browser devtools during implementation] ### Pattern 3: Hardcoded Tailwind Class Removal **What:** `rounded-full`, `rounded-md`, `rounded-sm`, `rounded-lg` as inline className strings in TSX are not driven by `--radius`. They must be removed or replaced with `rounded-none`. **When to use:** Category color swatches, skeleton shapes, chart legend dots, picker items. **Example:** ```tsx /* Before — in ExpenseDonutChart.tsx line 141 */ /* After */ /* or explicitly: rounded-none if you need to reset a parent's rounded */ ``` [VERIFIED: codebase scan] ### Pattern 4: Chart Component Token Wiring **What:** `SpendBarChart` and `IncomeBarChart` use `ChartConfig` objects to map data keys to colors. Currently `IncomeBarChart.actual` is `color: "var(--color-income-fill)"` — already correct. `SpendBarChart.budgeted` uses `--color-budget-bar-bg` and `actual` uses `--color-muted-foreground`. `Bar radius` props must go to 0. **When to use:** Any chart using Recharts `Bar` component. **Example:** ```tsx /* IncomeBarChart.tsx — Bar radius change */ /* SpendBarChart.tsx — Bar radius change */ ``` Note: `SpendBarChart` uses `Cell` with dynamic `var(--color-${entry.type}-fill)` — this already references fill tokens, which is correct post-rework. No ChartConfig color change needed there. [VERIFIED: codebase — SpendBarChart.tsx, IncomeBarChart.tsx] ### Pattern 5: Spacing Upgrade Pattern **What:** Page-level spacing classes that control section separation and card internal rhythm are upgraded in-place in each page's JSX. **When to use:** Each of the 9 pages. **Mapping:** ``` space-y-6 → space-y-8 (section-level vertical rhythm in pages) gap-6 → gap-8 (grid gaps between card sections) gap-4 → gap-6 (inner grid element gaps where currently gap-4) ``` Card internal padding (`p-4` → `p-6`) applies to card content divs in pages, not to `src/components/ui/card.tsx` directly (Card already uses `py-6` and `CardContent` uses `px-6`). [VERIFIED: codebase — PageShell.tsx, DashboardPage.tsx, BudgetDetailPage.tsx] ### Anti-Patterns to Avoid - **Editing shadcn component files for radius:** `src/components/ui/*.tsx` do NOT need edits for the radius change — the token cascade handles it. Editing them directly risks breaking shadcn's upgrade path. - **Changing `src/components/ui/card.tsx` padding:** `CardContent` already uses `px-6`. The `p-4 → p-6` upgrade applies to page-level wrappers that add inner padding on top of card defaults, not to the Card component itself. - **Adding new `rounded-none` classes broadly:** Prefer removing the hardcoded `rounded-*` class entirely over adding `rounded-none`, since `--radius: 0` already means all `rounded-md`/`rounded-sm` Tailwind classes resolve to 0px. Only add `rounded-none` if you need to explicitly reset a parent that might not be covered. - **Relying on `--color-chart-*` variables in new code:** These are being deleted this phase. All chart coloring must reference `--color-*-fill` tokens going forward. --- ## Don't Hand-Roll | Problem | Don't Build | Use Instead | Why | |---------|-------------|-------------|-----| | WCAG contrast calculation | Manual contrast ratio math | Use UI-SPEC.md confirmed values; verify with browser devtools color picker | OKLCH values already computed and confirmed in CONTEXT.md and UI-SPEC.md | | CSS-in-JS color token system | Runtime token object | Tailwind 4 `@theme inline` already does this | Single file, zero runtime cost | | Per-component radius reset | Adding `style={{ borderRadius: 0 }}` to every element | `--radius: 0` token | Token cascade handles all shadcn components in one edit | | Custom chart color mapping | Separate color registry | Existing `var(--color-*-fill)` CSS variables | Already wired in ExpenseDonutChart; standardize SpendBarChart/IncomeBarChart to match | --- ## Hardcoded `rounded-*` Inventory Complete list of non-token-driven rounding that requires manual removal, organized by file: ### Pages | File | Line | Class | Action | |------|------|-------|--------| | CategoriesPage.tsx | 101 | `rounded-sm` (group header div) | Remove | | CategoriesPage.tsx | 107 | `rounded-full` (Skeleton) | Remove | | CategoriesPage.tsx | 108 | `rounded-md` (Skeleton) | Remove | | CategoriesPage.tsx | 134 | `rounded-sm` (category header) | Remove | | TemplatePage.tsx | 250 | `rounded-sm` (group header div) | Remove | | TemplatePage.tsx | 256 | `rounded-full` (Skeleton) | Remove | | TemplatePage.tsx | 258 | `rounded-md` (Skeleton) | Remove | | TemplatePage.tsx | 292 | `rounded-sm` (template item header) | Remove | | TemplatePage.tsx | 385 | `rounded-full` (color swatch dot) | Remove | | QuickAddPage.tsx | 98 | `rounded-full` (Skeleton) | Remove | | QuickAddPage.tsx | 100 | `rounded-md` (Skeleton) | Remove | | BudgetListPage.tsx | 243 | `rounded-md` (row container div) | Remove | | BudgetDetailPage.tsx | 290 | `rounded-sm` (skeleton header) | Remove | | BudgetDetailPage.tsx | 303 | `rounded-md` (Skeleton) | Remove | | BudgetDetailPage.tsx | 353 | `rounded-sm` (budget item header) | Remove | | BudgetDetailPage.tsx | 439 | `rounded-md` (summary box) | Remove | | BudgetDetailPage.tsx | 497 | `rounded-full` (color swatch dot) | Remove | ### Components | File | Line | Class | Action | |------|------|-------|--------| | CategorySection.tsx | 73 | `rounded-md` (collapsible trigger button) | Remove | | DashboardSkeleton.tsx | 35,43,51 | `rounded-md` (Skeleton placeholders) | Remove | | DashboardSkeleton.tsx | 59 | `rounded-md` (row placeholder) | Remove | | DashboardSkeleton.tsx | 63,64 | `rounded-full` (Skeleton) | Remove | | ChartEmptyState.tsx | 12 | `rounded-lg` (empty state border) | Remove | | ExpenseDonutChart.tsx | 141 | `rounded-full` (legend color dot) | Remove | | QuickAddPicker.tsx | 156 | `rounded-sm` (picker item) | Remove | | QuickAddPicker.tsx | 201 | `rounded-full` (category dot) | Remove | **Note on Skeleton components:** `Skeleton` in `src/components/ui/skeleton.tsx` has `rounded-md` in its base class (`animate-pulse rounded-md bg-accent`). When `--radius: 0`, `rounded-md` resolves to 0px automatically — skeleton rounding is already handled by the token. The hardcoded `rounded-full` overrides on individual `` usages (which pass `className` that overrides the base) must still be removed to avoid pill-shaped skeletons. --- ## Common Pitfalls ### Pitfall 1: Skeleton `rounded-full` Overrides Persist **What goes wrong:** After `--radius: 0`, the base `Skeleton` component becomes square. But in multiple pages, Skeleton elements are given `className="h-5 w-16 rounded-full"`. Since `rounded-full` uses a fixed `border-radius: 9999px` that does not derive from `--radius`, those skeleton placeholders remain pill-shaped. **Why it happens:** `rounded-full` in Tailwind is always `9999px` regardless of the `--radius` token. Only `rounded-md`, `rounded-sm`, `rounded-lg`, `rounded-xl` etc. derive from `--radius`. **How to avoid:** Remove `rounded-full` from every `className` passed to `` as listed in the inventory above. **Warning signs:** Pill-shaped loading placeholders visible on BudgetDetail, Template, Categories, QuickAdd pages after the token change. ### Pitfall 2: Recharts `radius` Prop vs. CSS **What goes wrong:** `` and `` are SVG attribute props, not CSS. Setting `--radius: 0` does not affect them. CSS overrides on `.recharts-rectangle` may also need `rx: 0; ry: 0` as SVG presentation attributes. **Why it happens:** Recharts renders bars as `` SVG elements. CSS `border-radius` on SVG `rect` has browser-level quirks — some browsers respect it, others require the `rx`/`ry` attributes. **How to avoid:** Change the `radius` prop to `0` directly on `` in both `SpendBarChart.tsx` and `IncomeBarChart.tsx`. This is the most reliable fix. **Warning signs:** Chart bars still have rounded caps after CSS override but not prop change. ### Pitfall 3: Sonner Toast Radius Override Specificity **What goes wrong:** Sonner's Toaster component passes `"--border-radius": "var(--radius)"` as an inline `style` prop. After setting `--radius: 0`, this should automatically propagate. However, Sonner also has its own internal styles. **Why it happens:** The sonner.tsx wrapper already wires `--border-radius` to `var(--radius)`. When `--radius` becomes `0`, this should cascade automatically. The risk is Sonner's own stylesheet having higher specificity. **How to avoid:** Verify in browser after `--radius: 0` change. If toasts are still rounded, add `[data-sonner-toast] { border-radius: 0 !important; }` to `index.css`. **Warning signs:** Toast notifications still display with rounded corners after `--radius: 0` edit. ### Pitfall 4: `--color-chart-*` Still Referenced After Deletion **What goes wrong:** After removing `--color-chart-1` through `--color-chart-5` from `index.css`, if any component still references them the color falls back to transparent/browser default (usually black or undefined). **Why it happens:** The variables are only defined in one place but could theoretically be referenced in multiple chart config objects. **How to avoid:** Before deleting, grep the entire `src/` directory for `color-chart` to confirm only `index.css` references them (already confirmed — zero TSX references found). Safe to delete. **Warning signs:** Charts render with black or missing colors. ### Pitfall 5: `PageShell` `gap-6` Governs All Page Layouts **What goes wrong:** `PageShell` uses `flex flex-col gap-6`. Because it wraps most pages, the gap between the page header and first content section is controlled here. Updating spacing in individual pages but not in `PageShell` creates inconsistency. **Why it happens:** PageShell is a shared wrapper — spacing changes to individual pages don't affect header-to-content gap unless PageShell is also updated. **How to avoid:** Update `PageShell` from `gap-6` to `gap-8` as part of the spacing wave. All pages using `PageShell` benefit automatically. **Warning signs:** Pages with `PageShell` wrapper still show tight header-to-content gap despite page-level spacing upgrades. ### Pitfall 6: Donut Chart Legend Dots Remain Circular **What goes wrong:** The custom legend in `ExpenseDonutChart.tsx` uses `` for category color dots. These remain pill/circle shaped because `rounded-full` is hardcoded. **Why it happens:** The legend is custom HTML, not a Recharts component — token cascade doesn't reach it. **How to avoid:** Remove `rounded-full` from the legend span. A square dot of 3×3px is intentionally square in the sharp design aesthetic. **Warning signs:** Circular color swatches in the donut chart legend after the rework. --- ## Code Examples ### index.css Token Block (post-rework shape) ```css /* Source: src/index.css — annotated for phase 5 changes */ @theme inline { /* UI warmth — chroma 0.005 → 0.01 */ --color-background: oklch(0.98 0.01 260); /* ... other base tokens unchanged ... */ /* Category text colors — UNCHANGED (already WCAG 4.5:1) */ --color-income: oklch(0.55 0.17 155); /* ... */ /* Category fill colors — chroma raised to 0.22+ */ --color-income-fill: oklch(0.72 0.22 155); --color-bill-fill: oklch(0.70 0.22 25); --color-variable-expense-fill: oklch(0.74 0.22 50); --color-debt-fill: oklch(0.66 0.23 355); --color-saving-fill: oklch(0.72 0.22 220); --color-investment-fill: oklch(0.68 0.22 285); /* --color-chart-1 through --color-chart-5: DELETED */ --radius: 0; /* was 0.625rem */ } ``` [VERIFIED: src/index.css] ### SpendBarChart.tsx Post-Rework ```tsx // Source: src/components/dashboard/charts/SpendBarChart.tsx — key changes const chartConfig = { budgeted: { label: "Budgeted", color: "var(--color-budget-bar-bg)" }, actual: { label: "Actual", color: "var(--color-muted-foreground)" }, } satisfies ChartConfig // Bar radius props: 4 → 0 {/* Cell fill already uses var(--color-${entry.type}-fill) — correct */} ``` [VERIFIED: src/components/dashboard/charts/SpendBarChart.tsx] ### IncomeBarChart.tsx Post-Rework ```tsx // Source: src/components/dashboard/charts/IncomeBarChart.tsx — key changes const chartConfig = { budgeted: { label: "Budgeted", color: "var(--color-budget-bar-bg)" }, actual: { label: "Actual", color: "var(--color-income-fill)" }, // unchanged — already correct } satisfies ChartConfig // Bar radius props: [4, 4, 0, 0] → 0 ``` [VERIFIED: src/components/dashboard/charts/IncomeBarChart.tsx] ### PageShell Spacing Upgrade ```tsx // Source: src/components/shared/PageShell.tsx — spacing change // Before:
// After:
``` [VERIFIED: src/components/shared/PageShell.tsx] ### DashboardPage Section Spacing Upgrade ```tsx // Source: src/pages/DashboardPage.tsx — key spacing changes // Before:
// After:
// Before:
// After:
``` [VERIFIED: src/pages/DashboardPage.tsx lines 186, 207] --- ## State of the Art | Old Approach | Current Approach | When Changed | Impact | |--------------|------------------|--------------|--------| | Separate `--color-chart-*` variables | Reference `--color-*-fill` directly | This phase | Eliminates color duplication; single source of truth per category | | `--radius: 0.625rem` | `--radius: 0` | This phase | All shadcn components become sharp-cornered | | Fill chroma 0.18-0.20 | Fill chroma 0.22+ | This phase | Visually saturated pastels that read as colorful, not grey | | `gap-6` / `space-y-6` sections | `gap-8` / `space-y-8` | This phase | More generous breathing room between content sections | --- ## Assumptions Log | # | Claim | Section | Risk if Wrong | |---|-------|---------|---------------| | A1 | Sonner toast `--border-radius: var(--radius)` inline style in sonner.tsx will propagate `--radius: 0` correctly without additional CSS override | Pitfall 3 / Pattern 2 | If wrong, toasts remain rounded; mitigation: add `[data-sonner-toast] { border-radius: 0 !important }` as CSS override | | A2 | CSS `rx: 0; ry: 0` on `.recharts-rectangle` overrides SVG presentation attributes cross-browser | Pitfall 2 | If wrong, bars may still show rounding in some browsers; mitigation: change `radius` prop to 0 on `` (reliable) | | A3 | No `--color-chart-*` references exist in any TSX file (confirmed by grep returning zero results) | Don't Hand-Roll | LOW risk — grep confirmed no TSX references | --- ## Open Questions 1. **Sonner version 2.x selector name for border-radius override** - What we know: Sonner 2.0.7 is installed; `[data-sonner-toast]` is the conventional selector from v1/v2 - What's unclear: Whether v2.x changed the data attribute name on the toast wrapper - Recommendation: Verify in browser devtools after `--radius: 0` change; if toasts remain rounded, inspect the DOM element for the correct attribute selector 2. **`space-y-6` vs `gap-6` on TemplatePage and BudgetDetailPage** - What we know: These pages use `space-y-6` for section stacking (not `gap-*` which requires flex/grid parent) - What's unclear: Whether changing `space-y-6` → `space-y-8` alone is sufficient or whether the wrapping flex container also needs `gap-8` - Recommendation: Apply `space-y-8` where currently `space-y-6`; apply `gap-8` where currently `gap-6` in flex/grid containers — both changes are needed depending on context --- ## Environment Availability Step 2.6: SKIPPED — Phase 5 is purely CSS/TSX file edits with no external dependencies, database changes, or CLI tools beyond the existing Vite dev server. --- ## Validation Architecture ### Test Framework | Property | Value | |----------|-------| | Framework | None detected — no jest.config.*, vitest.config.*, pytest.ini found | | Config file | None — Wave 0 gap | | Quick run command | `npm run build` (TypeScript compile check) | | Full suite command | Manual visual pass across 9 pages in browser | ### Phase Requirements → Test Map | Req ID | Behavior | Test Type | Automated Command | File Exists? | |--------|----------|-----------|-------------------|-------------| | DS-01 | No rounded corners visible anywhere | Manual (visual) | `npm run build` (compile check only) | N/A — visual | | DS-02 | Category fills visibly colorful, not washed out | Manual (visual) | `npm run build` | N/A — visual | | DS-03 | Generous whitespace, no visual crowding | Manual (visual) | `npm run build` | N/A — visual | ### Sampling Rate - **Per task commit:** `npm run build` — confirms no TypeScript errors - **Per wave merge:** `npm run build` + manual browser check of affected pages - **Phase gate:** Full 9-page visual pass per the UI-SPEC checklist before `/gsd-verify-work` ### Wave 0 Gaps - [ ] No unit/integration test infrastructure exists — this phase is purely visual; manual browser verification is the acceptance gate - [ ] Consider running `npm run lint` as a secondary automated check --- ## Security Domain Security enforcement: not applicable. Phase 5 introduces zero new features, endpoints, authentication flows, user input handling, or data access. No ASVS categories apply. This is a pure CSS token and spacing change. --- ## Sources ### Primary (HIGH confidence) - `src/index.css` (codebase) — Full token inventory, current `--radius` value, all OKLCH color values, `--color-chart-*` variables - `src/components/ui/` (codebase) — shadcn component source confirming `rounded-*` class usage, `sonner.tsx` Toaster inline style wiring - `src/components/dashboard/charts/` (codebase) — Recharts `Bar` radius prop values, ChartConfig objects, Cell fill patterns - `src/pages/` (codebase) — All 9 pages audited for `gap-*`, `space-y-*`, `rounded-*` classes - `05-UI-SPEC.md` (project planning) — Confirmed post-rework color values, spacing targets, component inventory ### Secondary (MEDIUM confidence) - Tailwind CSS 4 `@theme inline` documentation — `rounded-md` etc. derive from `--radius` token; `rounded-full` does not ### Tertiary (LOW confidence — A1, A2 in assumptions log) - Sonner 2.x data attribute selector name (`[data-sonner-toast]`) — based on conventional usage; verify in browser - Recharts CSS `rx`/`ry` override cross-browser behavior — recommend prop change as primary approach --- ## Metadata **Confidence breakdown:** - Token edit scope (index.css): HIGH — full file read, all variables inventoried - Hardcoded rounded-* inventory: HIGH — exhaustive grep of src/pages + src/components - Spacing upgrade targets: HIGH — grep confirmed current class values in all 9 pages - Third-party overrides (Recharts, Sonner): MEDIUM — prop-based fix is reliable; CSS override selector needs browser verification - Color OKLCH values: HIGH — values provided in CONTEXT.md and UI-SPEC.md **Research date:** 2026-04-20 **Valid until:** 2026-05-20 (stable stack — Tailwind 4, Recharts 2.15, Sonner 2.x)