Amblem
Furkan Baytekin

Why I hate Tailwind CSS But Keep Using It

A critical look at Tailwind CSS pros & cons from a developer's perspective

Why I hate Tailwind CSS But Keep Using It
100
7 minutes

Tailwind CSS is becoming a sector standard for web development. It is a utility-first CSS framework that allows you to build complex designs with ease. Iโ€™ve been using it for a while, still using, will continue using but I hate it. Let me explain why.

Problems with Tailwind CSS

The Size Problem

Tailwind CSS is a utility-first CSS framework which means you must (not write but) generate tons of classes for your components. Letโ€™s compare it with a simple card component. And list 100 products on the page.

First write the component with vanilla CSS.

css
.card { background-color: #f3fafd; border-radius: 0.5rem; border: 1px solid #e0e0e0; padding: 1rem; } .card-title { font-size: 1.25rem; font-weight: 600; } .card-description { font-size: 0.875rem; color: #666; }

Source code:

js
const cards = Array.from({ length: 100 }, (_, i) => ( <div className="card"> <h2 className="card-title">Card Title</h2> <p className="card-description">Card Description</p> </div> ));

Output (Browser will parse this code):

html
<div class="card"><h2 class="card-title">Card Title 1</h2><p class="card-description">Card Description 1</p></div> <div class="card"><h2 class="card-title">Card Title 2</h2><p class="card-description">Card Description 2</p></div> <div class="card"><h2 class="card-title">Card Title 3</h2><p class="card-description">Card Description 3</p></div> <div class="card"><h2 class="card-title">Card Title 4</h2><p class="card-description">Card Description 4</p></div> ...

114 chars per line, practically 11400 chars for 100 cards. itโ€™s about 11.13KB of code.

Now write the same component with Tailwind CSS.

Source code:

js
const cards = Array.from({ length: 100 }, (_, i) => ( <div className="bg-gray-100 rounded-lg border border-gray-200 p-4"> <h2 className="text-lg font-semibold">Card Title</h2> <p className="text-sm text-gray-600">Card Description</p> </div> ));

Output (Browser will parse this code):

html
<div class="bg-gray-100 rounded-lg border border-gray-200 p-4"><h2 class="text-lg font-semibold">Card Title 1</h2><p class="text-sm text-gray-600">Card Description 1</p></div> <div class="bg-gray-100 rounded-lg border border-gray-200 p-4"><h2 class="text-lg font-semibold">Card Title 2</h2><p class="text-sm text-gray-600">Card Description 2</p></div> <div class="bg-gray-100 rounded-lg border border-gray-200 p-4"><h2 class="text-lg font-semibold">Card Title 3</h2><p class="text-sm text-gray-600">Card Description 3</p></div> <div class="bg-gray-100 rounded-lg border border-gray-200 p-4"><h2 class="text-lg font-semibold">Card Title 4</h2><p class="text-sm text-gray-600">Card Description 4</p></div> ...

175 chars per line, practically 17500 chars for 100 cards. itโ€™s about 17.09KB of code.

Getting the same result, but with an extra 5.96KB of code. Is this a problem? If you create a server-side rendered page and serving to a huge audience, yes it is. Lets calculate the difference in bytes. Assuming 100.000 users, will browse 100 pages. Imagine this is an e-commerce website.

17.09KB - 11.13KB = 5.96KB

5.96KB * 100.000 * 100 = 59.600.000KB

59.600.000KB = 59.6MB

So, the difference is 59.6MB. Is this a problem? If you create a server-side rendered page and serving to a huge audience, yes it is.

If you use a client-side rendered page, the difference will not be noticeable.

The Readability Problem

Now lets create a something more complex. A modal component. First with vanilla CSS. Then with Tailwind CSS.

Source code:

css
.modal { background-color: #f3fafd; border-radius: 0.5rem; border: 1px solid #e0e0e0; padding: 1rem; } .modal-title { font-size: 1.25rem; font-weight: 600; } .modal-description { font-size: 0.875rem; color: #666; } .modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; } .modal-button { color: #e0e0ff; border: none; padding: 0.5rem 1rem; border-radius: 0.25rem; } .modal-button-danger { background-color: #ff0000; } .modal-button-success { background-color: #00ff00; }
html
<div class="modal"> <h2 class="modal-title">Modal Title</h2> <p class="modal-description">Modal Description</p> <hr /> <div class="modal-actions"> <button class="modal-button modal-button-danger">Delete</button> <button class="modal-button modal-button-success">Save</button> </div> </div>

307 chars in total. modal class clearly shows us itโ€™s a modal. modal-title, modal-description, modal-actions, modal-button, modal-button-danger, modal-button-success is also clear.

Now with Tailwind CSS:

html
<div class="bg-gray-100 rounded-lg border border-gray-200 p-4"> <h2 class="text-lg font-semibold">Modal Title</h2> <p class="text-sm text-gray-600">Modal Description</p> <hr /> <div class="flex justify-end gap-2"> <button class="bg-red-500 text-white px-4 py-2 rounded-md">Delete</button> <button class="bg-green-500 text-white px-4 py-2 rounded-md">Save</button> </div> </div>

395 chars in total and doesnโ€™t tell us what it is. bg-gray-100, hmm, okay, something with light gray background. rounded-lg, Okay rounded corners. But what is it?

The Order Problem

You can have a classlist like this:

html
<div className="bg-green-100 bg-red-500"/>

Which one will be applied? The last one? No, I donโ€™t know. I must see the result. We must know the order of the classes in the result of purged and minified version of the css file generated by tailwind-jit compiler.

In Vanilla CSS you are the decision maker. You decide the order of the classes.

The Impossibilities

There are missing text-shadow, transforms, etc. Tailwind CSS does not support text-shadow. You can fallback to filter: drop-shadow but itโ€™s not the same. Or you can achieve it via a hacky way.

html
<span className="[text-shadow:0_0_10px_rgba(0,0,0,0.5)]"></span>

What a brilliant way to achieve text-shadow! ๐ŸŽ‰

Imagine you need to transform an element in different ways. Lets look at the example:

html
<div style="transform: translate(4rem) rotate(45deg);"></div> <div style="transform: rotate(45deg) translate(4rem);"></div>

These are totally different style. First one is translate first then rotate. Second one is rotate first then translate.

In Tailwind CSS you can try:

html
<div class="translate-16 rotate-45"></div> <div class="rotate-45 translate-16"></div>

But this will not work. Both will be the same. Itโ€™s because Tailwind CSS create the output class in an order. You must fallback to the style attribute or custom class in CSS. You can still fallback the hacky way ๐Ÿคฎ:

html
<div class="[transform:_translate(4rem)_rotate(45deg);]"></div> <div class="[transform:_rotate(45deg)_translate(4rem);]"></div>

This will create classes like ๐Ÿคฆ๐Ÿปโ€โ™€๏ธ:

css
.\[transform\:_translate\(4rem\)_rotate\(45deg\)\] { transform: translate(4rem) rotate(45deg); } .\[transform\:_rotate\(45deg\)_translate\(4rem\)\] { transform: rotate(45deg) translate(4rem); }

Partial CSS Support

Yes, Tailwind CSS creates a purged and minified CSS file. With the help of JIT compiler, it only includes the classes that are used in the project. But it completely includes all the classes in the output file.

For example, You can create a css file for order card components and serve it to the client. It will be cached on the client side. The client will ask for the css file only when a product card component is used. This means you can partially serve the css file to the client. But in Tailwind CSS, you must serve the entire css file to the client.

Imagine a user visited your website. Your main page using only the 70% of the classes in the css file. If you were use vanilla CSS, you can serve the css file only required for common components and required for the main page. Common components will be cached on the client side. So on other pages, you can serve the css file only remaining parts.

Why I Still Use Tailwind CSS

Itโ€™s still faster to use it. Yes, it looks like a mess. probably you hit more keystrokes. Harder to read. But you can organize your code by splitting elements into smaller components.

Just create components like Modal.Container, Modal.Header, Modal.Body, Modal.Footer and use them in your project. CSS classes may not give you a good understanding but component names will do.

Bandwidth is cheap. Calculations are cheap. Client-side rendering fast and takes a lot of workload from the server.

Easy to add custom styles. This means fixing annoying parts is possible.

You can fallback to css files and use @apply directive. (Developers of Tailwind CSS does not recommend this. But itโ€™s still possible.)

Conclusion

We live in a faster world, we have shorter deadlines. We need to build faster. Tailwind provides a clear developer experience. I hate it but I still use it.


Album of the day:

Suggested Blog Posts