Files
SimpleFinanceDash/.planning/phases/05-design-system-token-rework/05-RESEARCH.md
2026-04-20 16:44:11 +02:00

30 KiB
Raw Blame History

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>

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 </user_constraints>


<phase_requirements>

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-6space-y-8, gap-6gap-8 across 9 pages; p-4p-6 for card internals; standardize PageShell gap to gap-8
</phase_requirements>

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 }

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:

/* 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:

/* src/index.css — in @layer base block or after @theme */
/* Recharts: bars are SVG <rect> 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:

/* Before — in ExpenseDonutChart.tsx line 141 */
<span className="inline-block size-3 shrink-0 rounded-full" ... />

/* After */
<span className="inline-block size-3 shrink-0" ... />
/* 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:

/* IncomeBarChart.tsx — Bar radius change */
<Bar dataKey="budgeted" fill="var(--color-budgeted)" radius={0} />
<Bar dataKey="actual" radius={0}>

/* SpendBarChart.tsx — Bar radius change */
<Bar dataKey="budgeted" fill="var(--color-budgeted)" radius={0} />
<Bar dataKey="actual" radius={0}>

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-4p-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 <Skeleton> 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 <Skeleton> 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: <Bar radius={4}> and <Bar radius={[4, 4, 0, 0]}> 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 <rect rx="4" ry="4"> 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 <Bar> 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 <span className="inline-block size-3 shrink-0 rounded-full"> 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)

/* 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

// 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
<Bar dataKey="budgeted" fill="var(--color-budgeted)" radius={0} />
<Bar dataKey="actual" radius={0}>
  {/* Cell fill already uses var(--color-${entry.type}-fill) — correct */}
</Bar>

[VERIFIED: src/components/dashboard/charts/SpendBarChart.tsx]

IncomeBarChart.tsx Post-Rework

// 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
<Bar dataKey="budgeted" fill="var(--color-budgeted)" radius={0} />
<Bar dataKey="actual" radius={0}>

[VERIFIED: src/components/dashboard/charts/IncomeBarChart.tsx]

PageShell Spacing Upgrade

// Source: src/components/shared/PageShell.tsx — spacing change
// Before:
<div className="flex flex-col gap-6">

// After:
<div className="flex flex-col gap-8">

[VERIFIED: src/components/shared/PageShell.tsx]

DashboardPage Section Spacing Upgrade

// Source: src/pages/DashboardPage.tsx — key spacing changes
// Before: <div className="space-y-6">
// After:  <div className="space-y-8">

// Before: <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
// After:  <div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">

[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 <Bar> (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-6space-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)