What is good and what is bad Tailwind CSS, or “Let’s say you have a startup!”

Tailwind CSS — utility-first CSS framework. I will share the pain and joy that arose during its operation.

The Tailwind library is quite young, the first beta versions appeared in 2017. But by the number of stars and forks, you can understand that the community around has developed a solid one. The tool provides us with a large set of ready-made classes and utilities that make it easier and faster to style an application / site. You just write short names of pre-designed classes: colors, padding, positioning. There are enough classes for sizing, typography, background animation and much more.

Below you can see how it all looks when implemented in React JS.

Compatibility is also pleasing: everything is quietly assembled under Next.js, and under Vite, and under Angular, and under Create React App. Moreover, it is possible to build using the PostCSS plugin, which makes the tool really well implemented in any system.

But Tailwind also has disadvantages. It’s actually a pretty cool tool.

Let’s say you have a startup…

Let’s imagine you and your friends decided to make a startup: you rented a garage for work, gathered the best team, and picked up a cool technology stack. As for CSS, you decided to find a trendy styling solution, and your eye fell on Tailwind. Everyone likes it, colleagues are happy, and nothing portends trouble.

Suddenly, the team receives money from an investor and is already going to cut new business features, but is faced with the fact that for comfortable cohabitation with Tailwind it needs to be “put in order”, otherwise all further scaling is in jeopardy. And in general, it becomes difficult to maintain overgrown styles, the developers every morning wipe their mean tears with their left sleeve.

Why scaling and maintaining a trendy styling solution is sad:

  • The framework is not informative. There are simply no clear class names in it, and how to navigate in the DOM tree during debugging is not at all clear.

  • There are a lot of classes, they are simply hard to remember, especially for newly arrived young developers. We are growing, remember?

There is a lot of pain, as they say: “There is something to fall into despair.” The good news is that all the problems of a framework can be condensed into one: you need to clearly understand how to prepare it. If you initially build a neat approach, then possible pains when scaling are practically leveled. In other words, our team has a tactic, and we must stick to it.

Let’s go through each of the cons separately.

Tailwind is an uninformative solution

Imagine that I need to debug something in the UI. For example, the problem is in the retailer’s card. I often start debugging by opening DevTools and looking for the component I need by the class name.

Here is an example of simple navigation in a project styled with SCSS. Everything is very simple and transparent.

Tailwind will take away clear, readable class names from you. Instead of something semantic, you get sticky top-0 z-40 w-full and so on.

How to deal with it? You can just add data attributes. Yes, you will not get away from the footcloths of classes. How to “pack” them beautifully – they did not come up with. Yes, incomprehensible long names will remain, but you can navigate the DOM tree, you can search. Testers will thank you because data attributes are very useful for test automation.

Another simple and obvious solution is to beautifully divide the code into components. If you have React, use its DevTools, and now looking for something in the component tree is again simple and pleasant. If not React, we get upset and switch to React. This is, of course, a joke, all tools for their tasks.

There are too many classes in Tailwind and it’s hard to remember them

Sometimes I hear something like this: “The tool is great for prototyping. True, we have not worked with him before, but we want to throw something on our knees. This is not a very good story, it will not work on the knee. When starting from scratch, it will take a lot of time to understand what classes there are in general, how to properly prepare the framework, so that the work will line up more or less systematically.

The logic here is simple: the more you write, the better it is remembered. The set of classes is still limited. As always, practice and hours of work will do the trick.

Other than that, there is a magic extension to solve the problem intelligent sense. You put it in VS Code or any popular IDE and get a handy Tailwind-sagestor: the extension tells you the possible options. At first, you often have to look at the documentation and use additional tools to navigate the code. But, when you write 10-20 components, everything will reach a state of complete automatism, and the problem will disappear.

Inevitably, a footcloth is going from the classes

You have an element, you add a data attribute to it – and there are a lot of classes here. If you need to make an adaptive interface, where the style solution will be very different for large, medium and small screens, the task looks just monstrous.

The code becomes quite readable if you use the library classNames or something similar.

We work with it like this: we break a long class into small subclasses, which we semantically combine across screens. You can split classes according to other criteria, for example, depending on the values ​​of props. Decomposition is everything here. And you should always remember about the component approach: the more accurately and accurately we split, the less cumbersome classes we will have in the future.

However, it is important to say that by magic all this does not happen. Keep in mind the team agreements or tighten the rules of ESLint or another linter. Otherwise, it will be difficult to control that people write code correctly. plugin eslint-plugin-tailwindcss gives a set of rules that can be used to maintain the code. It will sort and order the classes, prohibit the use of inconsistent values.

