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

7.2 KiB

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.tsxB-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.tsxA + C mix (plus some B)
    • strong ui-* adoption
    • frequent inline radius/style injections from theme.ts
  • src/pages/RecipeDetailPage.tsxA + B + C mixed
    • uses many ui-* classes but also many non-token Tailwind palette classes and inline styles
  • src/pages/CookModePage.tsxB-only (high drift)
    • almost entirely gray/blue/green/red Tailwind palette utilities
    • no ui-* shell/primitives
  • src/pages/ImportUrlPage.tsxB + C (high drift)
    • heavily uses slate/blue/indigo/green palette classes
    • inline radius/progress width styles
  • src/pages/NotFoundPage.tsxB-only

Shared components

  • src/components/RecipeForm.tsxA-dominant
  • src/components/TagSelector.tsxA-dominant + tiny C
  • src/components/RecipeCard.tsxA + C mixed
  • src/components/Toast.tsxC-dominant
    • visual appearance mostly from theme.ts inline styles
  • src/components/MissionControlPanel.tsxB-only
  • src/components/ErrorBoundary.tsxB-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