WIP: Upgrade Tailwind V3 to V4 - #594
tarunsinghofficial wants to merge 14 commits into
Conversation
|
Warning Review limit reached
Next review available in: 40 minutes Limit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (10)
📒 Files selected for processing (48)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (23)
💤 Files with no reviewable changes (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughChangesTailwind CSS 4 migration
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to This dependency and build-tooling upgrade has no actionable merge-blocking risk remaining based on the supplied evidence and is merge-ready after normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
flowbite-svelte-icons@1.6.2 peers tailwindcss ^3.3.2, which breaks plain npm install on Tailwind v4. The package is not imported anywhere.
maplibre-gl pulls @mapbox/jsonlint-lines-primitives@2.0.3, which requires Node >=22. With engine-strict, npm install fails on CI (Node 18 lint / Node 20 test). Override to 2.0.2.
Tailwind v4 pulls @tailwindcss/oxide, which requires Node >=20.
0.8.1 crashes with "a.startsWith is not a function" on non-Svelte files, making prettier --check exit 2 (lint fails in CI). 0.6.14 still sorts Tailwind v4 classes. Reformat affected files.
dfd0078 to
78ebf0b
Compare
Import Flowbite components from subpath exports instead of the package barrel, and rewrite internal barrel imports in vite.config.js. Prevents Vite SSR crash after dependency re-optimization on Tailwind v4.
Code reviewFound 3 issues:
wayfinder/src/components/LoadingSpinner.svelte Lines 8 to 10 in a383acf
wayfinder/src/components/navigation/Header.svelte Lines 250 to 252 in a383acf
🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
aaronbrethorst
left a comment
There was a problem hiding this comment.
The structural decisions here are all the ones I'd have made. Moving to @tailwindcss/vite and dropping postcss.config.js/autoprefixer, keeping tailwind.config.js alive through the @config bridge so the COLOR_* → generatePalette() pipeline keeps working, and the @custom-variant dark (&:is(.dark, .dark *)) fix for v4's dark-mode specificity — that's a well-reasoned path through a migration that could easily have turned into a rewrite. Deferring the Flowbite upgrade and paying for it with a scoped Vite patch is a defensible trade too, and it's honestly commented.
The title says WIP and I think that's accurate. Three things need fixing, and two of them silently change what renders.
1. Dead opacity utilities survive the codemod
bg-opacity-* and ring-opacity-* are on v4's removed-deprecated list — they emit no CSS at all, so these fail silently rather than erroring:
LoadingSpinner.svelte:9—bg-opacity-80onbg-neutral-800. The loading overlay becomes fully opaque instead of 80%, so the map disappears behind it during every load.RouteModal.svelte:80—bg-opacity-80onbg-brand-accent. Same, on the route modal header.SurveyLauncher.svelte:43—focus:ring-opacity-50.
The replacement is the slash modifier: bg-neutral-800/80, bg-brand-accent/80, focus:ring-green-400/50. Worth grepping the whole tree for the rest of that family (text-opacity-*, border-opacity-*, divide-opacity-*, placeholder-opacity-*) since the codemod clearly didn't catch them all.
2. v4's default border color change isn't compensated
In v3, border defaulted to gray-200. In v4 it defaults to currentColor, so every bare border with no explicit color now draws in the element's text color. src/app.css has no base layer restoring the old behavior, and there are bare border classes on lines this PR modified — several in the LanguageSwitcher / Header dropdown region:
rounded-md border (flex-shrink-0 wrapper)
rounded-md border (8x8 button)
rounded-md border px-2 (font-semibold trigger)
Those pick up the surrounding text color, which on the dark surface means a border that either changes color or effectively vanishes. This is also almost certainly why it slipped through — the QA pass was dark-mode only.
The upgrade guide's own compat snippet is the cheapest fix, and it goes in app.css:
@layer base {
*, ::after, ::before, ::backdrop, ::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}Auditing every bare border and giving it an explicit color is the better long-term answer, but for a migration PR I'd take the compat layer and do the audit separately.
3. 19 MB of screenshots got committed
docs/tailwind-bef/ has nine PNGs totaling about 19 MB — raw macOS screenshots with timestamp filenames, no "after" set, and nothing referencing them. Those want to be in the PR description, not in git history where they're permanent. Please drop them from the branch.
Smaller notes
- The PR body says "Adapt
tailwind.config.jsfor ESM plugin imports," but that file is byte-identical todevelop—require('@tailwindcss/forms')and all. That's harmless (jiti handlesrequirein an ESM config under@config), but the description should match the diff. focus:outline-noneis still used in ~14 places. In v4 that'soutline-hidden; plainoutline-nonedrops v3's forced-colors fallback. Most sites pair it with a visiblefocus:ring-*, so the loss is confined to forced-colors mode — low priority, but it's part of the same codemod gap.- The codemod rewrote class strings inside
docs/superpowers/plans/*andspecs/*. Those are dated historical records; rewriting them makes them no longer describe the code as it was. I'd revert those files. overrides: { "@mapbox/jsonlint-lines-primitives": "2.0.2" }appears with no explanation — worth a line in the body or a comment.postcssis still in devDependencies afterpostcss.config.jswas deleted.- The
flowbiteSvelteNoCircularplugin regex-patchesnode_modulessource, so it'll silently no-op on any flowbite-svelte update rather than failing loudly. Worth a version assertion so a future bump doesn't quietly break the build.
Worth saying explicitly since the PR reads as reaching v4: this lands on the compat path, not the CSS-first @theme migration — tailwind.config.js still drives everything through @config. I think that's the right call given the brand pipeline needs JS at build time, and I verified the COLOR_* flow still works end-to-end under v4. Just want it to be a stated choice rather than an assumed way-station.
Fix the opacity utilities and the border default, drop the screenshots, and I'll do a full pass on the rest. This is good work on a genuinely unpleasant migration.
| role="status" | ||
| aria-live="polite" | ||
| class="absolute inset-0 z-50 flex items-center justify-center bg-neutral-800 bg-opacity-80 md:rounded-lg" | ||
| class="bg-opacity-80 absolute inset-0 z-50 flex items-center justify-center bg-neutral-800 md:rounded-lg" |
There was a problem hiding this comment.
bg-opacity-80 is on v4's removed-deprecated list, so this emits no CSS — silently, no build error.
The loading overlay goes from 80% to fully opaque, which means the map is completely hidden behind it during every load rather than dimmed.
-class="bg-opacity-80 absolute inset-0 z-50 ... bg-neutral-800 md:rounded-lg"
+class="absolute inset-0 z-50 ... bg-neutral-800/80 md:rounded-lg"Same family to sweep for across the tree: text-opacity-*, border-opacity-*, divide-opacity-*, placeholder-opacity-*, ring-opacity-* (there's one in SurveyLauncher.svelte:43).
| <div class="space-y-4"> | ||
| <div> | ||
| <div class="min-h-36 rounded-lg bg-brand-accent bg-opacity-80 p-4"> | ||
| <div class="bg-brand-accent bg-opacity-80 min-h-36 rounded-lg p-4"> |
There was a problem hiding this comment.
Same as LoadingSpinner — bg-opacity-80 emits nothing in v4, so this header renders at full opacity.
bg-brand-accent/80 is the replacement.
| @tailwind components; | ||
| @tailwind utilities; | ||
| @import 'tailwindcss'; | ||
| @config '../tailwind.config.js'; |
There was a problem hiding this comment.
This is the right place for the v4 default-border-color compat layer, and it's missing.
In v3 a bare border defaulted to gray-200; in v4 it defaults to currentColor, so every bare border now draws in the element's text color. There are several on lines this PR modifies (the LanguageSwitcher / Header dropdown uses rounded-md border with no color three times).
The upgrade guide's own snippet:
@layer base {
*, ::after, ::before, ::backdrop, ::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}Auditing each bare border and giving it an explicit color is the better end state, but for a migration PR the compat layer keeps the blast radius at zero.
Summary
Migrates Wayfinder from Tailwind CSS v3.4 to v4, using the
@tailwindcss/viteplugin and keeping the existing JS config via@configso env-var branding (generatePalette(), brand/surface colors) continues to work unchanged.Closes #353
What changed
Build pipeline
tailwindcss@4+@tailwindcss/vitevite.config.js; removepostcss.config.js,autoprefixer, and@tailwindcss/line-clamp(built into v4)@tailwindcss/formsandprettier-plugin-tailwindcssCSS / config
@tailwinddirectives with@import "tailwindcss"+@config "../tailwind.config.js"@custom-variant darkusing:is(.dark, .dark *)so dark utilities beat light ones on the same node (fixes search pane in dark mode)tailwind.config.jsfor ESM plugin importsCodemod + fixes
@tailwindcss/upgradefor v4 utility renames (bg-gradient-to-*→bg-linear-to-*,break-words→wrap-break-word, etc.)@referenceinStopMarker.sveltefor@applyin component stylesmaplibre-glas a direct dependency (peer of@maplibre/maplibre-gl-leaflet, dropped by--legacy-peer-depsinstalls)Flowbite
Spiked
flowbite-svelte@1.33— needs Svelte 5.40+, Datepicker build/API churn, and component API updates across 6 files.flowbite-svelte@0.47works with Tailwind v4 (build + UI verified), so it stays on 0.47 for this PR. A Flowbite 1.x upgrade can be a follow-up issue.Components in use: Modal, Button, Tabs/TabItem, Datepicker, Radio, Checkbox, Input (6 files).
Visual QA
Compared 8 dark-mode before/after screenshots (search pane, All Routes, route detail, map pins, etc.) — no visible regressions after the dark-mode specificity fix.
Test plan
npm run lintnpx vitest run(103 files, 1794 tests)npm run buildSummary by CodeRabbit
Refactor
Bug Fixes
Documentation