Why Tailwind Is The Best Choice For Custom CSS

Most of the CSS code bases I have worked with followed a similar pattern. At first, they were clean and easy to understand. But they always turned into a nightmare to maintain. Developers weren’t comfortable making changes to the CSS for fear of breaking the HTML in another part of the app. I have tried various solutions like Bootstrap, BEM, SMACSS, and others, but they always ended up in the same place: spaghetti CSS that was hard to work on.

Tailwind is a new kind of CSS framework that directly addresses the hard-to-maintain aspect of CSS. It’s also easy to customize, enforces consistency in your design, and helps keep your CSS bundle size small.

Why Traditional CSS Is Hard To Maintain

The reason CSS becomes hard to maintain is because it’s highly coupled to HTML. Splitting HTML and CSS isn’t a separation of concerns, but simply splitting one concern into multiple files. You may think that styling is completely separate from your DOM structure, but I bet it’s not. Think about how often your CSS code depends on HTML elements being in a specific structure. Or how often you need to add CSS classes to your HTML in a certain order to achieve the right look.

Tailwind takes a different approach: placing the styling in the HTML using CSS classes so small they only apply one style rule each. You get the benefits of having all of your layout code in one place, but none of the drawbacks of inline CSS. At first the code looks ugly, but you get used to it fast. And an easy-to-maintain code base is worth it.

If you use React or Vue, you can get similar benefits by using scoped styles in your components. You can use Tailwind for this, but you can also it anywhere else, making it more versatile. After using Tailwind for a while—including on this site—I’m convinced it’s the best choice for most projects that need custom styles.

What The Code Looks Like

As I mentioned, Tailwind gives you lots of small helper classes to use directly in your HTML. You get helper classes for font size, margin and padding, colors, flexbox and flex grid, and much more. Pretty much any CSS rule you want to use has a Tailwind helper class with a range of values.

Let’s consider a simple link element. Traditionally, you might have HTML and CSS like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
<a class="my-component" href="/">
  A Link
</a>

<style>
.my-component {
  font-family: 'Lato', 'sans-serif';
  font-size: 20px;
  color: '#625D53';
  font-weight: bold;
  margin-top: 16px;
}
</style>

But with Tailwind, you’d write this:

1
2
3
<a class="font-body text-xl text-gray-600 font-bold mt-4" href="/">
  A Link
</a>

The Tailwind classes target individual CSS attributes like font, font size, text color, font weight, and margin (mt stands for margin-top). There are different values for each “group” of classes. For instance, font size (text-) has the values sm, md, lg, xl…up to 4xl. Margin top (mt-) has values from 0 up to 96, and even negative values by using -mt-.

The number values aren’t pixels. Instead they’re based off of rem and go up in roughly 4 pixel jumps. Because you have less granularity than 1 pixel increments, this makes picking sizes pretty easy. You can pick a number and then adjust it up and down until the size looks right. mt-3 might look too small, mt-5 too big, but mt-4 just right. It also enforces consistency in your design because you won’t use 12px in one place and 13px somewhere else. There are fewer options to choose from and that’s a good thing.

Why Tailwind Is Perfect For Projects With Custom Designs

CSS frameworks tend to fall in two categories: ones that give you pre-styled classes and components to use (like Bootstrap) and ones that simply tell you how to organize your CSS (like BEM). Both of them have problems. The pre-styled class frameworks are hard to customize if you ever need to deviate from what they give you. If you choose one of these frameworks for custom designs, you’ll have to override a lot of their styles unless your design is very similar to what they give you.

The frameworks that only tell you how to organize your CSS, like BEM, are much better for custom designs. But, they also tend to create tangled code bases faster. They depend on developers always adhering to the framework’s principles when writing code—something that is hard to keep up over time and with multiple developers.

Tailwind instead lets you customize your design up front and then creates CSS classes for you to work with throughout the entire project. You will start the installation process by defining a tailwind.config.js file where all of your custom design can be declared—colors, font stacks, and sizes of various things. After that, the framework is yours, custom-tailored for your site.

How Tailwind Enforces Consistency In Your Design

I’ve noticed Tailwind enforcing my designs to be consistent in two areas: sizing and colors.

For sizing, instead of letting you pick any pixel value for a given class, Tailwind limits your options. By limiting your options, you get a small range of usable sizes and don’t have to think about exact pixel amounts. The sizes it gives you increase in small jumps—around 4 pixels for small sizes and the jumps get bigger as the sizes get bigger. This gives you enough flexibility for real-world projects but keeps sizing consistent.

For colors, you can specify any colors you want to use in your tailwind.config.js file, which is great to have them defined all in one place. The default theme for Tailwind has colors numbered from lightest to darkest—100 to 900, like font-weight. You don’t have to use this convention, but I highly recommend you do. The idea is to pick a range of usable colors so that you have a palette to handle any design task. Then when you’re coding, instead of choosing from a million shades of blue, you only have 9 to choose from.

