Skip to content
A tangled knot of blue glowing wires labeled "Tailwind" sits next to a glass tower labeled "CSS" with organized levels for structure, layout, and theme.

Tailwind as a Headwind

I’ve been contemplating writing a critique of Tailwind CSS for years. Initially, I assumed Tailwind was simply “not for me” and moved on. However, as it has grown in dominance, I’ve realized its impact on the ecosystem is impossible to ignore.

Technically, Tailwind is a CSS generator. It scans your project for utility classes and generates a stylesheet based on predefined rules. This approach ensures that unused class names are excluded, and the use of shorthands (aliases) instead of long-form CSS rules is understandably popular. At first glance, the benefits are clear: reduced bundle sizes and the convenience of styling elements without switching to a separate CSS file. It seems peaceful—but that peace is fragile.

If you stick strictly to Tailwind’s built-in classes, you are safe for the time being. However, the moment you implement custom plugins to support modern CSS features, you hit a wall. The CSS standard is evolving rapidly, and Tailwind cannot always stay up-to-date. To access new features, you must often do the heavy lifting yourself. Writing complex @supports fallbacks within a Tailwind configuration file result in code that is far less readable than native CSS.

Furthermore, there is the risk of technical debt. You have no way of knowing if your custom solution will align with Tailwind’s future official implementation. One day, you may be forced to migrate by replacing obsolete class names, or worse, your design may break due to naming conflicts.

Complicated styles are particularly difficult to express in Tailwind. Styles that require deep nesting or heavy reuse become chaotic when crammed into class attributes. This “flat” approach makes maintenance a nightmare. For instance, writing calc() functions in Tailwind is a frustrating experience compared to the clean syntax of a stylesheet.

One might argue that we should simply switch to native CSS for complex styles since Tailwind is compatible with it. However, in the era of modern at-rules, nesting has become essential. When dealing with multiple at-rules, nesting provides structural clarity and improves the quality of the developer’s logic. It allows me to compose refined CSS more quickly because I can see the hierarchy in a clean list rather than a compact muddle of strings.

While native CSS nesting is now widely supported, I still cannot abandon Sass. I cannot ignore the ~8% of users on browsers that don’t support native nesting; without it, the entire layout breaks. During this transition period, Sass remains crucial. Yet, when looking for guidance on integrating the two, the Tailwind documentation states:

“Think of Tailwind CSS itself as your preprocessor — you shouldn’t use Tailwind with Sass for the same reason you wouldn’t use Sass with Stylus.”

This is dogmatic. Tailwind is powerful, but it is not a total replacement for a preprocessor. By failing to acknowledge its weaknesses or accept superior tools for specific tasks, Tailwind Labs is letting down developers.

As CSS evolves, development is becoming more straightforward, not more complex. Modern features such as @scope, nesting, sibling-index(), and scroll-driven animations allow us to build maintainable projects without legacy overhead. CSS is becoming more structural and comprehensive.

Structure overshadows flatness. Clarity comes from organization, not obfuscation. We should not step back to a time when we put all our styling in style attributes. For the sake of our common future, please do not choose Tailwind.

About this Post

This post is written by Mr. Will, licensed under CC BY-NC 4.0.

#webdev #css #tailwindcss

This post is written by human, then polished by gemini-3-flash-preview. The cover image is generated by gemini-3-pro-image-preview.