docs(phase-02): research layout and brand identity phase
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
440
.planning/phases/02-layout-and-brand-identity/02-RESEARCH.md
Normal file
440
.planning/phases/02-layout-and-brand-identity/02-RESEARCH.md
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
# Phase 2: Layout and Brand Identity - Research
|
||||||
|
|
||||||
|
**Researched:** 2026-03-11
|
||||||
|
**Domain:** React UI polish — auth screen branding, shadcn sidebar customization, Tailwind CSS v4 token-based styling
|
||||||
|
**Confidence:** HIGH
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<phase_requirements>
|
||||||
|
## Phase Requirements
|
||||||
|
|
||||||
|
| ID | Description | Research Support |
|
||||||
|
|----|-------------|-----------------|
|
||||||
|
| AUTH-01 | Login screen has a branded pastel gradient background (not plain white) | Replace `bg-background` wrapper with gradient using palette tokens; full-bleed gradient div wrapping the Card |
|
||||||
|
| AUTH-02 | Login screen displays a styled app wordmark/logo treatment | Replace plain `CardTitle` text with a styled typographic mark using font weight + letter-spacing or gradient text fill |
|
||||||
|
| AUTH-03 | Register screen matches login screen's branded look | Same structural changes as AUTH-01/02 applied to `RegisterPage.tsx` |
|
||||||
|
| AUTH-04 | Auth form errors display with styled alert blocks and error icons | Install shadcn `alert` component; replace `<p className="text-sm text-destructive">` with `<Alert variant="destructive">` |
|
||||||
|
| NAV-01 | Sidebar has a pastel background color distinct from the main content area | `--sidebar` token already set to `oklch(0.97 0.012 280)` in index.css; sidebar.tsx uses `bg-sidebar` natively — need to verify contrast reads in browser |
|
||||||
|
| NAV-02 | Sidebar app name has a branded typographic treatment (not plain h2) | Replace `<h2 className="text-lg font-semibold">` with richer markup: gradient text, tracking-wide, or a custom wordmark span |
|
||||||
|
| NAV-03 | Active navigation item has a clearly visible color indicator using sidebar-primary token | `SidebarMenuButton` receives `isActive` prop — need to verify the `isActive` styling is visible; may need to override via className |
|
||||||
|
| NAV-04 | Sidebar is collapsible via a toggle button for smaller screens | `SidebarTrigger` component already exists and exported from `ui/sidebar.tsx`; needs to be placed in the layout (currently missing from `AppLayout.tsx`) |
|
||||||
|
</phase_requirements>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Phase 2 focuses on two surface areas: the auth screens (LoginPage, RegisterPage) and the app shell (AppLayout with the shadcn Sidebar). Both are self-contained files with minimal external dependencies, so the risk profile is low.
|
||||||
|
|
||||||
|
The auth screens currently render a plain white Card centered on `bg-background`. Both pages use the same pattern and can be updated in parallel. The wordmark treatment (AUTH-02) is pure CSS — no new libraries needed, just a styled span with gradient text or tracked lettering using existing font/color tokens. The error display (AUTH-04) requires installing the shadcn `alert` component, which does not exist in `frontend/src/components/ui/` yet.
|
||||||
|
|
||||||
|
The sidebar is already fully wired with the shadcn Sidebar primitives including the `--sidebar` CSS token (set to a distinct pastel `oklch(0.97 0.012 280)`). The main gap is NAV-04: `SidebarTrigger` is exported from `ui/sidebar.tsx` but is not rendered anywhere in `AppLayout.tsx`. The active-state indicator (NAV-03) is passed via `isActive` to `SidebarMenuButton` — the shadcn component has built-in active styling, but visual verification is needed since the token values may produce low contrast.
|
||||||
|
|
||||||
|
**Primary recommendation:** Add shadcn Alert, apply gradient backgrounds to auth pages, add `SidebarTrigger` to AppLayout, and style the sidebar wordmark using CSS gradient text — all within existing files with no new routing or data fetching.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Standard Stack
|
||||||
|
|
||||||
|
### Core
|
||||||
|
| Library | Version | Purpose | Why Standard |
|
||||||
|
|---------|---------|---------|--------------|
|
||||||
|
| shadcn/ui | 4.0.0 (installed) | UI component primitives via CSS variables | Project constraint: all UI via shadcn |
|
||||||
|
| Tailwind CSS v4 | 4.2.1 (installed) | Utility classes; `@theme inline` exposes CSS vars as utilities | Project constraint |
|
||||||
|
| lucide-react | 0.577.0 (installed) | Icon set including `AlertCircle`, `PanelLeft` | Already used throughout |
|
||||||
|
| tw-animate-css | 1.4.0 (installed) | Animation utilities (for any transitions) | Out-of-scope alternative to Framer Motion |
|
||||||
|
|
||||||
|
### Supporting
|
||||||
|
| Library | Version | Purpose | When to Use |
|
||||||
|
|---------|---------|---------|-------------|
|
||||||
|
| class-variance-authority | 0.7.1 (installed) | Variant-based className logic | If extracting a reusable wordmark component |
|
||||||
|
| clsx + tailwind-merge | installed | Conditional class merging | Standard in this project via `cn()` utility |
|
||||||
|
|
||||||
|
### Not Needed for This Phase
|
||||||
|
- No new npm installs required except `npx shadcn add alert` (fetches from registry, not a new npm dep)
|
||||||
|
- No animation library installs — tw-animate-css handles any needed transitions
|
||||||
|
- No routing changes
|
||||||
|
|
||||||
|
**Installation:**
|
||||||
|
```bash
|
||||||
|
cd frontend && bunx --bun shadcn add alert
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Patterns
|
||||||
|
|
||||||
|
### Recommended File Scope
|
||||||
|
|
||||||
|
All work is confined to these existing files:
|
||||||
|
```
|
||||||
|
frontend/src/
|
||||||
|
├── pages/
|
||||||
|
│ ├── LoginPage.tsx # AUTH-01, AUTH-02, AUTH-04
|
||||||
|
│ └── RegisterPage.tsx # AUTH-03, AUTH-04
|
||||||
|
├── components/
|
||||||
|
│ ├── AppLayout.tsx # NAV-01, NAV-02, NAV-03, NAV-04
|
||||||
|
│ └── ui/
|
||||||
|
│ └── alert.tsx # NEW — installed via shadcn CLI
|
||||||
|
└── index.css # No changes needed (tokens already set)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 1: Gradient Background Wrapper (AUTH-01, AUTH-03)
|
||||||
|
|
||||||
|
**What:** Replace the `bg-background` wrapper div on auth pages with a full-bleed gradient using existing palette tokens via inline style or Tailwind arbitrary values.
|
||||||
|
|
||||||
|
**When to use:** Full-screen auth layouts where background IS the brand statement.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tsx
|
||||||
|
// Replace:
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-background">
|
||||||
|
|
||||||
|
// With (using CSS custom properties from index.css):
|
||||||
|
<div
|
||||||
|
className="flex min-h-screen items-center justify-center"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, oklch(0.96 0.03 280), oklch(0.94 0.04 260), oklch(0.96 0.04 320))`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
The gradient values should pull from the `palette.ts` saving/bill/investment light shades to stay within the established pastel family. Alternatively, add a dedicated `--auth-gradient` CSS custom property to `index.css` and reference it with a Tailwind arbitrary value `bg-[var(--auth-gradient)]`.
|
||||||
|
|
||||||
|
### Pattern 2: Styled Wordmark (AUTH-02, NAV-02)
|
||||||
|
|
||||||
|
**What:** A CSS gradient text treatment using `background-clip: text` — a standard technique with full browser support.
|
||||||
|
|
||||||
|
**When to use:** When the app name needs brand identity treatment without SVG or image assets.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tsx
|
||||||
|
// Auth page wordmark (inside CardHeader, replacing or supplementing CardDescription)
|
||||||
|
<span
|
||||||
|
className="text-2xl font-bold tracking-tight"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(to right, oklch(0.50 0.12 260), oklch(0.50 0.12 320))`,
|
||||||
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
backgroundClip: 'text',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Budget Dashboard
|
||||||
|
</span>
|
||||||
|
|
||||||
|
// Sidebar app name (replacing <h2 className="text-lg font-semibold">)
|
||||||
|
<span
|
||||||
|
className="text-base font-semibold tracking-wide"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(to right, oklch(0.50 0.12 260), oklch(0.50 0.12 300))`,
|
||||||
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
backgroundClip: 'text',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('app.title')}
|
||||||
|
</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
The gradient values come from `--primary` (`oklch(0.50 0.12 260)`) shifted slightly in hue — no new tokens needed.
|
||||||
|
|
||||||
|
### Pattern 3: shadcn Alert for Error Display (AUTH-04)
|
||||||
|
|
||||||
|
**What:** Replace `<p className="text-sm text-destructive">` with a structured Alert component.
|
||||||
|
|
||||||
|
**When to use:** Form-level errors that need icon + message treatment.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tsx
|
||||||
|
// After: bunx --bun shadcn add alert
|
||||||
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||||
|
import { AlertCircle } from 'lucide-react'
|
||||||
|
|
||||||
|
// In LoginPage and RegisterPage CardContent:
|
||||||
|
{error && (
|
||||||
|
<Alert variant="destructive">
|
||||||
|
<AlertCircle className="h-4 w-4" />
|
||||||
|
<AlertDescription>{error}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
```
|
||||||
|
|
||||||
|
The shadcn `alert` component uses `--destructive` and `--destructive-foreground` tokens which are already defined in `index.css`.
|
||||||
|
|
||||||
|
### Pattern 4: SidebarTrigger Placement (NAV-04)
|
||||||
|
|
||||||
|
**What:** Add a visible toggle button that calls `toggleSidebar()` from the sidebar context. `SidebarTrigger` is already implemented and exported from `ui/sidebar.tsx`.
|
||||||
|
|
||||||
|
**When to use:** The current `AppLayout.tsx` renders no trigger — collapsing is only possible via the keyboard shortcut `Ctrl+B`.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tsx
|
||||||
|
import { SidebarTrigger, /* existing imports */ } from '@/components/ui/sidebar'
|
||||||
|
|
||||||
|
// In AppLayout, inside SidebarInset, add a header bar:
|
||||||
|
<SidebarInset>
|
||||||
|
<header className="flex h-12 items-center gap-2 border-b px-4">
|
||||||
|
<SidebarTrigger />
|
||||||
|
</header>
|
||||||
|
<main className="flex-1 p-4">{children}</main>
|
||||||
|
</SidebarInset>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `SidebarTrigger` renders a `PanelLeftIcon` button and calls `toggleSidebar()` internally. The current `collapsible` prop on `<Sidebar>` defaults to `"offcanvas"`, which means on mobile it slides in as a Sheet and on desktop it shifts the content. This behavior is already fully implemented in `sidebar.tsx`.
|
||||||
|
|
||||||
|
### Pattern 5: Active Nav Indicator (NAV-03)
|
||||||
|
|
||||||
|
**What:** Verify and if needed, reinforce the `isActive` visual state on `SidebarMenuButton`.
|
||||||
|
|
||||||
|
**Current state:** `AppLayout.tsx` already passes `isActive={location.pathname === item.path}` to `SidebarMenuButton`. The shadcn sidebar component applies `data-active` attribute and styles active items with `bg-sidebar-accent` and `text-sidebar-accent-foreground` by default.
|
||||||
|
|
||||||
|
**Potential gap:** The current `--sidebar-accent` is `oklch(0.93 0.020 280)` against `--sidebar` of `oklch(0.97 0.012 280)` — that is a 4-point lightness difference. This may render as visually insufficient contrast. If so, the fix is to update `SidebarMenuButton` via a `className` override or adjust `--sidebar-accent` in index.css to use `--sidebar-primary` for the active state.
|
||||||
|
|
||||||
|
**Verification test:** Render in browser, click nav items, confirm visible selection change. If not visible, apply:
|
||||||
|
```tsx
|
||||||
|
<SidebarMenuButton
|
||||||
|
asChild
|
||||||
|
isActive={location.pathname === item.path}
|
||||||
|
className="data-[active=true]:bg-sidebar-primary data-[active=true]:text-sidebar-primary-foreground"
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Anti-Patterns to Avoid
|
||||||
|
|
||||||
|
- **Editing `ui/sidebar.tsx` directly:** Project constraint — customize via `className` props or CSS variable overrides only, never edit `src/components/ui/` source files.
|
||||||
|
- **Hardcoded hex or oklch values in component files:** All colors must come from design tokens (`--primary`, `palette.ts`) or explicit inline style with values from the established token system.
|
||||||
|
- **Adding `text-green-*` or `text-blue-*` raw classes for wordmark:** Use gradient text via inline style from token values, consistent with Phase 1 decisions.
|
||||||
|
- **Adding `BrowserRouter` to auth pages:** Auth pages render outside the router (`App.tsx` renders them before `BrowserRouter`). Do not add routing-dependent hooks to `LoginPage` or `RegisterPage`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Don't Hand-Roll
|
||||||
|
|
||||||
|
| Problem | Don't Build | Use Instead | Why |
|
||||||
|
|---------|-------------|-------------|-----|
|
||||||
|
| Styled error alerts | Custom div with icon + border CSS | `shadcn add alert` | Handles destructive variant, screen-reader role, proper token use |
|
||||||
|
| Sidebar toggle button | Custom button + `useState` for open/closed | `SidebarTrigger` from `ui/sidebar` | Already wired to sidebar context; persists state in cookie |
|
||||||
|
| Sidebar collapse state | Manual `useState` + CSS width transitions | `SidebarProvider` + `collapsible="icon"` | Full collapse behavior with keyboard shortcut already built |
|
||||||
|
| Gradient text wordmark | SVG logo | CSS `background-clip: text` with inline style | No asset needed; uses existing tokens; responsive |
|
||||||
|
|
||||||
|
**Key insight:** The shadcn Sidebar component is unusually complete — toggle, collapse, cookie persistence, mobile Sheet, and keyboard shortcut are all pre-built. The only missing piece is exposing `SidebarTrigger` in the rendered layout.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
### Pitfall 1: Alert Component Not Installed
|
||||||
|
**What goes wrong:** Importing `@/components/ui/alert` fails at build time — the file does not exist yet.
|
||||||
|
**Why it happens:** shadcn components are opt-in; `alert.tsx` is not in `frontend/src/components/ui/`.
|
||||||
|
**How to avoid:** Run `bunx --bun shadcn add alert` before writing the import. Verify the file appears at `frontend/src/components/ui/alert.tsx`.
|
||||||
|
**Warning signs:** TypeScript error `Cannot find module '@/components/ui/alert'`.
|
||||||
|
|
||||||
|
### Pitfall 2: Sidebar Active State Low Contrast
|
||||||
|
**What goes wrong:** The `isActive` indicator renders but is nearly invisible due to minimal lightness difference between `--sidebar` and `--sidebar-accent`.
|
||||||
|
**Why it happens:** `--sidebar: oklch(0.97 0.012 280)` vs `--sidebar-accent: oklch(0.93 0.020 280)` — 4 lightness points difference in a low-chroma space.
|
||||||
|
**How to avoid:** After implementing, visually verify active state. If insufficient, override with `data-[active=true]:bg-sidebar-primary data-[active=true]:text-sidebar-primary-foreground` in the `SidebarMenuButton` className. The `--sidebar-primary` token `oklch(0.50 0.12 260)` provides strong contrast.
|
||||||
|
**Warning signs:** Clicking nav items produces no perceptible visual change.
|
||||||
|
|
||||||
|
### Pitfall 3: Auth Gradient Feels Jarring
|
||||||
|
**What goes wrong:** A high-saturation gradient makes the login screen feel loud rather than refined.
|
||||||
|
**Why it happens:** Pastels require low chroma (0.02–0.06) at high lightness (0.92+). Using chart or header gradient values (medium shades at 0.88 lightness) will appear oversaturated as full-screen backgrounds.
|
||||||
|
**How to avoid:** Use the `light` shades from `palette.ts` (lightness 0.95–0.97, chroma 0.03–0.04), not `medium` or `base` shades. Keep the gradient subtle — it should feel like tinted paper, not a colorful splash screen.
|
||||||
|
**Warning signs:** The gradient overwhelms the card form element visually.
|
||||||
|
|
||||||
|
### Pitfall 4: Wordmark Gradient Text Fallback
|
||||||
|
**What goes wrong:** `WebkitTextFillColor: 'transparent'` with `backgroundClip: 'text'` renders as invisible text in some edge environments.
|
||||||
|
**Why it happens:** The `-webkit-` prefix technique requires both properties to be set correctly.
|
||||||
|
**How to avoid:** Always pair `WebkitBackgroundClip: 'text'`, `WebkitTextFillColor: 'transparent'`, AND `backgroundClip: 'text'`. The non-prefixed version is needed for Firefox compatibility.
|
||||||
|
**Warning signs:** Wordmark text disappears or renders as black.
|
||||||
|
|
||||||
|
### Pitfall 5: SidebarTrigger Outside SidebarProvider
|
||||||
|
**What goes wrong:** `useSidebar()` throws "useSidebar must be used within a SidebarProvider."
|
||||||
|
**Why it happens:** `SidebarTrigger` calls `useSidebar()` internally and will throw if placed outside `<SidebarProvider>`.
|
||||||
|
**How to avoid:** `SidebarTrigger` must be placed inside the `<SidebarProvider>` tree — either inside `<Sidebar>`, `<SidebarInset>`, or any other child. In `AppLayout.tsx` the current structure has both `<Sidebar>` and `<SidebarInset>` inside `<SidebarProvider>`, so placing it in `<SidebarInset>` is safe.
|
||||||
|
|
||||||
|
### Pitfall 6: RegisterPage Missing from Auth Page Update
|
||||||
|
**What goes wrong:** AUTH-03 is missed — RegisterPage still renders the plain white card after LoginPage is polished.
|
||||||
|
**Why it happens:** The two pages are separate files with identical structure; easy to forget to update both.
|
||||||
|
**How to avoid:** Treat AUTH-01/02/04 and AUTH-03 as one logical task that touches both files simultaneously. Register page should be a near-exact structural mirror of Login page.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Examples
|
||||||
|
|
||||||
|
### Full-Screen Gradient Auth Wrapper
|
||||||
|
```tsx
|
||||||
|
// Source: palette.ts light shades — keeping within established token system
|
||||||
|
<div
|
||||||
|
className="flex min-h-screen items-center justify-center"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${palette.saving.light}, ${palette.bill.light}, ${palette.investment.light})`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card className="w-full max-w-md shadow-lg">
|
||||||
|
{/* ... */}
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alert Destructive Error Block
|
||||||
|
```tsx
|
||||||
|
// Source: shadcn alert component (installed via bunx shadcn add alert)
|
||||||
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||||
|
import { AlertCircle } from 'lucide-react'
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<Alert variant="destructive">
|
||||||
|
<AlertCircle className="h-4 w-4" />
|
||||||
|
<AlertDescription>{error}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sidebar Header with Branded Wordmark
|
||||||
|
```tsx
|
||||||
|
// Source: index.css --primary token; no new tokens needed
|
||||||
|
<SidebarHeader className="border-b px-4 py-3">
|
||||||
|
<span
|
||||||
|
className="text-base font-semibold tracking-wide"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(to right, var(--color-primary), oklch(0.50 0.12 300))`,
|
||||||
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
backgroundClip: 'text',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('app.title')}
|
||||||
|
</span>
|
||||||
|
{auth.user && (
|
||||||
|
<p className="text-sm text-muted-foreground">{auth.user.display_name}</p>
|
||||||
|
)}
|
||||||
|
</SidebarHeader>
|
||||||
|
```
|
||||||
|
|
||||||
|
### SidebarTrigger in Layout Header
|
||||||
|
```tsx
|
||||||
|
// Source: ui/sidebar.tsx — SidebarTrigger already exported
|
||||||
|
import { SidebarTrigger, /* ... */ } from '@/components/ui/sidebar'
|
||||||
|
|
||||||
|
<SidebarInset>
|
||||||
|
<header className="flex h-12 shrink-0 items-center gap-2 border-b px-4">
|
||||||
|
<SidebarTrigger className="-ml-1" />
|
||||||
|
</header>
|
||||||
|
<main className="flex-1 p-4">{children}</main>
|
||||||
|
</SidebarInset>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Active Nav Item Override (if default contrast insufficient)
|
||||||
|
```tsx
|
||||||
|
// Source: AppLayout.tsx — className addition only, no sidebar.tsx edit
|
||||||
|
<SidebarMenuButton
|
||||||
|
asChild
|
||||||
|
isActive={location.pathname === item.path}
|
||||||
|
className="data-[active=true]:bg-sidebar-primary data-[active=true]:text-sidebar-primary-foreground"
|
||||||
|
>
|
||||||
|
<Link to={item.path}>
|
||||||
|
<item.icon />
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</Link>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## State of the Art
|
||||||
|
|
||||||
|
| Old Approach | Current Approach | When Changed | Impact |
|
||||||
|
|--------------|------------------|--------------|--------|
|
||||||
|
| Manual sidebar toggle with useState | shadcn Sidebar with SidebarProvider context | shadcn 2.x | Built-in toggle, cookie persistence, keyboard shortcut |
|
||||||
|
| Tailwind v3 `theme.extend.colors` for custom tokens | CSS custom properties in `:root` + `@theme inline` in Tailwind v4 | Tailwind v4 | Tokens are pure CSS, not Tailwind-config-dependent |
|
||||||
|
| `lucide-react` named imports | Same — no change | - | Lucide is still the icon standard for shadcn |
|
||||||
|
|
||||||
|
**Deprecated/outdated:**
|
||||||
|
- `bg-primary` Tailwind class approach for backgrounds: Now use CSS variable direct references (`var(--color-primary)`) or token-based inline styles for gradient backgrounds — Tailwind v4 exposes all custom properties as utilities.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
1. **Active sidebar item contrast adequacy**
|
||||||
|
- What we know: `--sidebar-accent` is 4 lightness points from `--sidebar`; `SidebarMenuButton` uses `data-[active=true]:bg-sidebar-accent` by default
|
||||||
|
- What's unclear: Whether this is visually sufficient without browser testing
|
||||||
|
- Recommendation: Plan task includes a visual verification step; if insufficient, apply the `data-[active=true]:bg-sidebar-primary` className override
|
||||||
|
|
||||||
|
2. **Wordmark: gradient text vs. font-weight/tracking only**
|
||||||
|
- What we know: Gradient text works cross-browser with correct CSS properties
|
||||||
|
- What's unclear: Whether the design intent is a text-gradient wordmark or just tracked/bold typography
|
||||||
|
- Recommendation: Gradient text is the stronger brand treatment; the planner should implement gradient text as the default and treat simpler alternatives as a fallback
|
||||||
|
|
||||||
|
3. **SidebarInset main content padding**
|
||||||
|
- What we know: Current `AppLayout.tsx` has `<main className="flex-1">{children}</main>` with no padding
|
||||||
|
- What's unclear: Whether adding a header bar with `SidebarTrigger` requires padding adjustments to existing page components
|
||||||
|
- Recommendation: Add `p-4` to main only if pages do not already manage their own padding; inspect `DashboardPage` to confirm
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation Architecture
|
||||||
|
|
||||||
|
### Test Framework
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Framework | Vitest 4.0.18 + @testing-library/react 16.3.2 |
|
||||||
|
| Config file | `frontend/vite.config.ts` (test section present) |
|
||||||
|
| Quick run command | `cd frontend && bun vitest run --reporter=verbose` |
|
||||||
|
| Full suite command | `cd frontend && bun vitest run` |
|
||||||
|
|
||||||
|
### Phase Requirements → Test Map
|
||||||
|
|
||||||
|
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|
||||||
|
|--------|----------|-----------|-------------------|--------------|
|
||||||
|
| AUTH-01 | Login page renders gradient background wrapper | unit | `bun vitest run src/pages/LoginPage.test.tsx` | No — Wave 0 |
|
||||||
|
| AUTH-02 | Login page renders wordmark with gradient text style | unit | `bun vitest run src/pages/LoginPage.test.tsx` | No — Wave 0 |
|
||||||
|
| AUTH-03 | Register page renders same gradient/wordmark as login | unit | `bun vitest run src/pages/RegisterPage.test.tsx` | No — Wave 0 |
|
||||||
|
| AUTH-04 | Error string renders Alert with destructive variant | unit | `bun vitest run src/pages/LoginPage.test.tsx` | No — Wave 0 |
|
||||||
|
| NAV-01 | Sidebar renders with bg-sidebar class | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
|
||||||
|
| NAV-02 | Sidebar header contains branded wordmark element | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
|
||||||
|
| NAV-03 | Active nav item receives data-active attribute | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
|
||||||
|
| NAV-04 | SidebarTrigger button is rendered in layout | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
|
||||||
|
|
||||||
|
**Note on visual requirements:** AUTH-01 (gradient background), AUTH-02 (wordmark appearance), NAV-01 (sidebar color distinction), and NAV-03 (visible color indicator) have a visual correctness dimension that unit tests cannot fully capture. Unit tests verify structural presence (element rendered, class present, inline style set); visual correctness requires browser verification. Plan tasks should include explicit browser check steps alongside automated tests.
|
||||||
|
|
||||||
|
### Sampling Rate
|
||||||
|
- **Per task commit:** `cd frontend && bun vitest run`
|
||||||
|
- **Per wave merge:** `cd frontend && bun vitest run && cd frontend && bun run build`
|
||||||
|
- **Phase gate:** Full suite green + production build green before `/gsd:verify-work`
|
||||||
|
|
||||||
|
### Wave 0 Gaps
|
||||||
|
- [ ] `frontend/src/pages/LoginPage.test.tsx` — covers AUTH-01, AUTH-02, AUTH-04
|
||||||
|
- [ ] `frontend/src/pages/RegisterPage.test.tsx` — covers AUTH-03
|
||||||
|
- [ ] `frontend/src/components/AppLayout.test.tsx` — covers NAV-01, NAV-02, NAV-03, NAV-04
|
||||||
|
- [ ] `frontend/src/components/ui/alert.tsx` — must exist before any test imports it (install via shadcn CLI)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
### Primary (HIGH confidence)
|
||||||
|
- Direct file inspection: `frontend/src/components/AppLayout.tsx` — confirms SidebarTrigger not yet rendered
|
||||||
|
- Direct file inspection: `frontend/src/pages/LoginPage.tsx` and `RegisterPage.tsx` — confirms plain `bg-background` wrapper and `<p className="text-sm text-destructive">` error display
|
||||||
|
- Direct file inspection: `frontend/src/components/ui/sidebar.tsx` — confirms SidebarTrigger exported, collapsible behavior fully implemented
|
||||||
|
- Direct file inspection: `frontend/src/index.css` — confirms `--sidebar`, `--sidebar-primary`, `--sidebar-accent` token values
|
||||||
|
- Direct file inspection: `frontend/src/lib/palette.ts` — confirms light/medium/base shades for gradient construction
|
||||||
|
|
||||||
|
### Secondary (MEDIUM confidence)
|
||||||
|
- shadcn/ui Alert component documentation pattern — standard destructive variant with AlertCircle icon is the established pattern for form error alerts in shadcn projects
|
||||||
|
|
||||||
|
### Tertiary (LOW confidence)
|
||||||
|
- None
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
**Confidence breakdown:**
|
||||||
|
- Standard stack: HIGH — all libraries verified as installed via package.json; no new npm deps needed beyond `bunx shadcn add alert`
|
||||||
|
- Architecture: HIGH — all files inspected directly; changes are confined to known files with no new routing
|
||||||
|
- Pitfalls: HIGH — active state contrast gap identified by reading actual token values; alert install gap confirmed by directory listing
|
||||||
|
|
||||||
|
**Research date:** 2026-03-11
|
||||||
|
**Valid until:** 2026-04-10 (stable shadcn/Tailwind ecosystem; 30-day window)
|
||||||
Reference in New Issue
Block a user