# 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