diff --git a/docs/tailwind-v4-migration.md b/docs/tailwind-v4-migration.md new file mode 100644 index 00000000..2b14ea2d --- /dev/null +++ b/docs/tailwind-v4-migration.md @@ -0,0 +1,633 @@ +# ResolutionFlow — Tailwind v4 Migration & Feature Guide +**Claude Code Handoff Document · Pre-Investor Pitch Sprint** + +--- + +## Purpose + +This document gives Claude Code everything needed to: +1. Execute the mechanical Tailwind v3 → v4 upgrade +2. Know which new v4 features to use going forward so the ResolutionFlow UI looks and feels premium for the investor pitch demo + +## Branch + +This migration touches nearly every file in the frontend. **Always work on a dedicated branch:** + +```bash +git checkout -b feat/tailwind-v4-upgrade +``` + +Do not merge to `main` until all phases are complete and the full visual QA pass is done. + +--- + +## Stack Context + +| | | +|---|---| +| Frontend | React 19.2 + Vite 7 + TypeScript 5.9 | +| Current Tailwind | v3.4.19 | +| Component Library | shadcn/ui (target: new-york style post-upgrade) | +| Canvas Editor | React Flow (@xyflow/react) | +| Brand Color | Cyan — `#06b6d4 → #22d3ee` (hsl 187 72% 43%) | +| Background | `hsl(228 12% 7%)` — dark theme only, no light mode | +| Body Font | IBM Plex Sans | +| Heading Font | Bricolage Grotesque | +| Label/Mono Font | JetBrains Mono | +| Brand Gradient | `linear-gradient(135deg, #06b6d4 0%, #22d3ee 100%)` | +| Animation Library | tailwindcss-animate → **replace with tw-animate-css** | +| Tailwind Plugins | None | + +--- + +--- + +# PART 1: Mechanical Migration + +Execute phases in order. Verify the app builds and renders correctly after each phase before proceeding. + +--- + +## Phase 1 — Update Dependencies + +Run the official Tailwind upgrade tool from the `/frontend` directory. It handles the majority of migration automatically: + +```bash +# Run from /frontend +npx @tailwindcss/upgrade@latest +``` + +This tool will: +- Update `tailwindcss` to v4 +- Install `@tailwindcss/vite` (replaces the PostCSS plugin) +- Migrate `tailwind.config.js` → CSS `@theme` block +- Update `@tailwind` directives to `@import` +- Handle renamed utility classes in template files + +After running, verify `package.json` reflects: + +```json +// devDependencies should now include: +"tailwindcss": "^4.x.x", +"@tailwindcss/vite": "^4.x.x" + +// These are no longer needed and can be removed: +// "autoprefixer" (bundled in v4 via Lightning CSS) +// "postcss" (unless used for something other than Tailwind) +``` + +--- + +## Phase 2 — Update Vite Config + +Replace the PostCSS-based Tailwind setup with the first-party Vite plugin: + +```ts +// vite.config.ts — BEFORE +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], +}) + +// vite.config.ts — AFTER +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' + +export default defineConfig({ + plugins: [react(), tailwindcss()], +}) +``` + +Remove `postcss.config.js` if it exists and was only used for Tailwind. Lightning CSS is now bundled inside Tailwind v4. + +--- + +## Phase 3 — Migrate CSS Entry Point + +Update `index.css`: + +```css +/* BEFORE — v3 directives */ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* AFTER — v4 single import */ +@import "tailwindcss"; +@import "tw-animate-css"; + +/* ResolutionFlow is dark-only. Replace darkMode: ["class"] from tailwind.config.js + with this custom variant declaration. This is the v4 equivalent. */ +@custom-variant dark (&:where(.dark, .dark *)); + +/* React Flow styles must now live in CSS, not imported in App.tsx */ +@layer base { + @import "@xyflow/react/dist/style.css"; +} +``` + +> **Important:** Remove `import '@xyflow/react/dist/style.css'` from `App.tsx` after adding it here. +> **Important:** Remove `darkMode: ["class"]` from `tailwind.config.js` — it is now handled by `@custom-variant dark` above. Since ResolutionFlow is dark-only with no light mode toggle, verify that the `.dark` class is still present on the `` element in `index.html`. + +--- + +## Phase 4 — Migrate Theme Configuration + +The upgrade tool migrates `tailwind.config.js` automatically, but verify the ResolutionFlow design tokens are correctly expressed. The `@theme` block lives in `index.css` after the imports. + +### OKLCH Color Values + +Claude Code flagged that the cyan range benefits from OKLCH for better gradient interpolation. Use these OKLCH equivalents for brand colors inside `@theme` — they render more accurately on wide-gamut displays (MacBooks, modern monitors used in pitch settings): + +| Hex | OKLCH | +|---|---| +| `#06b6d4` (brand-from) | `oklch(72% 0.15 195)` | +| `#22d3ee` (brand-to) | `oklch(82% 0.13 195)` | +| `#0891b2` (brand-dark) | `oklch(62% 0.14 195)` | + +### Full `@theme` Block + +```css +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:where(.dark, .dark *)); + +@theme { + /* ResolutionFlow Brand — Cyan system (OKLCH for wide-gamut accuracy) */ + --color-brand-from: oklch(72% 0.15 195); /* #06b6d4 */ + --color-brand-to: oklch(82% 0.13 195); /* #22d3ee */ + --color-brand-dark: oklch(62% 0.14 195); /* #0891b2 */ + + /* Dark surface palette */ + --color-dark-DEFAULT: #101114; + --color-dark-card: #14161a; + --color-dark-surface: #14161a; + + /* Text palette */ + --color-text-primary: #f8fafc; + --color-text-secondary: #8891a0; + --color-text-muted: #5a6170; + + /* Typography */ + --font-sans: 'IBM Plex Sans', system-ui, -apple-system, sans-serif; + --font-heading: 'Bricolage Grotesque', system-ui, sans-serif; + --font-label: 'JetBrains Mono', monospace; + + /* Border radius */ + --radius-lg: 0.75rem; + --radius-md: calc(0.75rem - 2px); + --radius-sm: calc(0.75rem - 4px); + + /* Keyframe animations — move ALL @keyframes from index.css into @theme */ + --animate-fade-in: fade-in 200ms ease-out; + --animate-fade-in-up: fade-in-up 200ms ease-out; + --animate-slide-in-left: slide-in-from-left 200ms ease-out; + --animate-slide-in-bottom: slide-in-from-bottom 200ms ease-out; + --animate-scale-in: scale-in 150ms ease-out; + --animate-breathe-glow: breatheGlow 3s ease-in-out infinite alternate; + --animate-bell-wobble: bellWobble 0.5s ease-in-out; + + @keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } + } + @keyframes fade-in-up { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: translateY(0); } + } + @keyframes slide-in-from-left { + from { transform: translateX(-100%); } + to { transform: translateX(0); } + } + @keyframes slide-in-from-bottom { + from { opacity: 0; transform: translateY(16px); } + to { opacity: 1; transform: translateY(0); } + } + @keyframes scale-in { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } + } + @keyframes breatheGlow { + from { box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 0 20px oklch(72% 0.15 195 / 0.04); } + to { box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 0 30px oklch(72% 0.15 195 / 0.12); } + } + @keyframes bellWobble { + 0% { transform: rotate(0deg); } + 20% { transform: rotate(8deg); } + 40% { transform: rotate(-6deg); } + 60% { transform: rotate(4deg); } + 80% { transform: rotate(-2deg); } + 100% { transform: rotate(0deg); } + } +} +``` + +> **Important:** Remove the loose `@keyframes` blocks from `index.css` after moving them into `@theme`. They should not exist in both places. + +### shadcn/ui CSS Variable Bridge + +Keep the existing `:root` block as-is — shadcn still uses HSL format for its variables: + +```css +/* shadcn/ui CSS variable bridge — keep as-is from existing index.css */ +@layer base { + :root { + --background: 228 12% 7%; + --foreground: 210 40% 98%; + --card: 220 10% 10%; + --card-foreground: 210 40% 98%; + --popover: 220 10% 10%; + --popover-foreground: 210 40% 98%; + --primary: 187 72% 43%; + --primary-foreground: 228 12% 7%; + --secondary: 220 8% 14%; + --secondary-foreground: 210 40% 98%; + --muted: 220 8% 14%; + --muted-foreground: 215 10% 58%; + --accent: 220 8% 14%; + --accent-foreground: 210 40% 98%; + --destructive: 350 81% 55%; + --destructive-foreground: 210 40% 98%; + --border: 220 8% 14%; + --input: 220 8% 14%; + --ring: 187 72% 43%; + --radius: 0.75rem; + /* ... rest of existing tokens ... */ + } +} + +--- + +## Phase 5 — Replace Animation Library + +```bash +# Remove old plugin +npm uninstall tailwindcss-animate + +# Install replacement +npm install -D tw-animate-css +``` + +Remove any `@plugin 'tailwindcss-animate'` references from CSS files. The `@import "tw-animate-css"` added in Phase 3 replaces it. + +--- + +## Phase 7 — Consolidate Hardcoded Inline Colors + +Claude Code identified **128 instances of `style={{}}` inline color values across 44 files**, including atmosphere orbs in `AppLayout.tsx` using hardcoded `rgba(6,182,212,...)`. Now that brand colors are proper CSS variables in `@theme`, replace all hardcoded cyan/brand references with variables. + +**Search and replace targets:** + +```ts +// FIND these hardcoded patterns: +rgba(6, 182, 212, ...) // brand-from with opacity +rgba(34, 211, 238, ...) // brand-to with opacity +#06b6d4 // brand-from hex +#22d3ee // brand-to hex +#0891b2 // brand-dark hex + +// REPLACE with CSS variable equivalents: +oklch(72% 0.15 195 / 0.XX) // brand-from with opacity (XX = your alpha) +oklch(82% 0.13 195 / 0.XX) // brand-to with opacity +var(--color-brand-from) // brand-from solid +var(--color-brand-to) // brand-to solid +var(--color-brand-dark) // brand-dark solid +``` + +**Specifically in `AppLayout.tsx`** — atmosphere orbs should become: + +```tsx +// BEFORE +style={{ background: 'rgba(6, 182, 212, 0.08)' }} + +// AFTER — references the theme token, respects any future brand color changes +style={{ background: 'oklch(72% 0.15 195 / 0.08)' }} + +// Or better yet, move to a CSS utility class entirely: +// .atmosphere-orb { background: oklch(72% 0.15 195 / 0.08); } +``` + +> This is the right time to do this cleanup — the theme is already being touched, and it means future brand color changes only require updating `@theme` instead of hunting through 44 files. + +--- + +## Phase 8 — Reinstall shadcn/ui Components + +shadcn/ui components have been updated for Tailwind v4 and React 19. Commit any custom component modifications first, then reinstall to get refreshed versions. + +```bash +# Re-initialize with v4 defaults +npx shadcn@latest init + +# When prompted: +# Style: new-york (more refined than 'default') +# Base color: cyan (matches ResolutionFlow brand) + +# Reinstall actively used components: +npx shadcn@latest add button card dialog dropdown-menu +npx shadcn@latest add badge input label select textarea +npx shadcn@latest add table tabs tooltip separator +npx shadcn@latest add sheet command popover +``` + +**What's new in shadcn v4 components:** +- `forwardRef` removed from all components (React 19 handles ref natively) +- `data-slot` attributes added for easier CSS targeting +- HSL colors converted to OKLCH for wider color gamut +- Dark mode colors revisited for better accessibility +- `tailwindcss-animate` deprecated in favor of `tw-animate-css` + +--- + +## Phase 9 — Visual QA Checklist + +Work through the app after Phases 1–6. These are the most likely regression areas: + +- [ ] **Borders** — default border color changed from `gray-200` to `currentColor`. Check all card, table, and input borders against the `hsl(var(--border))` token. +- [ ] **Focus rings** — default ring changed from `3px blue` to `1px currentColor`. Verify form fields and button focus states look intentional. +- [ ] **Placeholder text** — changed from `gray-400` to `currentColor` at 50% opacity. Check all inputs. +- [ ] **Button cursors** — now `cursor:default` (browser standard). Add `cursor-pointer` explicitly where desired. +- [ ] **React Flow canvas** — verify nodes, handles, edges, and controls render correctly with CSS import moved to `@layer base`. +- [ ] **Animations** — check dialog open/close, sheet slide, dropdown appear animations after the animation library swap. +- [ ] **Keyframe animations** — verify `breatheGlow` on stat cards, `bellWobble` on notification bell, and all `fade-in-*` utilities still work after moving keyframes into `@theme`. +- [ ] **Cyan brand gradient** — verify `bg-gradient-brand` still renders correctly. Check it looks at least as good (ideally better) with OKLCH values. +- [ ] **Atmosphere orbs** — verify `AppLayout.tsx` orbs render correctly after inline style replacement with OKLCH variables. +- [ ] **Glass morphism** — verify `.glass-card` and `.glass-card-static` backdrop blur still applies on all surfaces. +- [ ] **JetBrains Mono** — verify label/code elements still use mono font correctly. +- [ ] **Sonner toasts** — verify custom toast styling (card bg, border, icon colors) still applies. +- [ ] **Dark variant** — verify `.dark` class on `` is present and the `@custom-variant dark` replacement behaves identically to the old `darkMode: ["class"]` config. + +--- + +## Commit Strategy + +```bash +git checkout -b feat/tailwind-v4-upgrade + +git commit -m "chore: upgrade Tailwind v3 → v4, update Vite plugin" +git commit -m "chore: migrate CSS entry point — @import, @custom-variant dark" +git commit -m "chore: migrate theme to @theme block — OKLCH colors, keyframes" +git commit -m "chore: swap tailwindcss-animate for tw-animate-css" +git commit -m "chore: consolidate 128 hardcoded inline colors to CSS variables" +git commit -m "chore: reinstall shadcn/ui components for v4 (new-york style, cyan)" +git commit -m "fix: visual QA pass — borders, rings, placeholders, glass, animations" +git commit -m "feat: apply v4 feature enhancements across components" + +# When complete and QA passes: +git checkout main +git merge feat/tailwind-v4-upgrade +``` + +--- + +--- + +# PART 2: Tailwind v4 Feature Guide + +**These are new capabilities available after the upgrade. Apply them proactively when building or modifying components — do not default to v3 patterns when a v4 equivalent is listed here.** + +--- + +## Feature 1 — Auto-Resizing Textareas (`field-sizing-content`) + +Apply to every multi-line text input in the app. Engineers typing session notes and documentation should never fight a fixed-height box. + +**Where to apply:** Session runner documentation field · step notes input · FlowPilot prompt input · any multi-line input in the flow editor + +```tsx +// BEFORE — v3, fixed height or JS resize logic +