Skip to content

Obsessed with Tailwind CSS

Published on April 28, 2023

About a year ago, I introduced Webflow to a team of project managers. I was the only developer at this agency and my capacity was quickly being filled up. There was a healthy demand for client websites and we knew eventually, I would not be able to keep up.

We devised a system that allowed non developers to contribute to web builds, specifically getting a very basic wireframe set up that allowed content to be filled before I stepped in to add the styles. Doing this with Webflow was painless. Because the team was familiar with the Adobe Suite, onboarding was smooth. We had tried using UI libraries but learned quickly it did not mesh with our development process. Even the simple UI libraries were packed with classes and styles that were eventually edited or removed completely. While it did save us time, it wasn’t perfect.

Fast forward some time and I found that my capacity was close to it’s limit. I recruited a junior developer that was still learning web development to help address our capacity issues. While pairing for a web project, something came up that reminded me of one of my biggest hurdles when I started learning code–what do I name things? How should I name things? Specifically, CSS class names. There’s just going to be so many of them.

Most often when training or teaching a younger developer CSS, naming or labeling things would cause friction. Even now, after more than a decade of writing CSS, I’ll catch myself saying “…uhhhh” with a pause, before I end up with something arbitrary like “.card-grid-wrapper”. Wow! Pro coder move there.

At some point in my career, I landed on the BEM naming conventions. This was around the time I adopted Sass into my workflow. It allowed me to think about what something was and what my CSS was actually going to be doing.

Here’s a simple card component with BEM.

<div class="card">
  <h3 class="card__title">Card Title</h3>
  <p class="card__description">Card Description Here.</p>
  <a class="card__link card__link--danger">Danger</a>
  <a class="card__link card__link--confirmation">Confirm</a>
</div>

When reading through the markup, I felt it made just enough sense for me to adopt it full time. What’s the block? “Card”, what are the elements inside? “Titles, descriptions, links”, and finally, what needs to be modified, “Link type”.

That’s all fine and good, but I always felt that the compromise was how long markup could get. And I’m still needing to think about the element to name it something. Did I mention I once had a cat named “Kitty”?

One thing I did enjoy was how the SCSS became this compact thing, and CSS specificity rarely became an issue.

.card {
  padding: 12px;
  &__title {
    font-size: 32px;
  }
  &__description {
    font-size: 16px;
  }
  &__link {
    text-decoration: none;
    &:hover {
      text-decoration: underline;
    }
    &--danger {
      color: red;
    }
    &--confirmation {
      color: green;
    }
  }
}

I liked the idea of this approach because the HTML and CSS inherently paired together. To the point that making these components reusable was a huge value to speed up production. The theory was being able to have a library of basic UI components that could be reusable. A dream for efficiency.

I enjoy thinking about these kinds of problems and then discovering potential solutions. Living with the solution for some time can help unearth more problems and then the vicious cycle repeats. Unfortunately, being a working developer means less desire to solve these problems and more desire to get the actual work done.

So I lived with BEM for some time. In the back of my mind, I wasn’t fully content with this solution. The CSS portion clicked with me, but the HTML side left a lot to be desired. When pairing with the junior developer I mentioned earlier, they said something while going over a piece of code that blew the doors off. “That’s a lot of typing.”

<h2 class="section-title section-title__inside-article section-title__inside-article--bold section-title__inside-article--has-decoration">Subscribe to our newsletter</h2>

I fell into a trap. So long, I’d been coding within the constraint of a naming convention. I did casually solve the problem of wondering what to name things, but at the cost of being very verbose. I try my best to write code that wouldn’t need additional comments and being clear with naming was a solution that I never revisited. I can type around 120 words per minute, and about 80 when coding. This junior, however, was still getting their bearings with symbols and all the other weird coding syntax that doesn’t really come naturally, additionally, they were still learning good touch typing habits. I don’t like this hurdle. An amazing person, a strong passion for code, and a great mind for solving programming problems was being held back by typing. I knew their education and progression was being slowed down because of the context switching. In the middle of solving problems mentally and now they are solving the typing good problem. Momentum would grind to a crawl. I knew that their typing would improve eventually, but I felt that the solution of BEM and being verbose stalled the bigger problem which was the actual coding.

We spoke for some time on theory. The whys and the relationships, and most important, the whats. What is something for? The idea I shared was “code can be anything” but it’s important to share the language. When language is shared, collaboration is easier, alignment is simpler to grasp, and the road to mastery gets paved a little bit better. You don’t have to name something a certain way but there are design systems that allow maintainability and communication (with yourself and others), to be more simple. Especially if future you has to deal with past you. Past me is kind of inconsiderate of present me to be honest.

Speaking of past me, we revisited the code and I showed them how I would have wrote this code in the past:

<h2 class="newsletter-title">Subscribe to our newsletter</h2>

We’d jam all the CSS into the singular class “.newsletter-title“.

It worked just the same, but the compromise was how specific the class was. We’d be using this class for at least 20 other elements throughout the site. So we vague’d it up.

<h2 class="fancy-section-title">Subscribe to our newsletter</h2>

This allowed the class to be more flexible. As we were talking through why this is acceptable, we also talked about the shortcomings to keep in mind of and how to solve them. Interestingly, it brought us to a very important concept. Imperative vs Declarative programming. We discussed why we preferred declarative programming, especially when we worked in React. So this was the friction with BEM, and naming in general I thought.

Using BEM, each class straddled between imperative and declarative paradigms–mainly because our patterns weren’t consistent. There wasn’t a good fence to keep us wrangled in, so we’d stray at times. “fancy-section-title” felt declarative–This is what I want. Alternatively, “section-title section-title__inside-article section-title__inside-article–bold section-title__inside-article–has-decoration” felt imperative. Here’s a list of ingredients to make up a “Fancy section title”. On top of that, typing “section-title” ten times doesn’t sound efficient. Also it’s not pretty, it’s actually kind of gross if I’m being honest.

