recipe-manager/.harness/docs/styling-inventory.md

185 lines
7.2 KiB
Markdown

# T01 — Styling Inventory + Drift Map
Date: 2026-03-27
Project: `recipe-manager/frontend`
Task: **T01 — Styling Inventory + Drift Map**
## Scope audited
- Token sources: `src/styles/tokens.css`, `src/theme.ts`, `frontend/tailwind.config.js`
- Shared styling layer: `src/index.css` (`ui-*` primitives)
- Legacy stylesheet: `src/App.css`
- App shell + representative screens/components:
- `src/App.tsx`
- `src/pages/RecipeListPage.tsx`
- `src/pages/RecipeDetailPage.tsx`
- `src/pages/CookModePage.tsx`
- `src/pages/ImportUrlPage.tsx`
- `src/pages/NotFoundPage.tsx`
- `src/components/RecipeCard.tsx`
- `src/components/RecipeForm.tsx`
- `src/components/TagSelector.tsx`
- `src/components/Toast.tsx`
- `src/components/MissionControlPanel.tsx`
- `src/components/ErrorBoundary.tsx`
---
## 1) Where styling primitives currently live
### Canonical CSS token surface (already strong)
- **`src/styles/tokens.css`**
- semantic color, typography, spacing, radius, shadow, focus, gradients
- includes dark-mode token overrides
### Utility-class primitives (already present)
- **`src/index.css` @layer components** defines:
- `.ui-page`, `.ui-section`, `.ui-card`
- `.ui-btn`, `.ui-btn-primary`, `.ui-btn-secondary`
- `.ui-input`, `.ui-textarea`, `.ui-select`
- `.ui-chip`, `.ui-badge`
- Also contains global focus-visible behavior and utility legacy classes (`.card`, `.shadow-card`, `animate-slide-in`)
### Tailwind token mapping
- **`frontend/tailwind.config.js`** maps many Tailwind theme keys to CSS vars (good bridge)
### Parallel TS token layer (drift risk)
- **`src/theme.ts`** duplicates many token values from `tokens.css`
- `colors`, `typography`, `spacing`, `radius`, `shadows`, `componentStyles`
### Legacy stylesheet
- **`src/App.css`** contains Vite/demo-era nested selectors (`#next-steps`, `.hero`, `.counter`, etc.)
- not imported by `main.tsx` or other src files in current app
---
## 2) Systems currently being mixed
1. **UI primitives + token vars** (`ui-*`, `var(--...)`)
2. **Raw Tailwind palette utilities** (`bg-blue-*`, `text-slate-*`, `border-gray-*`, etc.)
3. **Inline style objects via `theme.ts`** (`style={{ borderRadius: radius.lg }}`, `boxShadow: shadows.card`)
4. **One-off arbitrary values** (`hover:[box-shadow:var(--shadow-hover)]`, `shadow-[...]`)
5. **Legacy class debt** (`App.css`, and old helper classes in `index.css`)
Result: there is no enforced single styling path; authors can pick multiple parallel approaches in the same file.
---
## 3) Inventory by file (top-level pages + shared components)
Legend:
- **A** = ui-* primitives + var tokens (preferred direction)
- **B** = raw Tailwind palette utilities (non-tokenized drift)
- **C** = inline `theme.ts` style objects
- **D** = legacy/older pattern
### App shell
- `src/App.tsx`**B-heavy**
- Header/nav/footer use many `blue/slate/gray` classes and custom focus ring colors
- Does not use `ui-page`, `ui-section`, `ui-btn` contract
### Pages
- `src/pages/RecipeListPage.tsx`**A + C mix (plus some B)**
- strong `ui-*` adoption
- frequent inline radius/style injections from `theme.ts`
- `src/pages/RecipeDetailPage.tsx`**A + B + C mixed**
- uses many `ui-*` classes but also many non-token Tailwind palette classes and inline styles
- `src/pages/CookModePage.tsx`**B-only (high drift)**
- almost entirely gray/blue/green/red Tailwind palette utilities
- no `ui-*` shell/primitives
- `src/pages/ImportUrlPage.tsx`**B + C (high drift)**
- heavily uses slate/blue/indigo/green palette classes
- inline radius/progress width styles
- `src/pages/NotFoundPage.tsx`**B-only**
### Shared components
- `src/components/RecipeForm.tsx`**A-dominant**
- `src/components/TagSelector.tsx`**A-dominant + tiny C**
- `src/components/RecipeCard.tsx`**A + C mixed**
- `src/components/Toast.tsx`**C-dominant**
- visual appearance mostly from `theme.ts` inline styles
- `src/components/MissionControlPanel.tsx`**B-only**
- `src/components/ErrorBoundary.tsx`**B-only**
### Approximate drift signal (quick scan)
- Highest raw palette concentration: `CookModePage`, `ImportUrlPage`, `App.tsx`, `RecipeDetailPage`
- Highest inline-style concentration: `RecipeListPage`, `RecipeCard`, `Toast`
---
## 4) Representative drift hotspots
1. **`App.tsx` (app shell)**
- Entire global frame (header/nav/footer) is on blue/slate palette utilities
- creates visual disconnect from tokenized warm palette used in recipe surfaces
2. **`CookModePage.tsx`**
- Separate visual language (blue/gray success/error blocks) and no `ui-*` primitives
- likely to regress independently if token theme changes
3. **`ImportUrlPage.tsx`**
- Similar drift pattern to CookModePage; raw utility palette + custom gradients
4. **`RecipeDetailPage.tsx`**
- Mixed approach in same component: token vars + ui-* + raw palette + inline styles
- hard to predict and maintain consistency
5. **`Toast.tsx` + `theme.ts` coupling**
- toast visuals encoded in TS style objects instead of ui primitives/token classes
6. **`App.css` + legacy selectors in `index.css`**
- `App.css` appears unused yet present
- `.card` / `.shadow-card` coexist with `ui-card`, increasing ambiguity
---
## 5) Highest-risk problem areas
- **Token drift risk**: same values duplicated in `tokens.css` and `theme.ts`.
- **App-shell inconsistency risk**: `App.tsx` not on design-system primitives.
- **Page-level divergence risk**: CookMode/Import URL/NotFound/MissionControl use different style dialect than core recipe pages.
- **Maintainability risk**: inline style overrides (`borderRadius`, `boxShadow`) bypass primitive contract and encourage per-component customization.
- **Regression risk**: no guardrails prevent new raw palette utilities in future UI work.
---
## 6) First conversion targets (for stabilization wave)
1. `src/App.tsx` (global shell contract)
2. `src/components/MissionControlPanel.tsx` (small, easy, high-visibility drift)
3. `src/pages/CookModePage.tsx` (largest isolated drift surface)
4. `src/pages/ImportUrlPage.tsx` (same drift pattern as CookMode)
5. `src/components/Toast.tsx` (inline-style dependency cleanup)
---
## 7) Acceptance criteria for stabilization work (T02+)
A stabilization pass is accepted when all are true:
1. **Single token authority**
- CSS vars in `tokens.css` are canonical.
- `theme.ts` no longer independently defines conflicting visual token values.
2. **Primitive contract adoption**
- app shell + major pages use shared `ui-*` primitives (or wrapper components built from them).
- new features avoid ad-hoc color utilities outside tokenized scheme.
3. **Inline-style reduction**
- no routine radius/shadow/color inline overrides where equivalent primitive/token class exists.
4. **Legacy debt disposition**
- `App.css` removed or explicitly retained with documented rationale.
- overlapping legacy helpers (`.card`, `.shadow-card`) either standardized or deprecated.
5. **Governance check**
- lightweight styling checklist/doc exists and is referenced in workflow.
---
## 8) Suggested next task after T01
**T02 — Canonical Token Source Lock**
- lock CSS tokens as source of truth
- redefine `theme.ts` as typed accessor only (or trim to non-visual helpers)
- document token contract before broad conversion starts