By limiting the number of choices you have, you are forced to think about your design up front and stay consistent. This pays off in your final product, making your app look much more cohesive from page to page.

Tailwind Config Examples

Let’s look at how the Tailwind configuration file works. Here are my font family declarations in the tailwind.config.js file for this site. Once I define these, I can use the classes font-body or font-heading on any DOM element and get the exact same font-family stack.

1
2
3
4
5
6
7
8
9
10
// tailwind.config.js
module.exports = {
  theme: {
    fontFamily: {
      body: ['Lato', 'sans-serif'],
      heading: ['Oswald', 'sans-serif'],
    },
    ...
  }
}

To use Google Fonts like I am, you either import the fonts in your HTML’s head or at the beginning of your main CSS file where the Tailwind setup code is. Then you can use the string name here like I did.

And here are some of the colors defined for this site. The colors are ordered like font-weight, smaller numbers are lighter and larger numbers are darker. Now, everywhere in my app, I only have ten shades of gray to choose from.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// tailwind.config.js
module.exports = {
  theme: {
    ...
    colors: {
      transparent: 'transparent',
      current: 'currentColor',
      gray: {
        50: '#FAF9F7',
        100: '#E8E6E1',
        200: '#D3CEC4',
        300: '#B8B2A8',
        400: '#A39E94',
        500: '#857F73',
        600: '#625D53',
        700: '#504A41',
        800: '#423D34',
        900: '#27241D'
      },
      purple: {
        25: '#F4F1F5',
        30: '#E9E4EB',
        50: '#E9E2F7',
        100: '#CFBDF0',
        200: '#A083D7',
        300: '#8665C4',
        400: '#724FB4',
        500: '#6540AA',
        600: '#512D99',
        700: '#421F85',
        800: '#34176D',
        900: '#240C53',
        1000: '#373147'
      },
      ...
    }
  }
}

This generates color classes like text-transparent, text-gray-100, text-purple-700, and so on. Having all of your colors defined in one file where they can be reused is a huge win. And naming colors with numbers from lightest to darkest is something you should start doing immediately. Can you imagine not ever seeing color names like gray-lighter and gray-lightest anymore? I first read about this strategy in the Refactoring UI book, which is a fantastic book.

You also may have noticed that I have more shades of purple than gray. The config file is always open to modification or extension. If your colors are spaced out 100 numbers apart, you can add numbers in between if you need to. Like my purple-25, purple-30, and purple-1000 colors. These kinds of additions are encouraged, because now instead of having a random #E9E4EB in my CSS code, I have purple-30 defined in one spot and I can use it in the rest of my app anytime.

You can also customize margin sizes, font sizes, and more just like this. Having everything defined in one place and able to be reused makes your design more consistent. You can update a color shade that’s used a lot, or you can add a new one just as easily.

Using Tailwind In Your Normal CSS

If you’re used to creating reusable CSS classes, you might be wondering how you can do that in Tailwind. Tailwind tends to push you to create a reusable component or partial in HTML instead of CSS classes, but sometimes you just need a simple button class.

If you want to add your own reusable classes, you can use Tailwind’s @apply function in your main CSS file:

1
2
3
.my-class {
  @apply font-body text-xl text-gray-900;
}

And then you’ll have my-class available for use. I would only use this for small components, like buttons or smaller. Even then, you may want to leave out the margin, padding, and width so you can customize it with Tailwind classes in different spots.

How Tailwind Keeps Your (CSS) Asset Sizes Small

Most likely, your CSS isn’t your largest asset. Still, every bit counts. Tailwind helps to keep your asset size small because you’re reusing the same helper classes over and over—not adding CSS for every new page. When you install and customize Tailwind for the first time, you’ll get all of the helper classes you will likely ever need. That’s it. You can build your whole app with that. Sure, you may customize it later, but it won’t add to the asset size in a meaningful way.

In development mode, Tailwind generates a very large CSS file. But when you’re ready to build for production, it uses tree-shaking to check which helper classes you’re actually using and cuts the rest.

In order to get Tailwind to check which classes you’re using, you need to tell it which files to look through in the purge section of tailwind.config.js:

1
2
3
4
5
6
7
8
9
// tailwind.config.js
module.exports = {
  purge: [
    './app/**/*.html',
    './app/**/*.css',
    './app/**/*.js'
  ],
  ...
}

Using purge took this site’s CSS file size from 3.2MB to 33KB uncompressed and unminimized. For reference, Bootstrap 5 is 193KB. The best part is that the size of this CSS won’t ever get much bigger, because I can simply reuse the classes I’ve already been using. That’s a huge deal for most websites.

Wrap Up

I hope this has given you a glimpse into why Tailwind is a great choice for your next project. If you want to learn more, you should read the official website and docs. Tailwind is a new way to style websites that might seem weird at first. It will take some time to get used to adding classes like mt-4 text-body pl-6 instead of writing regular CSS. But once you do, you’ll be more productive and less hindered by your CSS code in the long run.