Tailwind sucks
I've been doing a lot of vibe coding, and my particular tools of choice include v0.dev (as a Vercel fanboy). By default, v0 will use tailwind for styling. This means I've been using Tailwind for the first time, and... I hate it.
Tailwind CSS has become popular in the frontend community, but its design philosophy contradicts several long-standing best practices in software engineering. While it markets itself as utility-first and ergonomic, in practice it often creates messy, hard-to-maintain codebases. Here’s why.
- Separation of Concerns Is a Strength, Not a Problem
One of the core tenets of maintainable software is separation of concerns. HTML (structure), CSS (presentation), and JS (behavior) are typically kept distinct for clarity and reusability. Tailwind violates this principle by jamming styling directly into the markup. You end up with a long string of class names living in your JSX, which makes the file harder to read and reason about.
With traditional CSS or CSS-in-JS, the styles live in their own files or objects. That means you can open a component, understand its logic, and then refer to its styles if and when you need them. Tailwind encourages the opposite: put every style decision directly into the markup, which creates visual noise and makes the logic of the component harder to spot.
- Tailwind Is Illegible and Clutters the Markup
Tailwind’s “utility-first” approach is often utility vomit. Here’s a basic example:
<button className="bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-700">Click me</button>
That’s a single button, yet it already feels bloated. Multiply that across a large component or page, and it becomes unreadable fast.
Compare that to:
<button className="primary-button">Click me</button>
And in your CSS:
.primary-button {
background-color: #3b82f6;
color: white;
font-weight: bold;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
.primary-button:hover {
background-color: #1d4ed8;
}
You get clear separation, better reusability, and drastically improved legibility in both files.
Tailwind is worse than inline styles, which at least use a predictable JavaScript object format and show up in the same syntax as everything else in JSX. Tailwind is like cryptic shorthand sprayed through your DOM tree.
- Tailwind Is More Complicated and Inconsistent Than Plain CSS
Tailwind’s class names are neither intuitive nor consistent. You have to memorize a set of custom keywords (like mt-4, text-gray-700, px-6) that are essentially a DSL for writing CSS—but a worse one.
There are also inconsistencies and arbitrary constraints: • Why is padding p-4 but max-width max-w-md? Why not mw-md? • Why does Tailwind collapse responsive, pseudo, and variant styles into strings like sm:hover:text-red-500? • Why does customizing it require digging into a tailwind.config.js file and understanding its internal token system?
CSS already has a steep learning curve. Tailwind replaces it with a new one, where you’re learning Tailwind’s version of CSS. And unlike CSS, it’s not a web standard.
⸻
Summary
Tailwind may be fast for prototyping, but it’s not a scalable or maintainable styling solution. It breaks separation of concerns, reduces readability, and introduces its own learning curve and inconsistencies. For most real-world teams and projects, traditional CSS (or CSS-in-JS) remains the better long-term approach—clearer, simpler, and easier to debug.