When I started to use TailwindCSS - I was an early adopter. It was before the JIT mode that comes in V3. I did not like it. But the time passed and it get more attention and improved a lot. So I gave it a second chance - in my daily job.
Stuff I Like About Tailwind
TailwindCSS does not require js runtime - which makes it faster compared to CSS in js solutions.
It works with almost every platform and framework without difficult config (styled-components
for example that is tied to react only). I works well on the web - you can configure it as a PostCSS plugin, via CLI or with CDN (for scaffolding) and React native have solutions like twrnc
. Also for emails with ‣.
It is also very popular. It's a big advantage because the community makes it better - more extensions, plugins, and IDE supports. It get a lot of improvements because of that - for example the new JIT mode was inspired by Windi CSS and then added to Tailwind. We now have other projects inspired by Tailwind - windy CSS, Unocss, stylifycss, and master css.
The JIT mode of Tailwind is cool - it replaces the purge css and enables us to generate custom styles and arbitrary values on the fly. For example w-[200px]
will give us the same result as width: 200px
.
We can create plugins and override default colors and breakpoint constants and configure them to our design system easily
We don't have to create files for CSS compared to CSS modules. It shines when we usually change the component file name and we need to search for its matching css file and rename it too to be aligned. So we have less boilerplate (no need to import the matching css file), and less files - which give us cleaner file structure with less noise (especially if you use file based routing framework like NextJS). Using the same file in the IDE and looking at its styles without context switch to css file is nice. We also refer to hierarchy easily
It comes with a reasonable CSS reset by default ‣
The classes are mostly atomic - which makes them easier to customize - and create more smaller css files.
Tailwind CSS-generated styles are reused so the total CSS size is smaller compared to other non-reused css styles. Which is good for performance too
We don't have to create names for element classes - which is good and bad. Sometimes the name helps us identify the element and sometimes not.
Copy-paste code from sites that uses tailwindcss / snippets to your code is easier to do compared to copy-paste CSS and HTML
Stuff I Don’t Like
The devtools experience is worse than the regular CSS
In CSS modules we can see the matching react component name in the devtools with the class name - which is nice. We can see it with react devtools but it requires more clicks. In tailwind, we can search the CSS classes in the codebase but it gives us less information.
We can see the class names in the browser and modify them. Yet, generating CSS in the browser devtools feels better with CSS modules. Also - in jit mode and without a proper extension - we don't get autocompletion and sometimes the jit classes are not generated.
Also in Devtools - hard to inspect the CSS properties that are applied to an element compared to a single css class. We have to look in the HTML for each class and remember its meaning / look at the class properties for each class.
It's also hard to catch which class creates which property. They are not combined because they are created by different classes. Our eyes struggle to look at the long horizontal css class list compared to short lines of css properties with values.
Does not work well with tools like stylelint. IMO stylelint is an ecosystem too and it can improve DX.
Currently - its prettier plugin tends to clash with other prettier plugins https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/31
I still don't think its default design system values are better than configuring them in the CSS variables file. If you have a design system - you have to configure the values yourself in css variables file/tailwind config.
Else - their design system is good but contains too many values. We want to restrict them if we can. Yes - 10 shades of red is better than all shades of red - but if we use only 3 shades - it's not optimal to have 10. But we can configure it in tailwind to have specific colors
It's not just CSS syntax - meaning that each new CSS feature we want to use in will have to be configured separately (the jit mode helps with it a bit).
We need to search in its good docs for more complex CSS properties
CSS is the platform and the base for every styling. Tailwind is just a collection of classes. The platform won't change - tailwind will change. So better to bet on the platform - even if you write tailwind
Learning curve. Although it's easy and we have good docs - we have to search in the docs for every simple CSS property we forget until we use to it.
Selectors are less supported in Tailwind. It's ok - most of the time we don't need to write complex selectors (and simple CSS is better) but sometimes we need and we have to look on the docs
Group selectors scoping problems can be created. We need to avoid using group class that can be applied to the element that contains children.
In this example - the blue background color will apply to the Child text when hovering over the root parent element. In CSS modules we can avoid it by creating a different class name and using the matching class selector https://play.tailwindcss.com/IoDgQtU0sg

Stuff like CSS grid template areas are hard to write
Same for non-trivial CSS like using calc for example - https://stackoverflow.com/questions/65976223/how-to-use-calc-in-tailwind-css Or values like min, max, and clamp are hard to use. With that being said - the config allows us to define classes for them but compared to regular css it's a downside
Custom transitions/animations / linear-gradients are hard to write with tailwind
Sometimes we get a bunch of large CSS class list that is hard to read/understand
We can avoid problems like unused css classes for free
The claim that tailwindcss requires fewer chars to write is not always true. Modern IDEs can have a better auto-completion for regular CSS. For example - mine support Emmet CSS syntax and typing ai:c will generate align-items: center. We can also create snippets if needed.
https://docs.emmet.io/cheat-sheet/ for more CSS values in Emmet style
We also can't use shortened css properties like border: 1px solid var(--border)
Compared to css modules its hot reload is worse
We also need to enable soft wraps in the IDE in order to see all the classes that element have:

Which is also hard to read, understand and debug.
Conclusion
TailwindCSS is a decent option to write CSS. It's very flexible, performant popular, and well supported in frameworks. It's not so hard to learn and its documentation is good. The main advantage over CSS modules is that it requires fewer boilerplates (no need to create a separate file) and create smaller css file (which is good for performance).
For simple CSS stuff, it has a better DX and the fact that we don’t need to create a separate file for CSS is nice. But for more complex CSS it starts nagging and has a worse DX than regular CSS.
it's not the native CSS - so we will ask ourselves again and again how to translate the css we know to tailwind css classes = it's not trivial all the time. Over time - we will get more experience and will search less and less in the documentation.
Fun fact is that after I used to tailwind - I was too lazy to create the css files on every component file.
TODO:
update - after using Tailwind for ~4 months at my daily job, I handled some of the problems. I may write about it in a different post. Plugins like labeled grouping, advanced arbitrary variants, tailwind config, and even the eslint plugin I want to try. Returning to regular CSS after using tailwindcss in a real projects for several months seems readable, but I became more lazy typing stuff. And the long CSS class list in the HTML seems less freighting. I use it to search for the matching HTML element in the code. Still I don’t remember the the design tokens in my head - h-2 = ? in px / rem for example, is h-20 exists? p-2 size = ?
And it actually make me use more advanced css selectors. It’s because I use only tailwind class composition or global css. And the arbitrary values means I can do some cool selectors. Also for grouping. I can create a class name / tailwind named group (via plugin or the default tailwind group) / add data attribute and select it in css.