Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com
at main 512 lines 15 kB view raw view rendered
1# TailwindCSS Migration Implementation Plan 2 3> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. 4 5**Goal:** Replace the ~1000 lines of custom CSS with TailwindCSS utility classes, while preserving the existing design and dark mode support. 6 7**Architecture:** 81. Install TailwindCSS as a dev dependency with PostCSS 92. Create a minimal `tailwind.config.js` that extends the default theme with the existing color palette 103. Keep a small `base.css` for critical custom styles that can't be replaced (e.g., CodeMirror overrides, Milkdown-specific styles) 114. Replace HTML class names with Tailwind utility classes in templates 125. Use `@apply` for complex patterns that appear multiple times 13 14**Tech Stack:** TailwindCSS 4.x, PostCSS, existing esbuild for CSS bundling 15 16--- 17 18## Current CSS Inventory 19 20| File | Lines | Purpose | 21|------|-------|---------| 22| `static/css/style.css` | 427 | Base styles, navbar, cards, buttons, forms, alerts | 23| `static/css/editor.css` | 419 | Editor layout, toolbar, modals, split pane | 24| `static/css/markdown.css` | 84 | Markdown preview rendering | 25| `static/css/diff.css` | 60 | Diff view styling | 26| **Total** | **990** | | 27 28--- 29 30## Chunk 1: Setup TailwindCSS 31 32**Files:** 33- Modify: `/Users/johnluther/projects/diffdown/package.json` 34- Create: `/Users/johnluther/projects/diffdown/tailwind.config.js` 35- Create: `/Users/johnluther/projects/diffdown/postcss.config.js` 36- Create: `/Users/johnluther/projects/diffdown/src/styles/input.css` 37 38- [ ] **Step 1: Install TailwindCSS and dependencies** 39 40Run: `npm install -D tailwindcss postcss autoprefixer` 41 42- [ ] **Step 2: Initialize Tailwind config** 43 44```javascript 45/** @type {import('tailwindcss').Config} */ 46module.exports = { 47 content: [ 48 "./templates/**/*.html", 49 "./static/**/*.js", 50 ], 51 darkMode: 'class', // Use data-theme attribute for dark mode 52 theme: { 53 extend: { 54 colors: { 55 bg: 'var(--bg)', 56 'bg-card': 'var(--bg-card)', 57 text: 'var(--text)', 58 'text-muted': 'var(--text-muted)', 59 border: 'var(--border)', 60 primary: 'var(--primary)', 61 'primary-hover': 'var(--primary-hover)', 62 danger: 'var(--danger)', 63 success: 'var(--success)', 64 'code-bg': 'var(--code-bg)', 65 'alert-error-bg': 'var(--alert-error-bg)', 66 'alert-error-border': 'var(--alert-error-border)', 67 }, 68 fontFamily: { 69 sans: ['"Barlow"', '-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', 'sans-serif'], 70 heading: ['"Lexend"', '-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', 'sans-serif'], 71 mono: ['"JetBrains Mono"', '"Fira Code"', '"Cascadia Code"', 'monospace'], 72 }, 73 borderRadius: { 74 DEFAULT: '6px', 75 }, 76 }, 77 }, 78 plugins: [], 79} 80``` 81 82- [ ] **Step 3: Create PostCSS config** 83 84```javascript 85module.exports = { 86 plugins: { 87 tailwindcss: {}, 88 autoprefixer: {}, 89 }, 90} 91``` 92 93- [ ] **Step 4: Create input.css for Tailwind directives** 94 95```css 96@tailwind base; 97@tailwind components; 98@tailwind utilities; 99 100/* Keep custom font import */ 101@import url(https://fonts.bunny.net/css?family=barlow:100,200,300,400,500,600|lexend:100,200,300,400,500,600,700,800,900); 102 103/* CSS variables - kept for backward compatibility */ 104:root { 105 --bg: #fafafa; 106 --bg-card: #fff; 107 --text: #1a1a2e; 108 --text-muted: #6b7280; 109 --border: #e5e7eb; 110 --primary: #2563eb; 111 --primary-hover: #1d4ed8; 112 --danger: #dc2626; 113 --success: #16a34a; 114 --code-bg: #f3f4f6; 115 --alert-error-bg: #fef2f2; 116 --alert-error-border: #fecaca; 117} 118 119[data-theme="dark"] { 120 --bg: #0d1117; 121 --bg-card: #161b22; 122 --text: #e6edf3; 123 --text-muted: #ededed; 124 --text-secondary: #d4d4d4; 125 --border: #30363d; 126 --code-bg: #1f2428; 127 --primary: #388bfd; 128 --primary-hover: #58a6ff; 129 --danger: #f85149; 130 --success: #3fb950; 131 --alert-error-bg: #1c0608; 132 --alert-error-border: #6e1c20; 133} 134 135@media (prefers-color-scheme: dark) { 136 :root:not([data-theme="light"]) { 137 --bg: #0d1117; 138 --bg-card: #161b22; 139 --text: #e6edf3; 140 --text-muted: #ededed; 141 --text-secondary: #d4d4d4; 142 --border: #30363d; 143 --code-bg: #1f2428; 144 --primary: #388bfd; 145 --primary-hover: #58a6ff; 146 --danger: #f85149; 147 --success: #3fb950; 148 --alert-error-bg: #1c0608; 149 --alert-error-border: #6e1c20; 150 } 151} 152``` 153 154- [ ] **Step 5: Add build script to package.json** 155 156```json 157"scripts": { 158 "build:css": "npx tailwindcss -i ./src/styles/input.css -o ./static/css/tailwind.css", 159 "build:collab": "npx esbuild node_modules/prosemirror-collab/dist/index.js --bundle --format=esm --outfile=static/vendor/collab.js", 160 "build": "npm run build:css && npm run build:collab" 161} 162``` 163 164- [ ] **Step 6: Build TailwindCSS** 165 166Run: `npm run build:css` 167 168Expected: Creates `static/css/tailwind.css` with Tailwind utilities 169 170- [ ] **Step 7: Commit** 171 172```bash 173git add package.json tailwind.config.js postcss.config.js src/styles/input.css static/css/tailwind.css 174git commit -m "feat: add TailwindCSS setup" 175``` 176 177--- 178 179## Chunk 2: Replace CSS in base.html 180 181**Files:** 182- Modify: `/Users/johnluther/projects/diffdown/templates/base.html` 183 184- [ ] **Step 1: Replace navbar styles with Tailwind classes** 185 186Old: 187```html 188<nav class="navbar"> 189 <a href="/" class="logo"> 190 <img src="/static/img/dd-logo.svg" alt="" width="22" height="28" style="vertical-align:middle;margin-right:0.4rem">Diffdown 191 </a> 192 <div class="nav-right"> 193 {{if .User}} 194 <a href="/">Documents</a> 195 <a href="/about">About</a> 196 <span class="nav-user">{{.User.Name}}</span> 197 <form method="post" action="/auth/logout" style="display:inline"> 198 <button type="submit" class="btn-link">Log out</button> 199 </form> 200 {{else}} 201 <a href="/auth/login">Log in</a> 202 <a href="/auth/register" class="btn btn-sm">Sign up</a> 203 <a href="/about">About</a> 204 {{end}} 205 <button id="theme-toggle" class="btn-link" aria-label="Toggle dark mode" onclick="toggleTheme()" style="font-size:1.1rem;padding:0.25rem"></button> 206 </div> 207</nav> 208``` 209 210New: 211```html 212<nav class="flex items-center justify-between px-6 py-3 border-b border-[var(--border)]"> 213 <a href="/" class="flex items-center"> 214 <img src="/static/img/dd-logo.svg" alt="" width="22" height="28" class="align-middle mr-1">Diffdown 215 </a> 216 <div class="flex items-center gap-4"> 217 {{if .User}} 218 <a href="/" class="text-[var(--primary)] hover:underline">Documents</a> 219 <a href="/about" class="text-[var(--primary)] hover:underline">About</a> 220 <span class="text-[var(--text-muted)]">{{.User.Name}}</span> 221 <form method="post" action="/auth/logout"> 222 <button type="submit" class="text-[var(--primary)] bg-transparent border-none cursor-pointer hover:underline">Log out</button> 223 </form> 224 {{else}} 225 <a href="/auth/login" class="text-[var(--primary)] hover:underline">Log in</a> 226 <a href="/auth/register" class="px-3 py-1.5 text-sm bg-[var(--primary)] text-white rounded hover:bg-[var(--primary-hover)]">Sign up</a> 227 <a href="/about" class="text-[var(--primary)] hover:underline">About</a> 228 {{end}} 229 <button id="theme-toggle" class="text-[var(--primary)] bg-transparent border-none cursor-pointer text-lg" aria-label="Toggle dark mode" onclick="toggleTheme()"></button> 230 </div> 231</nav> 232``` 233 234- [ ] **Step 2: Replace main and alert styles** 235 236Old: 237```html 238<main> 239 {{if .Error}} 240 <div class="alert alert-error">{{.Error}}</div> 241 {{end}} 242 {{block "content" .}}{{end}} 243</main> 244``` 245 246New: 247```html 248<main class="p-6"> 249 {{if .Error}} 250 <div class="p-4 mb-4 bg-[var(--alert-error-bg)] border border-[var(--alert-error-border)] rounded text-[var(--danger)]">{{.Error}}</div> 251 {{end}} 252 {{block "content" .}}{{end}} 253</main> 254``` 255 256- [ ] **Step 3: Update base.html to link tailwind.css instead of style.css** 257 258Old: 259```html 260<link rel="stylesheet" href="/static/css/style.css"> 261``` 262 263New (keep style.css for now, we'll remove later): 264```html 265<link rel="stylesheet" href="/static/css/tailwind.css"> 266<link rel="stylesheet" href="/static/css/style.css"> 267``` 268 269- [ ] **Step 4: Test locally** 270 271Run: `make run` 272Open: `http://127.0.0.1:8080` 273Verify: Navbar, main content, and dark mode still work 274 275- [ ] **Step 5: Commit** 276 277```bash 278git add templates/base.html 279git commit -m "refactor: convert base.html to TailwindCSS classes" 280``` 281 282--- 283 284## Chunk 3: Replace style.css (base styles) 285 286**Files:** 287- Modify: `/Users/johnluther/projects/diffdown/templates/landing.html` 288- Modify: `/Users/johnluther/projects/diffdown/templates/documents.html` 289- Modify: `/Users/johnluther/projects/diffdown/templates/register.html` 290- Modify: `/Users/johnluther/projects/diffdown/templates/login.html` 291 292- [ ] **Step 1: Review style.css for reusable patterns** 293 294Run: `cat static/css/style.css` 295 296Identify patterns to convert: 297- `.btn`, `.btn-sm`, `.btn-outline` → Tailwind utility classes 298- `.alert`, `.alert-error` → Already handled in base.html 299- `.card` → Tailwind card styles 300- `.form-group`, `.form-label`, `.form-input` → Tailwind form styles 301 302- [ ] **Step 2: Convert landing.html** 303 304Read and convert elements to use Tailwind classes inline instead of custom CSS classes. 305 306- [ ] **Step 3: Convert documents.html** 307 308Same approach for the documents dashboard page. 309 310- [ ] **Step 4: Convert auth pages (register, login)** 311 312Convert form elements to use Tailwind classes. 313 314- [ ] **Step 5: Test all pages** 315 316Run: `make run` 317Verify: Landing, documents, login, register pages render correctly with dark mode 318 319- [ ] **Step 6: Commit** 320 321```bash 322git add templates/ 323git commit -m "refactor: convert auth and dashboard pages to TailwindCSS" 324``` 325 326--- 327 328## Chunk 4: Replace editor.css (complex layout) 329 330**Files:** 331- Modify: `/Users/johnluther/projects/diffdown/templates/document_edit.html` 332- Modify: `/Users/johnluther/projects/diffdown/templates/document_view.html` 333 334- [ ] **Step 1: Review editor.css** 335 336Key patterns: 337- `.editor-page` → Full height flex container 338- `.editor-toolbar` → Sticky top toolbar with flex layout 339- `.editor-rich` → Rich text editor container 340- `.editor-split` → Split pane layout (editor + preview) 341- `.invite-modal` → Modal overlay and box 342 343- [ ] **Step 2: Convert document_edit.html toolbar** 344 345```html 346<!-- Old --> 347<div class="editor-toolbar"> 348 <div class="breadcrumb"> 349 ... 350 </div> 351 <div class="toolbar-actions"> 352 ... 353 </div> 354</div> 355 356<!-- New --> 357<div class="sticky top-0 z-50 flex items-center justify-between px-4 py-2 border-b border-[var(--border)] bg-[var(--bg)]"> 358 <div class="flex items-center gap-2"> 359 ... 360 </div> 361 <div class="flex items-center gap-2"> 362 ... 363 </div> 364</div> 365``` 366 367- [ ] **Step 3: Convert editor containers** 368 369- [ ] **Step 4: Convert modal components** 370 371- [ ] **Step 5: Test editor functionality** 372 373Run: `make run` 374Create/edit a document 375Verify: Rich mode, source mode, preview, toolbar all work 376 377- [ ] **Step 6: Commit** 378 379```bash 380git add templates/document_edit.html templates/document_view.html 381git commit -m "refactor: convert editor pages to TailwindCSS classes" 382``` 383 384--- 385 386## Chunk 5: Replace markdown.css and diff.css 387 388**Files:** 389- Modify: `/Users/johnluther/projects/diffdown/templates/document_view.html` 390- Modify: `/Users/johnluther/projects/diffdown/templates/landing.html` 391- Create: Keep minimal overrides in a new file if needed 392 393- [ ] **Step 1: Review markdown.css** 394 395Mostly Markdown rendering styles - can keep as-is for now with minimal overrides. 396 397- [ ] **Step 2: Review diff.css** 398 399Diff view styles - can keep as-is. 400 401- [ ] **Step 3: Link tailwind.css in view templates** 402 403Add `<link rel="stylesheet" href="/static/css/tailwind.css">` to templates that don't have it. 404 405- [ ] **Step 4: Test view pages** 406 407Run: `make run` 408View a document 409Verify: Markdown renders correctly with dark mode 410 411- [ ] **Step 5: Commit** 412 413```bash 414git add templates/ 415git commit -m "refactor: add TailwindCSS to view pages" 416``` 417 418--- 419 420## Chunk 6: Cleanup and final integration 421 422**Files:** 423- Delete: `/Users/johnluther/projects/diffdown/static/css/style.css` (after verifying no missing styles) 424- Delete: `/Users/johnluther/projects/diffdown/static/css/editor.css` (after verifying no missing styles) 425- Modify: `/Users/johnluther/projects/diffdown/templates/base.html` 426- Modify: `/Users/johnluther/projects/diffdown/package.json` 427 428- [ ] **Step 1: Check for any remaining custom CSS references** 429 430Run: `grep -r 'class="' templates/ | grep -E '\.(btn|alert|card|form)' | head -20` 431 432- [ ] **Step 2: Remove unused CSS files one by one** 433 434Start with style.css - comment out the link in base.html, test thoroughly, then delete. 435 436- [ ] **Step 3: Keep minimal base.css for CodeMirror/Milkdown overrides** 437 438Create `static/css/base.css` for: 439- CodeMirror theme overrides 440- Milkdown-specific styles 441- Any other third-party library styles 442 443- [ ] **Step 4: Update base.html to remove old CSS links** 444 445```html 446<link rel="stylesheet" href="/static/css/tailwind.css"> 447<link rel="stylesheet" href="/static/css/base.css"> 448``` 449 450- [ ] **Step 5: Add CSS build to existing build command** 451 452```json 453"scripts": { 454 "build": "npm run build:css && npm run build:collab", 455 "build:css": "npx tailwindcss -i ./src/styles/input.css -o ./static/css/tailwind.css --minify", 456 "build:collab": "npx esbuild node_modules/prosemirror-collab/dist/index.js --bundle --format=esm --outfile=static/vendor/collab.js" 457} 458``` 459 460- [ ] **Step 6: Final testing** 461 462Run: `make build && make run` 463Test all pages: 464- Landing page 465- Login/register 466- Documents list 467- Document edit (rich + source mode) 468- Document view 469- About page 470- Dark mode toggle 471 472- [ ] **Step 7: Commit final cleanup** 473 474```bash 475git add -A 476git commit -m "refactor: complete TailwindCSS migration, remove legacy CSS" 477``` 478 479--- 480 481## Testing Checklist 482 483- [ ] Landing page displays correctly (logged out) 484- [ ] Login page renders with proper styling 485- [ ] Register page renders with proper styling 486- [ ] Dashboard shows document list with cards 487- [ ] Dark mode toggle works on all pages 488- [ ] Document editor loads in rich mode 489- [ ] Document editor toggles to source mode 490- [ ] Preview pane renders Markdown 491- [ ] Invite modal opens/closes properly 492- [ ] Document view renders Markdown 493- [ ] About page displays correctly 494- [ ] Navbar shows correct links for logged in/out users 495 496--- 497 498## Rollback Plan 499 500If issues arise: 5011. Keep old CSS files in a `static/css/legacy/` folder 5022. Restore old link in base.html: `<link rel="stylesheet" href="/static/css/style.css">` 5033. Test before committing deletions 504 505--- 506 507## Notes 508 509- TailwindCSS 4.x uses a different configuration approach - verify which version is installed 510- The `data-theme` attribute approach is already implemented, Tailwind's `darkMode: 'class'` should work with it 511- CodeMirror and Milkdown may need specific overrides in a separate CSS file 512- Consider using `@layer components` in input.css for complex custom patterns