When I started at Simple Thread earlier this year, I got brought on a new project where we decided on using Tailwind for writing CSS. I was initially hesitant about it since I very much like writing plain old CSS. I wrote a lot of custom CSS at my previous job using BEM (Block, Element, Modifier) and SCSS. It’s what I enjoy most about development and despite taking a dislike to BEM, I was still a bit leery about the idea of not writing CSS in my day-to-day.
I’ve been using Tailwind for about half a year now but it didn’t take long for me to warm up to it. I think in a lot of ways, Tailwind fits how I had started to think about coding and writing CSS.
Thoughts On BEM
My issue with BEM is people have their own interpretations on how to use it. I think this is because although its basic principles can be summed up concisely, it isn’t easily apparent how to handle special cases. Conventions vary which is fine working individually, but if team members have different ideas about it, it can turn CSS into a nightmare. The basic underlying principle and syntax behind BEM is very easy to understand and adhere to, but what happens when you run into any sort of situation that isn’t so plain and simple? Well I guess either you do it your own way or search for documentation.
Doing a quick web search brings up this official looking site that covers the entire methodology and could probably answer any question you might have about it. But that is a lot to read, remember, and have to refer to. Do we really need so many rules when it comes to writing CSS? It’s a lot of cognitive load for something that should be relatively straightforward.
BEM promises good things like structure and reusability, but in practice I found it also caused or amplified a lot of other issues that make CSS difficult to maintain.
Too Much Abstraction and Repeated CSS
With BEM, every block is meant to be portable, encapsulating all of the styles inside versus relying on the cascade of CSS to inherit them. This means every block, element, and variation of each needs to have its own abstraction resulting in a lot of repeated CSS. When exceptions and tweaks to designs are made, new abstractions are added and old ones are sometimes left behind resulting in stale, repetitive, and conflicting code. This can happen across separate stylesheets which further amplifies the problem of understanding and maintaining CSS.
Low Confidence in Updating Styles
Difficulty in understanding where styles are makes updating them scarier due to fear of breaking things. I’ve seen and had to write lots of iffy selectors and CSS which ends up compounding the problem. I’ve often had to do guesswork when looking at CSS, questioning things like: was it an issue of specificity? Is it targeting something dynamic that I just missed? Did something get removed, is it overwriting something from an external script or library, was it an oversight, or just some arbitrary selector?
How Tailwind Fixed These Issues
With Tailwind there’s a lot less room for developer preferences and styles. Utility classes are placed directly on the elements. This means there’s no guesswork when it comes to reading styles since it’s all there in the HTML structure which I have come to really appreciate. Updating styles is quick and easy and there are fewer places for things to go stale. Although there is a lot of repetition and the re-use of classes can make HTML look cluttered and messy, the removal of abstraction fixes a lot of the issues that I ran into with BEM.
Regarding the mess of class names, it takes some time to familiarize yourself with them but once up to speed, it’s easy to read. Tailwind also maintains an official Prettier plugin for sorting Tailwind classes which helps with tidying things up making them more easily scan-able.
Working With Tailwind
Generally there’s no need to write selectors and come up with custom class names. The few times where I’ve needed to write actual CSS is for complex selectors, overrides, or modifier styles. When writing SFC (single file components) in Vue, I use Tailwind classes inline for most of the base styles and use style tags for modified styles, overrides, and anything that’s either difficult to do or not possible with Tailwind. Class binding with Vue also makes it easy to write and differentiate alternate styles. The separation of concerns makes finding things easy and is something I’ve come to like.
As a side note, CUBE CSS is a newer methodology that embraces the cascade and is absolutely what I’d advocate using if we weren’t using a utility class-based approach with Tailwind. Every Layout was my introduction to this way of thinking.
I realize I have a lot of trouble separating my thoughts on Tailwind and BEM with my experience working in two different teams at two very different companies. Some of my gripes about BEM have less to do with BEM itself and more to do with the work culture and environment. BEM really suffers from having too many hands as a result of high turnover, bad (or no) onboarding, poor quality control and a lack of coordination or agreement on coding styles.
I think this comparison has also highlighted just how much more I’ve enjoyed working here at Simple Thread. As part of a tight-knit team here, I’ve always felt like I’m on a more equal footing with everyone else including our team lead. I’m comfortable engaging in discussions and sharing my thoughts on architecture, coding style and project structure which was not the case at my last place of employment especially as a junior developer.
Although I’m still not a fan of the abstraction and going against the cascade of CSS, I think BEM can work better but it seriously needs an environment where it can work well – more planning and quality control through code reviews and committing to an agreed-upon style guide or convention. The improved clarity that Tailwind provides is why I’ll probably advocate for using Tailwind (or a utility-class based) approach for team projects in the future.
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.