Repository navigation
Upgrade Tailwind CSS 3 to 4 with v3 visual parity (T17, Dependabot #9) - #221
Conversation
Moves the theme into index.css (@theme), builds through @tailwindcss/vite, replaces tailwindcss-animate with tw-animate-css and drops postcss, autoprefixer and tailwind.config.ts. tailwind-merge 3 understands the v4 class names. Keeps the v3 look where v4 changed defaults: the v3 palette for the families in use, v3 space-x/space-y, hover only on fine pointers, grey placeholders, pointer buttons, table-cell padding and the container. Fixes the upgrade tool's variant="outline-solid" rewrite, overflow-wrap order, two line heights and Sonner's unlayered styles overriding the toast. Removes braces (GHSA-vfj7-8cjw-p6xm, Dependabot #9) from the tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v4 centres dialogs with the translate property, which tw-animate-css's transform keyframes add to, so the old -50%/-48% slide offsets sent every dialog in from far up and left. The dialogs now only rise 2% on open, as they did in v3. Breakpoints stay in px so a larger browser font doesn't move them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v3 emitted @layer components unlayered, so .motion-lift:hover and .motion-press:active outranked a button's shadow-* and duration-* classes; they are utilities now so specificity decides again. The [id] scroll margin is unlayered again and beats scroll-mt-*, as before. The select popper's offset is a translate the slide now adds to, so its slide starts one step further out and closes back onto the trigger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ℹ️ Recent review info
📝 Walkthrough
Note 🎁 Summarized by CodeRabbit Free
Comment |
Upgrades Tailwind CSS from 3.4 to 4.3 and closes Dependabot alert #9:
braces3.0.3 (GHSA-vfj7-8cjw-p6xm, high), which only Tailwind 3's chokidar, micromatch and fast-glob dependencies pulled in.npm auditnow finds 0 vulnerabilities andnpm ls bracesis empty.The goal is the same UI. Where v4 changed a default, this PR restores the v3 behaviour instead of accepting the change.
What changed
@tailwindcss/vitereplaces the PostCSS plugin.tailwind.config.ts,postcss.config.js,autoprefixer,postcssandtailwindcss-animateare gone, andtw-animate-cssreplaces the animate plugin.tailwind-merge3 knows the v4 class names. knip now readssrc/**/*.css, so it sees dependencies that only CSS imports.src/index.css. Tokens use@theme inline, so.darkandprefers-contraststill switch them at runtime. The app's shadow variables are renamed--app-shadow-*, because v4 owns the--shadow-*names. Tailwind still scans onlysrc/**/*.{ts,tsx}, like v3'scontent.npx @tailwindcss/upgrademade them, for exampleoutline-nonetooutline-hidden,break-wordstowrap-break-word,!mt-2tomt-2!,bg-gradient-to-*tobg-linear-to-*and[var(--x)]to(--x). The tool also rewrote the Button propvariant="outline"to"outline-solid"in six places; this PR reverts those.space-xandspace-ykeep v3's sibling margins. v4 puts a bottom margin on each child, and an inline<Label>ignores it, so every label lost 8px above its field.hoverOnlyWhenSupporteddid.@layer baseand.scrollbar-hidestill wins.[id]stays unlayered..motion-lift:hoverand.motion-press:activebecome@utility, so they still outrank a button'sshadow-*andduration-*. The toast surface classes get!, because Sonner's unlayered stylesheet would otherwise win.leading-*beats a responsivetext-*size, so the landing section headings and the sparring motion box pin the line heights v3 gave them.wrap-break-wordandwrap-anywherewere both present, v4's sort order letbreak-wordwin, and long URLs overflowed/expocit/sourcesat 375px. Those elements keep onlywrap-anywhere.translateproperty, and tw-animate-css's keyframes add atransformon top. Dialogs and alert dialogs drop their-50%/-48%slide offsets and keep v3's 2% rise on open. The select popper's slide starts one step further out and closes back onto the trigger.Before and after
I took 142 screenshots against
main(91e1e85) at 1280 and 375 px, in light and dark. They cover every main page: landing,/login,/privacy,/terms, the tools hub and each free tool, the sources page, 404 and every/app/*page. They also cover the onboarding tour, the start-debate and motion dialogs, the mobile menu, the pricing modal, a toast, keyboard focus and hover on three controls. I pixel-diffed each pair and compared computed styles element by element.transformreplaced their centring translate.shadow-lgbutton.The comparison caught these, and this PR fixes them:
/expocit/sourcesoverflowed at 375px;Checks
These are local results at f75e979. The current head, 32a0181, only adds the merge of main with T16's three locale-string changes; after that merge I reran lint, typecheck and the 605 unit tests, and they pass.
npm run verifypasses: docs, lint and knip, typecheck, 605 unit tests, 30 collaboration tests, 457 edge tests, build and check-site,test:sw(offline routes, precache and worker upgrade) and 47 smoke e2e tests. Three smoke tests needed a retry right after the Supabase stack stopped; run alone 3 times each with no retries, they passed every time.npm run test:e2e(desktop and mobile Chromium): 271 passed, 5 skipped, 2 flaky (Hungarian parity, which passed 3 of 3 times when rerun alone) and 6 failed. The 6 failures are three specs on each project:loading-performance.spec.ts:63,navigation-performance.spec.ts:344andsearch-quality.spec.ts:7. All three fail the same way onmain(91e1e85) and on this branch's base (a7610fa), so this change didn't cause them.drive allin fake mode, on desktop-chromium and mobile-chromium: 62 passed and 2 skipped on each project.npm audit:found 0 vulnerabilities. On main it reports 5 high, allbracesthrough tailwindcss.Notes for review
bg-primary/10solid wherecolor-mix()is missing. Vite's default JS target is already Safari 16 and Chrome 107, so the gap is small.docs/architecture.mdrecords this.@propertyrules andcolor-mixfallbacks.[&>svg]:data-[state=open]:rotate-180never matched in v3 either. I left it for a follow-up.🤖 Generated with Claude Code