Why Tailwind's utility-first approach is more than inline styles
Tags: tailwindcss, css, react, javascript
Published: 2021-03-22 18:54:33

We are using Tailwind CSS for our company's internal design system for over a year now and it is doing its job perfectly. Although most devs new to Tailwind are sceptical in the beginning, they embrace the setup after having written their first components with it, especially as it makes so much sense in a constrained environment like a design system.

In this post I want to explain my take on Tailwind's utility-first approach and what is doesn't have to do with inline styles…

## Historically styles were abstracted away into CSS classes...

Historically, CSS styles have been abstracted away into a CSS class in order to be easily used in the HTML markup:

```css
.my-component {
  width: 100%;
  padding: 1.25rem;
  border-radius: 9999px;
  background-color: black;
}

.my-component--text {
  color: white;
  font-weight: bold;
}
```

So whenever you wanted to use these styles in your components, you would simply add their classnames to their corresponding HTML elements like this:

```html
<div class="my-component">
  <strong class="my-component--text">Hello world</strong>
</div>
```

This especially makes sense as defining styles with inline style attributes would lead to unnecessary duplication, bad readability and poor maintainability of your components.

## ... but nowadays the abstraction is the component

With modern JavaScript frameworks and libraries like React, Vue, Angular or Svelte, there is another abstraction: the component.

The above example in React could be abstracted away like this:

```jsx
const MyComponent = ({ children }) => (
  <div class="my-component">
    <strong class="my-component--text">{children}</strong>
  </div>
);
```

So now, when using the component you don't need to think about its styles at all. Just write:

```jsx
<MyComponent>Hello world</MyComponent>
```

This approach led to a mind shift in using CSS. Suddenly, even inline-styles made sense again: There is no need to abstract away style definitions, as they will only be written once – within the component itself.

## The Tailwind approach

Tailwind provides many CSS classes that almost always do only one thing. These classes are referred to as utility classes. As their purpose is so limited they are perfectly suited to be reused in many places:

| Utility class | CSS Properties |
|---------------|----------------|
| `.w-full`     | `width: 100%;` |
| `.w-auto`     | `width: auto;` |
| `.mt-5`       | `margin-top: 1.25rem;` |
| `.mb-5`       | `margin-bottom: 1.25rem;` |
| ...           | ... |

***But this is just a fancy way of writing inline styles, isn't it?***

People keep comparing Tailwind utility-classes to inline styles and I certainly get their point. But there is a huge difference: Tailwind classes are constrained by what you define in Tailwind's configuration file:

In the configuration you will define your colors, spacings, fonts, sizes and so on that will be transformed into utility classes. This means that when sticking to Tailwind classes, you can't just write any style you like but you are constrained to what has been defined before. This is a huge difference.

In a design system you may think of the Tailwind's configuration as the "tokens" that are then used to create components.

The component above with Tailwind would look something like this:

```jsx
const MyComponentWithTailwindStyles = ({ text }) => (
  <div className="w-full p-5 rounded-full bg-black">
    <strong className="text-white font-bold">
      {text}
    </strong>
  </div>
);
```

Also check the Tailwind docs for a more comprehensive explanation: https://tailwindcss.com/docs/utility-first

What's your take on Tailwind?