My mixed feelings about Tailwind CSS
Tags: webdev, css, javascript
Published: 2020-05-20 20:09:20

There's a lot of  **hype**  going around in Web Development. Every now and then a new framework/library/tool appears that gets the attention of many developers, possibly even to a point of being called  _"the next big thing"_.

A while ago, I decided to leave my JavaScript comfort zone, to see what's "the next big thing" in other parts of Web Development such as HTML, or CSS. I quickly discovered that it's now  [Tailwind CSS](https://tailwindcss.com/)  - utility-first CSS framework. So, why's that, what are my personal thoughts about it?

# Utility-first CSS

Let's first discuss what utility-first CSS even means as it's not only a cool marketing term. You see, Tailwind is basically a set of small CSS class names that you can use to change certain styles of your element. Consider the code snippet below:

```html
<button
  class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
  Button
</button>
```

Here you can see an example button, styled using various different Tailwind utilities. We've got e.g.  `text-white`  to set the color to white,  `py-2`  to set vertical (top and bottom) padding to what Tailwind indicates as 2 (  `0.5rem`  by default), and  `hover:bg-blue-700`  to set the background color of the button to Tailwind's 700 shade of blue (  `#2b6cb0`  by default).

Overall, I think you get the idea - a giant set of different class names with an arguably fairly understandable  **naming scheme**. But what are the pros and cons of such a solution?

# Advantages

The first thing people usually ask when introduced to Tailwind is  _"why not just set a CSS property?"_. That's a pretty logical question. Why use class names like  `text-white`  instead of just setting  `color: white`  directly on a class name dedicated to the specified element?

## Utility-first

Here, it's pretty important to understand the possibilities of utility-first classes. First off, they're very  **reusable**. Instead of repetitively writing  `color: white`  in multiple classes, you just drop the  `text-white`  class and that's it! Plus, you don't have to create it yourself since the library already does it for you.

Next up, no one says that single utility must set only a single property (although that's how things are in most cases). Tailwind utilities like  `clearfix`  make for very  **convenient**  and ready-to-use solutions that you'd otherwise have to search the web for.

And speaking of convenience, Tailwind's utilities like  `px-{n}`  accelerate the whole design process in a weird way. Instead of having to think about perfect values for padding, margin, width, or whatever, you're limited only to a small subset of them, with pre-set increments. I know this might sound quite illogical at first, but trust me - it's really helpful!

## Customization

So, Tailwind's utility-first approach has many advantages, but what else does the framework provide? Well, undeniably vast and deep  **customization**  options. Tailwind allows you to configure most, if not all of its utilities within a single  `tailwind.config.js`  file.

Such a deep level of customization is important for multiple use-cases, with the main one being  **design systems**  creation. Tailwind gives you customization options that allow you to maintain the utilities' versatility, while easily modifying their values to fit your custom style across the board.

## Ease-of-use

I've already touched upon it when speaking about the convenience of the utility-first approach, but I'll repeat myself as this is one of my favorite features of Tailwind. This library is extremely  **comfortable**  and easy to use. Don't let yourself think that it's too hard to learn because of all the utilities it gives you. The naming scheme is so convenient that once you get a grasp of it, you'll know exactly how to use the entire library. And besides, there are extensions for many different IDEs and code editors (like  [VS Code](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)) that provide you with helpful  **autocompletion**  capabilities.

About the naming scheme though. It's arguably one of the most important parts of any heavy utility-based library, and Tailwind made it just right.  `p-{n}`  for padding,  `text-white`  for setting color,  `-{n}`  for using a certain value for the utility, and  `md:`  or  `hover:`  for handling breakpoints and different states of the element - all that is truly brilliant!

# Down-sides

Surely, after reading all the advantages you might think that I'm positively biased towards Tailwind. Mind you that all you've just read is simply me describing my experiences with the library. But sadly a coin always comes with 2 sides and so, Tailwind isn't without flaws.

## Usage

While the whole concept of utility-first CSS sounds great on paper, it's really quite rough in implementation. I mean just take a look at a slightly more complex use-case than the button we've covered earlier:

```html
<div class="md:flex bg-white rounded-lg p-6">
  <img class="h-16 w-16 md:h-24 md:w-24 rounded-full mx-auto md:mx-0 md:mr-6" src="avatar.jpg" />
  <div class="text-center md:text-left">
    <h2 class="text-lg">Erin Lindford</h2>
    <div class="text-purple-500">Customer Support</div>
    <div class="text-gray-600">erinlindford@example.com</div>
    <div class="text-gray-600">(555) 765-4321</div>
  </div>
</div>
```

Do you feel what I feel? Isn't the HTML snippet here getting a little...  **crowded**? This example is taken from Tailwind's landing page, and even after looking at it for a short moment, you start to get this awkward, uncomfortable feeling. It's unavoidable - the more utilities you use, the less enjoyable, and potentially even readable your HTML/JSX/Vue template/whatever becomes.

## Tooling

Apparently, the Tailwind team is aware of this issue, as the framework does provide a solution in the form of  **custom directives**. Here's an example for the outer-most element from the previous example:

```css
.container {
  @apply bg-white rounded-lg p-6;
  @screen md {
    @apply flex;
  }
}
```

Here we basically turn the previous use of Tailwind utilities into a dedicated CSS class, that's composed of the same utils. To make that happen, Tailwind provides custom directives, like  `@apply`  (for applying Tailwind utilities to another class name) and  `@screen`  (for interacting with Tailwind's breakpoints as both  `hover:`  and  `md:`-like utilities are not available in this syntax), which we use above.

So what's the issue here? Well, with custom directives comes the use of custom processors, and with that comes some additional setup. Now, it's not like processing code for additional features is something bad, it's just that I personally try to stay away from such means when it comes to CSS. Call me old-fashioned, but I've got enough processing going on the JavaScript side already.

I understand that tools like  [PostCSS](https://postcss.org/)  with its  _Autoprefixer_  or  _postcss-preset-env_  are really useful when writing modern CSS. But that's a bit different from introducing new directives to your code - directives that are specific to and only work with a given tool. This drastically limits the "portability" of your CSS and makes any potential changes of underlying framework or library much more difficult.

But let's say that you're willing to go with the crowded HTML, only to not use any pre-processing tools. Well, in this case, you're still out of luck, as you most likely would want to do at least some processing to shrink the giant  **144 KB**  size of Tailwind. Of course, it's hard to expect a small size from a library of this kind, but it's the CSS processing requirement that's the real issue for me.

## Customization

I've already mentioned all the customization options of Tailwind as its advantage, but sadly, it's kind of a double-edged sword.

Sure, all these options are great to have if you're willing to take some time to really create your own design system from ground-up. But arguably, it's not what most people are going to do and it's the defaults with only small tweaks that they'll be relying upon. And that's where all this customization hurts the most. The sheer amount of all the options, plugins, and variants can be really daunting or  **overwhelming**  for both beginners as well as more advanced Tailwind users. Of course, nothing prevents them from using the defaults without any configuration what-so-ever, but I think you get the point.

Tailwind is not the only tool that suffers from the need to find a  **balance**  between customizability and convenience. It's like a guessing game - you're never sure if you're going to win.

# Prototope

So, overall, I've got pretty mixed feelings on Tailwind. On one hand, I appreciate the utility-first design, but on the other, I don't like the way it looks in the HTML file nor how it can be integrated into CSS with custom directives. That's why I ended up not using Tailwind in any of my bigger projects but was inspired to create my own library instead -  **Prototope**.

## Utility-first CSS-in-JS

[Prototope](https://areknawo.com/prototope)  is a utility-first CSS-in-JS library, created specifically to go alongside my UI library -  [Isotope](https://github.com/Isotope-js/isotope). It's heavily-inspired by Tailwind's utility naming scheme and overall design, with a difference of it being a JS instead of a CSS library.

```javascript
import { bgColor, h, w } from "@isotope/prototope";
import { createDOMView } from "@isotope/core";

const view = createDOMView(document.body);
const { node } = view.$(Prototope());

node.div([bgColor("primary"), h(8), w(8)]);
```

All of Prototope's utils are essentially  [Isotope directives](https://areknawo.com/isotope/#docs%3Edirectives)  - functions that can modify Isotope nodes they're used on.

After  [initializing Prototope](https://areknawo.com/prototope/#docs%3Esetup)  with a single top-level  `Prototope()`  component, you can use all of its utilities just like that. Isotope nodes accept arrays of directives and so you can easily combine, merge, and operate on your custom utility sets the way you want.

There's also support for  [breakpoints](https://areknawo.com/prototope/#docs%3Ebreakpoints)  and element  [variants](https://areknawo.com/prototope/#docs%3Esub-selectors)  - just like in Tailwind!

```javascript
import { bgColor, hover, h, w } from "@isotope/prototope";

// ...

node.div([
  bgColor("primary"),
  hover(bgColor("secondary")),
  h(8),
  w(8),
]);
```

Instead of dashed names, Prototope accepts config values for certain utils in the form of simple function parameters.

## Behind the scenes

Now, Prototope works a little bit different than Tailwind, in a sense that it applies its classes at runtime, through JS. And the way it does so is also different. Instead of applying multiple classes to an Element, it applies only a single one, with a hashed name, and then sets all the styles on it. Sort-of like inline styles, but with support for  `@media`  and  `:hover`-like rules.

And of course, there's a  [server-side implementation](https://areknawo.com/prototope/#docs%3Essr)  too, for those of you who are wondering.

## Interested?

Prototope still doesn't solve all of the utility-first CSS problems. And yet, it's something I recommend you to try if you're into CSS-in-JS and want to feel how it works with the Tailwind-like approach. If you're interested in it and Isotope, definitely go check out  [the docs](https://areknawo.com/prototope),  [the repo](https://github.com/Isotope-js/isotope/tree/master/packages/prototope), and feel free to play with it on your own!

# Bottom line

So, this is just my opinion on Tailwind. Like I've said, I really like what it's doing, but it still has some major drawbacks. With Prototope, I wanted to fix a few of them and make a similar library that's a bit more fit for my personal type of use. If you find it interesting for you too, I encourage you to check it out.

Anyway, I'm interested to see  **your thoughts**  about both Tailwind and Prototope down in the comments below! If you're interested in more up-to-date web development content, feel free to  **follow me**  on  [Twitter](https://twitter.com/areknawo),  [Facebook](https://facebook.com/areknawoblog), or here on **Dev.to**. Thanks for checking in!