Styling in Tailwind is non-reusable, poorly scalable, and unsystematic

About reuse. Tailwind has a very useful @apply directive that adds your custom classes to the component layer and allows you to use them everywhere. This means that you can write a separate class and use a set of other classes in it.

@layer components {
  .btn-primary {
    @apply py-2 px-4 bg-blue-500 text-white
         font-semibold rounded-lg shadow-md 
         hover:bg-blue-700 focus:outline-none 
         focus:ring-2 focus:ring-blue-400 

And now you can extend the class of any element with a directive. Comfortable enough!

Using such apply classes gives us the ability to export entire sets of styles. Roughly speaking, I can write my own components, put all the styles into a common core library and reuse them in my other projects!

About scalability. The framework provides the user with a very flexible config. There is tailwind.config.js which is created at the root of your project when you initialize Tailwind. It allows you to customize everything the way you want. With this config, you can severely limit the development team, giving them access only to typography, colors, indents, which are provided for by your design system. The screenshot describes the behavior of boxShadow and colors.

It is quite possible to put up with the tradeoffs above, some of them are resolved positively. We either improve the experience of interaction, or we get used to it.

However, there are problems in Tailwind that are really hard to deal with.

It’s easy to get an uncontrolled class dump

For example, we have this class:

The developer used everything possible, combining:

  • Curve and non-semantic data attribute.

  • Margins in pixels, in negative values ​​and in rem.

  • Colors in text values ​​and through hex.

It turned out very garbage and not very readable. You will say that the case is inorganic, and I will tell you that there is something else in the startup code 🙂 Therefore, it is important to negotiate or tighten ESLint. I already wrote about this above, but it is so important that I allow myself to repeat myself.

There is no other solution, only this way.

Most custom solutions in Tailwind are awkward and complicated

Before you consider this a problem, ask yourself if you often need to reinvent the wheel. The class library is huge, and you can find something for almost any need.

But there are situations when you definitely need a personal bike. For example, Tailwind is not good at clip-path. The tool is developing, new versions are coming out, but yes, at the moment there really are a number of use cases that have remained without implementation.

You can always write native CSS and attach it to utilities via the @layer directive. It seems nothing complicated, but let’s imagine that every time you write such a piece for any more or less custom solution. It’s scary to think how many times the time and effort will increase. If your project styling is too custom, think ten times over if you should use Tailwind.

No RTL out of the box

There is no built-in RTL in Tailwind in principle. It may not be needed in your project, but who knows from which country investors will come to a startup.

An excellent framework community solved this problem with a library tailwindcss-rtl. It provides an additional set of classes that your RTL will start with without any difficulty. No wonder they say: “Add .js to any word – and you get some kind of existing NPM library.” Here the community really decides!

Accessibility is lame

Let’s say we have a div that should only be read by the screen reader and should not be displayed in the UI. To do this, we are given an sr-only class that absolutely positions the element, hides it, and applies a number of additional styling. That’s all – there is simply nothing more out of the box in Tailwind.

Inspires hope with package in testing Tailwind CSS Beta 2 with accessibility goodies like aria-* attributes of different classes. They allow you to determine the compatibility of the browser with a particular CSS solution. There are many other useful features as well.


While we’re on the subject of NPM packages, I’d be sad if I didn’t say that with React, you can write cross-platform styles using one of these couple of cool packages: one, two. The UI will be reusable: we can build React Native for the Web using something like react-native-web and get a powerful combo with Tailwind styling.

The Tailwind community also gives us ready-made UI kits (for example, tailwind kit). You take the finished code, copy-paste it into your project, styled with Tailwind, and that’s it. Everything works, no additional libraries are needed.

Like any tool, Tailwind has its own tradeoffs, but it is fast and cross-platform. When you get to the peak of the learning curve, spending N hours and writing M components, everything will become intuitive, styling work will become a pleasure.

Can code written with Tailwind scale? Yes, but you need to know how to cook it. If you don’t negotiate or use your hard and fast rules for ESLint, the team will fail.

Tailwind gives a cool community: if you use its power, you can implement almost any idea. For me, this solution turned out to be convenient and well scalable within the project. Everyone else is free to draw their own conclusions. Use or not – you decide.

The tech team of SberMarket maintains social networks with news and announcements. If you want to know what’s under the hood of high-load e-commerce, follow us on Telegram and on YouTube. And also listen podcast “For tech and these” from our IT managers.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *