DEV Community

Cover image for Learnings from upgrading an existing Umbraco project to TailwindCSS 4
Søren Kottal
Søren Kottal

Posted on

4

Learnings from upgrading an existing Umbraco project to TailwindCSS 4

As a developer relying on TailwindCSS for crafting responsive designs effortlessly, the release of TailwindCSS v4 was an exciting occurrence. I jumped immediately into the project I’m currently working on, and started upgrading to the latest Tailwind version.

We use a somewhat simple approach to TailwindCSS in our Umbraco (ASP.net MVC) projects. In the website project, we initalize an npm package with npm init and then install TailwindCSS and necessary scripts for building. The source files is placed in ./UI/CSS/main.css and the config in ./UI/CSS/tailwind.config.js

Moving config to CSS, spacing changed

One of the big headlines of the new Tailwind version, is the addition of config in CSS. With that, all configurable values are now defined as custom properties, which adds some exciting new possibilities.

I like to stay as close to the default config as possible, so moving this over to CSS was not a big hurdle.

Though, I used to generate spacing values using an npm package I built, generate-tailwind-scale, I opted to leave this one behind, as the new spacing scale is dynamic.

I did use the same package for defining font-sizes though, but for this project, most of the font-sizes used already matched the default font-sizes. And the rest I simply changed into using arbitrary values, eg. text-[10px]

Automatic content scanning

In TailwindCSS v3, developers had to manually specify content paths to ensure Tailwind scanned the correct files for class usage. TailwindCSS v4 automates this process, scanning the entire directory it's executed from (except files specified in .gitignore). This clever tweak reduces configuration overhead.

Initially, I was worried that my output.css file would be considered for class generation since we don’t git-ignore it in our projects. If it were considered, removing a class would be impossible. Fortunately, it's not included, and when I added a one-time class like m-[1337ch], it appeared in the output but was promptly removed when unused elsewhere.

For content files ignored by .gitignore or located outside the project path, developers can specify paths via @source "../path/to/something", offering flexibility for more complex directory structures.

No more content transforms (for now?)

One missing feature in this content definition method is the ability to transform content during scanning. Previously, I configured content to allow writing @@container to add the @container class used by the container query plugin (now built into Tailwind!). Since my frontend is written in Razor/cshtml, @container would cause errors, while @@ worked as an escape character. For now, I have to wrap those like @("@container").

UTF-8 BOM encoding issue when working from Visual Studio

A notable challenge during the upgrade was related to file encoding. If your main Tailwind input file (main.css) was created using Visual Studio, it might be saved in UTF-8 BOM format. TailwindCSS v4 cannot correctly process UTF-8 BOM, causing incorrect or incomplete outputs.

This issue initially puzzled me, as the output.css file still contained Tailwind-specific directives like @utility and @import "tailwindcss".

The simple solution is to save your CSS file as UTF-8 without BOM. Once converted, TailwindCSS functions as expected.

Safelisting classes is deprecated

In a significant change, TailwindCSS v4 no longer supports class safelisting. For simple classes, I recommend documenting them in comments within your CSS files. I previously used regex for safelisted classes but had to refactor. Instead of using direct dynamic class definitions like class="bg-@Model.Color", I transitioned to custom properties: class="bg-(--my-bg)" style="--my-bg: var(--color-@Model.Color)". This approach also results in a cleaner, more maintainable solution, and fewer bg- and color- classes.

New method for custom utilities and variants

Another significant change is the method for defining custom utilities. The reliance on @layer utilities blocks is gone. Instead, in TailwindCSS v4, you can define them using a new directive: @utility. For example, to create a utility to hide scrollbars:

@utility scrollbar-hidden {
   /* css here */
}
Enter fullscreen mode Exit fullscreen mode

For variants it has become very simple, heres an example of a custom variant I had in this project:

@custom-variant mouse-only (@media screen and (pointer: fine));
Enter fullscreen mode Exit fullscreen mode

Simpler plugin setup

TailwindCSS v4 introduces a more streamlined way to incorporate plugins. Plugins are now embedded directly using directives such as @plugin "@tailwindcss/typography". This approach simplifies the integration process, reducing boilerplate code and setup time.

And it seems like v3 plugins are supported out of the box. At least my quantity query plugin just works.

JavaScript config still possible

While TailwindCSS v4 still supports JavaScript configuration files, the authors advises against using them. I think they will end up completely deprecated at some time, but at least not before an eventual v5 or v6 of TailwindCSS.

Conclusion

Updating to TailwindCSS v4 was quite straight forward. If not for the BOM encoding issue, I would say it was as easy as pie.

Next, I think I need to link into using the standalone CLI in the build pipeline, or maybe even when starting the website, to make our projects even simpler to work with.

Bonus tip

Working with TailwindCSS is a lot better, with the right tools. For VSCode, Tailwind Labs has their own intellisense extension.

For full Visual Studio, an unofficial extension offers similar functionality. Although it's not supporting v4 yet, the author is trying to make it happen in the next few weeks.

Billboard image

Monitor more than uptime.

With Checkly, you can use Playwright tests and Javascript to monitor end-to-end scenarios in your NextJS, Astro, Remix, or other application.

Get started now!

Top comments (1)

Collapse
 
jpkeisala profile image
Jukka-Pekka Keisala

I’ve been using Tailwind primarily on frontend-only projects, and I would love an official Visual Studio extension that fully supports Razor views. I haven’t yet tried integrating Tailwind into a .NET project, so if you happen to have a demo or a sample repository showing how you set everything up, I’d be thrilled to take a look. Thanks again for all the helpful details!

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay