Published on

Essence of Tailwind


I've been using Tailwind for CSS in my projects lately. At first glance, Tailwind looks pretty ugly and hard to decipher. Just look at this.

Tailwind is not pretty

It sure is ugly-looking but it works well! Drawing inspiration from Atomic CSS and utility classes (1 class = 1 style), Tailwind makes a few strong design decisions:

  1. Markup and style should live next to each other (bye, bye stylesheets): This makes it easier to write and update components since you don't have to change two different files. Global CSS is also hard to maintain over time since a change in the global namespace could affect any number of things on your website.
  2. Coming up with class names is a waste of time.
  3. CSS is an expressive style language that maps directly to browser rendering models and so, there's no need to learn a new mental model for styling (which some other CSS frameworks force you to).

You might note that all of this is true for inline CSS as well! Why not just use regular CSS and just avoid using stylesheets?

<div style={{
   color: 'black',
   backgroundColor: 'white'

True. You do get these benefits with inline CSS. But Tailwind does better in a couple of ways:

  1. With inline CSS, you can't have media queries or hover states. In Tailwind, you can do this via modifiers which you can add to any utility class: bg-sky-600 hover:bg-sky-700
  2. Tailwind comes with a design system (colors, fonts, spacing, etc.). You can write stuff like m-2 (margin of 2 units) rather than specify exact pixels. As a result, across your website, spacing will be uniform, colors will come up from your theme and so on.

That's the essence of Tailwind. It's a very low-level CSS framework composed of a bunch of utility classes and a customizable design system.

How does Tailwind work?

Tailwind processes all of your code, looking at className properties in React components but not just that. In case you have some dynamically generated classes (const classes = disabled ? "color-gray-100" : "color-indigo-100" ), Tailwind will also look for utility classes in your JS. Once it parses all utility classes in the code, it generates the corresponding CSS and writes them to a file. I use it with React and it's supported with Create React App v5 and above.

If your framework doesn't support it (I had a hard time getting it working in CRA v4), you can always use the CLI to watch and generate the CSS file. You'll specify the directory to be watched. And you'll have to load the output CSS file in your HTML. Good setup and troubleshooting tips here.

Editor Setup

VS code support via the official Tailwind plugin makes Tailwind very pleasant to write and less ugly to read. The plugin offers autocomplete so you don't need to remember every utility class. It also lets you hover over utility classes to see the CSS they will generate.

You should definitely add the following to your VS Code settings for your Tailwind project:

"css.validate": false
"editor.quickSuggestions": {
   "strings": true

The second setting tells VS Code to autocomplete not just at the start of a string but also in the middle. Without this, you'll only get autocomplete for utility classes when your cursor is at the end of className=" but not when it's at the end of className="mx-4 . That is, without this setting, you only get autocomplete for the first class in a list.

Now that we finally don't have to worry about naming classes, what else can we bike shed about? Well, the order of utility classes, of course. Fret not. The Tailwind just released a Prettier plugin last month to end these pointless debates. The Prettier plugin automatically sorts classes. And it's not configurable.

One annoyance with the plugin is that the features only work for values of className attributes.

No plugin support for arbitrary strings

My only other gripe is that the linting seems finicky.


From the docs, "the most important implication of how Tailwind extracts class names is that it will only find classes that exist as complete unbroken strings in your source files." If you have code like error ? 'text-red-600' : 'text-green-600', CSS will be generated for text-red-600 and text-green-600. But if you have text-{{ error ? 'red' : 'green' }}-600", your Tailwind-generated CSS file will in fact not contain any classes text-red-600 and text-green-600.

This stumped me for a bit. I didn't know this and I went about creating an invisible <div> that had all the classes that were being used with conditional logic. But if you use unbroken strings for all class names, that solves it.