Upgrading from v3 and browser support

Tailwind CSS · Expert · 12 min

Many codebases and tutorials are still v3; knowing the differences prevents subtle visual regressions.

v4 is a rewrite with a CSS-first configuration. The official tool does most of a migration: npx @tailwindcss/upgrade (run it on a clean branch) updates dependencies, converts tailwind.config.js into @theme and CSS directives, and rewrites class names in templates.

What changes and why it matters:

  • Imports: @tailwind base; @tailwind components; @tailwind utilities; becomes @import "tailwindcss";. The CLI and PostCSS plugin moved to @tailwindcss/cli and @tailwindcss/postcss.
  • Renamed scales: the old unnamed shadow, rounded and blur are now shadow-sm, rounded-sm and blur-sm, and the old -sm versions became -xs. outline-none is outline-hidden; flex-shrink-0 is shrink-0.
  • Removed: bg-opacity-* and similar opacity utilities; use the /50 modifier.
  • Changed defaults: borders and dividers default to currentColor instead of gray-200; ring is 1px instead of 3px; placeholder text uses the current colour at 50%; buttons use cursor: default.
  • Syntax: variants stack left to right, the important modifier moves to the end (bg-red-500!), and CSS-variable values use bg-(--x). Use var(--color-red-500) in CSS instead of theme().

Browser support is the one hard constraint. v4 relies on @property, cascade layers and color-mix(), so it needs Safari 16.4+, Chrome 111+ and Firefox 128+. Projects that must support older browsers should stay on v3.4.

Example

# Migrate a v3 project (on a clean git branch)
npx @tailwindcss/upgrade

# Typical class changes the tool rewrites:
#   shadow-sm        -> shadow-xs
#   shadow           -> shadow-sm
#   rounded          -> rounded-sm
#   outline-none     -> outline-hidden
#   flex-shrink-0    -> shrink-0
#   bg-opacity-50    -> bg-black/50  (merged into the colour)
#   !mt-0            -> mt-0!
#   bg-[--brand]     -> bg-(--brand)

# Default changes to review visually afterwards:
#   border  -> currentColor instead of gray-200 (add border-gray-200 where needed)
#   ring    -> 1px instead of 3px (use ring-3 for the old look)
git diff --stat

Remember

  • Run npx @tailwindcss/upgrade on a clean branch
  • Unnamed scale steps shifted: shadow -> shadow-sm, shadow-sm -> shadow-xs
  • Borders default to currentColor and ring to 1px
  • v4 requires Safari 16.4+, Chrome 111+, Firefox 128+

Watch out for

  • Trusting the automated upgrade without visually checking borders and rings, whose defaults changed.

Check yourself

  1. After upgrading, bare 'border' elements suddenly look dark. Why?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: tailwindcss.com/docs/upgrade-guide