What Tailwind taught me about the separation of concerns
Published onThere are many heated debates going on in the world of frontend, and while some of them eventually land on their feet, other ones keep on hovering for a long time because there are many highly respected members of the community on both sides. In this post I would like to talk about the one that arise from topics like CSS-in-JS and atomic CSS—separation of concerns.
I started my career as a CSS developer. I loved bringing designs to life on the web, and I wanted to become so good at it that I could eventually design in the browser. However, before I was able to reach that degree of CSS agility, JavaScript caught my attention, and eventually I became more interesting in frontend tooling, but I still follow news about CSS and make excuses to write it.
I always wanted to learn how to write CSS “correctly”, so in my static sites and web applications I was eagerly trying out various tools and techniques: sorting declarations, using naming methodologies like SMACSS, CSS Modules, exploring CSS-in-JS, etc. These helped me deal with some common problems, like naming classes and changing styles based on state, but no matter how hard I tried to write manageable CSS, I was never truly happy with my code.
Then at a local CSS meetup I found out about Tailwind CSS, a utility-first CSS framework. The first interesting aspect of that presentation was that it was given by a backend developer (my evil twin brother), which made Tailwind additionally intriguing to me, so I decided to give it a shot right away.
At the beginning, managing so many classes was weird, to say the least, but after I got used to it I started liking it more and more. However, I didn’t understand why exactly I liked it so much until I came across Sarah Dayan’s talk “In Defense of Utility-First CSS”, and then it all clicked—I liked the inversion of dependency direction between HTML and CSS. I was taught to write HTML that tries to be agnostic of how it’s being styled, which causes CSS to depend on HTML, but utility-first CSS flips that relationship around. I highly recommend watching the talk to see how that looks and understand the implications, it provides a great background to this post.
For a long time I believed that we should always strive towards separation of concerns (SoC), but didn’t we already break that principle with JSX? It appears that SoC between HTML and CSS is more sacred, so I was holding on to it as well, but the hardest part about that was adding HTML elements like <div class="navigation-inner">
. I was never able to explain to myself how that’s not breaking SoC; it’s an element that exists purely so that it can be styled, the semantic-sounding name is there only to maintain the illusion. I would be able to endure it if it was a rare occurrence, but it’s such a common practice that it was really hard to ignore. With Tailwind I can finally stop trying to be clever and just style it directly, e.g. <div class="px-2 flex items-center">
, this way I’m no longer pretending that my HTML structure doesn’t depend on CSS.
Another aspect of the SoC principle is that it results in CSS that keeps growing. This is nothing new, Thierry Koblentz, who coined the term “atomic CSS”, recognized this in 2013 in his article Challenging CSS Best Practices, so why is SoC still a thing? One site that is continuously being flashed when advocating for this principle is CSS Zen Garden, and its unquestionable beauty is very effective in maintaining this recommendation.
“CSS Zen Garden is a technical demonstration, not a best practice.”
When I learned that CSS that follows SoC inherently keeps growing, I was wondering why exactly. One time I joined a conversation on Twitter, and Adam Wathan, the creator of Tailwind, explained why. It took me a while to wrap my head around what he meant, but then it clicked—I always considered rules to be a unique sets of declarations that don’t need to be DRY, but that, combined with all the CSS I’m afraid to touch, is precisely why my CSS kept growing.
One recent example where I noticed the prevalence of SoC is a CSS-in-JS situation with styled-components
. I like to occasionally use the css
helper function to add some styles instead of having to create a separate component every time:
<div
css={css`
display: flex;
align-items: center;
`}
>
{/* ... */}
</div>
However, the CSS-in-JS community visibly discourages this, claiming that it’s mixing concerns. The preferred way seems to be to create a separate styled component, meaning that moving these styles 50-ish lines upward would have satisfied the SoC principle. I’ve also noticed that some people like to keep styled components in separate files, so the definition of SoC varies from person to person.
The reason why Tailwind sits so well with me is because now I can read the styles from the HTML. With SoC I had to look at HTML and CSS files in parallel, I never realized how exhausting that was!
Conclusion
Tailwind helped me realize that, despite being so prevalent, SoC is just an approach. It claims that CSS describes the style of the page, while HTML describes the structure, but CSS is relying on the details of HTML’s structure, so HTML is actually fulfilling two purposes at the same time. That’s why I believe that keeping them together is a much better idea.
Resources
- On Auto-Generated Atomic CSS
- Why you’ll probably regret using Tailwind
- Why Tailwind Isn’t for Me
- Color Theming with CSS Custom Properties and Tailwind
- Tailwind versus BEM
- Why I Love Tailwind
- Why Tailwind CSS
- The Shorthand-Longhand Problem in Atomic CSS
- A Real-Life Journey Into the Opinionated World of “Utility-First” CSS
- In Defense of Utility-First CSS