Okay, so that was a long back story. I thought this post was called “Obsessed with Tailwind CSS” and all I’ve been doing is talking about how much I dislike naming things. The long and short is, I finally looked into Tailwind CSS seriously. I had dismissed it when I first learned about it because I mainly worked on WordPress sites and implementing Tailwind with WordPress felt like a chore and my capacity was always close to full. With an additional developer to help, I had the opportunity to dive in. The community was also hyping it up like it was the best thing to happen to front end. I committed to using it on a client project. With room to breathe, I didn’t see any issues.

Tailwind’s WordPress support is a lot better now so I started using _tw. I’ve used _s (underscores) as our base theme for a majority of client WordPress sites so it felt like a natural transition. I’d also been looking to sunset _s as it was no longer being maintained. New paradigms with WordPress were also coming out like ACF Blocks, Gutenberg, Full Site Editing etc. I knew _s would not be the best option.

So here’s the thing. Tailwind isn’t something new, or even revolutionary on paper. It’s a library of utility classes. Just a bunch of classes that do things. You know, what the devs are here for. Writing the code that does the thing. But something magical happened when I went over it with the junior dev, they talked through their code as they were writing it, declaratively. Something that didn’t happen much before because they were busy typing and thinking, and of course, context switching.

Imagine with me while coding this:

<div class="card">
  <h3 class="card-title">Title</h3>
  <p class="card-description">Description</p>
  <a class="cancel-link">Cancel</a>
  <a class="confirm-link">Confirm</a>
</div>
.card {  
  //styles
}
.card-title {
  //styles
}
.card-description {
  //styles
}
.cancel-link {
  //styles
}
.confirm-link {
  //styles
}

This is how my thought process would go writing the above code:

  1. Okay, we need a card
  2. The card has a title
  3. The card has a description
  4. The card should have a cancel link
  5. The card should have a confirm link
  6. Okay, now we need to style this card
  7. The card itself has x styles
  8. The title should have these styles
  9. The description should have these other styles, but it shares some code with the title
  10. The cancel link should have these styles
  11. The confirm link should have these other styles but shares some of cancel’s styles

Yes, that goes through my head when I code, that’s only an excerpt too. I want to focus on a few things. One, this feels incredibly imperative styled thinking to me, I think mainly because it feels like a recipe for a component. While HTML and CSS is declarative, CSS classes can feel imperative at times. And writing CSS can also feel imperative. Give me all the ingredients to make a component, and then write the ingredients into a recipe that gives us the component. When my colleague was talking out loud while coding, the lightbulb appeared. Steps 1-5 is HTML, steps 6-11 is CSS. We typed what we wanted in HTML (declaratively) and then when we switched to CSS, and we started thinking imperatively–how are these elements being styled. Sure the CSS is declarative, but the mindset did not align.

Let’s look at a Tailwind’ed component and I’ll explain my thought process.

<h2 class="pb-4 text-red-500 font-bold underline">Section Headline</h2>
  1. I need a section headline, it has bottom padding of 16px, it’s red, it’s font is bold, and it’s underlined. The text should read “Section Headline”

There’s my brain talking through that element in 1 step. At the end of the day, it saves one bit of context switching (HTML <-> CSS). And it keeps focus on the element as it’s being declared in the markup.

Here’s the thing. I yearn for immediate gratification as a developer. I like to type things and have it appear in front of my eyes. I love talking about theory, but friction-less efficiency? Now that’s an addiction. Using Tailwind as a tool is one thing, but it’s really opening my eyes to how I code and how I prefer to code. Learning Tailwind has been smooth. The classes are named as such that compliments CSS properties and Tailwind’s documentation is fast and clear to travel through. I didn’t even mention how it only writes the utilities that you use when it compiles. The features are great and I’m still unpacking all the features.

I’m obsessed with Tailwind not because of what it is, but what it’s doing to me. Not just because I have to write less code, but it’s reminding me to focus on the solution, not how to arrive at the solution. It’s allowing shared language to become a bit more aligned, and reading someone else’s Tailwind code is the same as reading my own.

One last thing with Tailwind I’m only starting to play with. And that’s using their @apply directive. The first issue that came up with Tailwind was that I could see writing so many utility classes in the markup could muddy the file. I’ve seen divs with 5 lines of classes. It can get ugly. Of course these smarter people came up with a great tool. If an element becomes too unwieldy, that’s when I can go back and name a new class and use @apply.

<div class="max-w-full p-4 grid grid-cols-3 md:grid-cols-6 gap-4 md:gap-6 mb-8 border-2 border-sky-500 rounded">
 <!-- more elements -->
</div> 

That can now become:

<div class="custom-class">
  <!-- more elements -->
</div>

And the CSS:

.custom-class {
  @apply max-w-full p-4 grid grid-cols-3 md:grid-cols-6 gap-4 md:gap-6 mb-8 border-2 border-sky-500 rounded;
}

I think the beauty of this is it becomes part of the evolution of your code seamlessly. If I find myself adding Tailwind too often on a component that I know will be re-used, that’s when I’ll name a class. And if it happens after the fact, copy/pasting the utility classes is a much more straightforward process than say multiple classes that need to be consolidated.

Tailwind reminded me that I thrive when I’m learning and I had fallen into a complacency trap. Being set in your ways as a developer is not a good way to become better. There will always be something better though, and sometimes that thing arrives and reminds you that you don’t know as much as you think you do.