# 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