Upgrading Business Class to Tailwind CSS 4

Table of contents

Business Class started on Bulma but I finally switched to Tailwind CSS when the version 3 came out. Tailwind devides people, but I believed that with version 3 and @apply we could finally build a nice little theme.

Tailwind CSS 4

Tailwind recently came with Tailwind 4. There are lots of new features from a new high-performance engine to a redesigned color palette. But it also means the extra work we now have to do.

Upgrading to Tailwind 4

Dependencies

A first obvious step to upgrade is to update the tailwindcss package to ^4.0.0 in package.json and run:

$ npm update tailwindcss

Then we also install the new CLI:

$ npm install @tailwindcss/cli

Build task

The new Tailwind CLI has to be used in our build process, so let's update the task in package.json :

   "scripts": {
-    "build:css": "tailwindcss -i ./app/assets/stylesheets/*.tailwind.css -o ./app/assets/builds/application.css --minify
",
+    "build:css": "npx @tailwindcss/cli -i ./app/assets/stylesheets/*.tailwind.css -o ./app/assets/builds/application.css
 --minify",
     "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds"
   }

Changed imports

Next we need to fix the import in application.tailwind.css :

-@tailwind base;
-@tailwind components;
-@tailwind utilities;
+@import "tailwindcss";

After this step you should be able to run yarn build:css as usual, although with errors complaining about old Tailwind classes.

Dropped config

Tailwind 4 dropped support for tailwind.config.js which we can now safely delete. However, this is how we loaded our forms plugin for the beautifully-styled forms that are suddenly without styling.

Let's add the plugins back with @plugin directive inside application.tailwind.css :

@plugin "@tailwindcss/forms";
@plugin "@tailwindcss/typography";

A bit unfortunately we won't get the same styling we initially had as Tailwind will pick the default unstyled styles instead of the 'simple' theme.

There is also a way to keep loading the configuration for the time being with @config directive:

@config "../../../tailwind.config.js";

Still, no luck for keeping the original form CSS. Nevertheless, there was a mention that Tailwind might bring a CSS-only variant for v4.

New defaults

Some of the preflight CSS has changed. You might like the new behaviour or not. To reset the buttons pointers and dialog centering, we can add the rules back to our base layer:

 @layer base {
+  button:not(:disabled),
+  [role="button"]:not(:disabled) {
+    cursor: pointer;
+  }
+
+  dialog {
+    margin: auto;
+  }
+

New classes

At last, we need to update the old deprecated Tailwind classes.

This is true for selects:

   select {
-    @apply text-sm mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-
indigo-200 focus:ring-opacity-50;
+    @apply text-sm mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring-2 focus:rin
g-indigo-200/50 focus:outline-none;
     @apply w-full sm:max-w-[500px];
   }

For buttons:

.button.is-primary,
   .stripe_button button {
-    @apply bg-gray-700 hover:bg-gray-900 text-white !important;
+    @apply [&]:bg-gray-700 [&]:hover:bg-gray-900 [&]:text-white;
   }

For menu links:

.menu-navigation ul a {
     @apply text-base flex items-center w-full p-2 px-3 rounded-lg text-start leading-tight transition-all text-gray-600
 outline-none;
-    @apply hover:bg-gray-200 hover:bg-opacity-80 focus:bg-gray-200 focus:bg-opacity-80 active:bg-gray-50 active:bg-opaci
ty-80 hover:text-gray-900 focus:text-gray-900 active:text-gray-900;
+    @apply hover:bg-gray-200/80 focus:bg-gray-200/80 active:bg-gray-50/80 hover:text-gray-900 focus:text-gray-900 active
:text-gray-900;
   }

For profile links:

.profile-links {
-    @apply absolute right-0 z-10 px-1 py-1 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-op
acity-5 float-none focus:outline-none;
+    @apply absolute right-0 z-10 px-1 py-1 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black/5 float
-none focus:outline-none;
   }

And tables:

.table tbody {
-    @apply divide-y divide-gray-200;
+    @apply border-t border-b border-gray-200;
+  }
+  .table tbody > tr + tr {
+    @apply border-t border-gray-200;
   }

We'll also need to add border-gray-200 explicitly to td.short to maintain the light color. Same thing with .menu-navigation .

What's next

I am not sure if I'll release a separate update for Tailwind CSS 4. However, Tailwind 4 opens a path towards latest versions of shadcn/ui, DaisyUI, and other modern component libraries. Their consideration will be a topic for another post.


© Business Class Blog