Upgrading to Tailwind v4

Dark Helmet

This was kind of a pain, but here’s how I did it.

  • Start in clean branch. (obvi)
pnpm dlx @tailwindcss/upgrade
pnpm add clsx@latest
pnpm add tailwind-merge@latest
pnpm remove tailwind-animate
pnpm add tw-animate-css -D
  • Add @import 'tw-animate-css'; to globals.css after @import 'tailwindcss';
@import 'tailwindcss';
@import 'tw-animate-css';
  • Move fonts to @theme inline.
/* https://tailwindcss.com/docs/theme#referencing-other-variables */
@theme inline {
  --font-sans: var(--font-inter), Arial, Helvetica, sans-serif;
}
  • Add button css
@layer base {
  button:not(:disabled),
  [role="button"]:not(:disabled) {
    cursor: pointer;
  }
}
  • Port my “components” from my v3 config to v4 utility classes
@utility my-display-title {
  font-weight: 600;
  font-size: 24px;
  line-height: 32px;
}
  • Follow shadcn’s recommendation on css variables. I moved all my css vars to :root and .dark respectfully.
:root {
  --background: 0 0% 100%;
}

.dark {
  --background: 240 10% 3.9%;
}

@theme {
  --color-background: hsl(var(--background));
}
  • Then wrapped them in hsl() and removed hsl() from it’s usage.
:root {
  --background: hsl(0 0% 100%);
}

.dark {
  --background: hsl(240 10% 3.9%);
}

@theme {
  --color-background: var(--background);
}
  • I then moved the entire theme to inline because tailwind says you have to do this to reference other variables.
@theme inline {
  --color-background: var(--background);
}
  • Lastly, I replaced all h-* w-* with size-*. This is easy to do if you have prettier and prettier-plugin-tailwindcss installed. Since the tailwind migration ran in the first step, your class names might not be in order anymore. Run prettier --write ./src (obviously change the folder name to your components folder or whatever) and let prettier and prettier-plugin-tailwindcss sort everything. It will put height first, width second. Then you can run a regex find and replace. Search: h-(\d+)\s+w-\1. Replace: size-$1.

At this point you should be completely migrated to tailwind v4 and prepped for shadcn updates. I’d commit this into main/master and call it a day.