Hot take: all CSS frameworks are bad

I had a long (civil) argument today with a coworker about using Tailwind in React. Basically, he thinks it's better because it's reusable, and I think it's a maintenance nightmare. He was so adamant about using it, though, that it became quite a big deal by the end of the day. So, thanks to that, it inspired this post. Buckle in, I'm gonna break down everything wrong with CSS frameworks. Not just Tailwind, but Foundation, Bootstrap, etc. Any of them, because the very philosophy they all have in common is flawed.
--
According to w3c recommendations, we should keep content concerns separate from presentation concerns.
It's a simple concept, really. HTML is content, CSS is style. Although the famous quote from Adam Wathan may be technically interpreted as true, I think it's attacking the wrong issue.
When you think about the relationship between HTML and CSS in terms of "separation of concerns", it's very black and white.
You either have separation of concerns (good!), or you don’t (bad!). This is not the right way to think about HTML and CSS.
Instead, think about dependency direction. There are two ways you can write HTML and CSS:
CSS that depends on HTML … In this model, your HTML is restyleable, but your CSS is not reusable.
HTML that depends on CSS … In this model, your CSS is reusable, but your HTML is not restyleable.
This is addressing the issue of DRY code, which I'll come back to in a minute. This is NOT addressing the issue of what is responsible for which concerns.
It's a misdirection to start talking about reusability when that's not the issue in question, let's take on only one issue at a time. HTML, by definition, is content. CSS, by definition, is style. Keep these two buckets nice and organized, don't let your meat touch your potatoes.
"But you have to choose whether to keep HTML or CSS reusable, you can't have both!!"
Well, actually, yes you can.
Assuming you're using one of the major libraries of modern times, this issue goes away with a little component composition.
So you have two components that are very similar, going with the example given by Wathan: .author-bio and .article-preview, which he proposes adding a class that can be shared by both and still make sense, .media-card. He then descends down the rabbit hole talking about all the issues with this approach, but back up, there's something fundamentally wrong here.
Let's treat these as actual components, instead of just BEM blocks. You would still keep <AuthorBio> and <ArticlePreview>, but since they have common structures, they would both make use of the <MediaCard> component. That's what composition is. This approach leaves no issues behind, so there's no point in joining Wathan in the abyss that follows. If something looks slightly different between the two, you can just tweak it from each of the more specific components.
for example, in React:
const ArticlePreview = ({children}) =>
<MediaCard className={css.articlePreview}>{children}</MediaCard>
The reusability of all the markup in <MediaCard> is preserved, while also providing the fine-tuned control of the more specific uses of it in <ArticlePreview> and <AuthorBio>. Moving on...
--
Non-semantic class names greatly interfere with the readability of the templates.
It should be a no-brainer that <nav className={css.nav}> is more readable than, say, <nav className={[css.f6, css.br3, css.ph3, css.pv2, css.white, css.bgPurple, css.hoverBgLightPurple].join()}>.
But maybe that's not enough to convince you, maybe you think it's nice to see how it should look with a quick glance rather than looking at a different file.
To which I say, first of all, these class names are not even descriptive of the visual effect they have either. In order to be aware of whatever br3 is supposed to mean, you would have to be fluent in this particular CSS framework before you could even read this.
My main criticism, though, is that this is akin to writing all your CSS rules on a single line. You could potentially write a multiline class name in the markup, but at that point, why would you do that? It certainly doesn't provide any benefit over just simply writing out the style rules in CSS. The abstraction exists to make things easier, so to treat it like a chore seems really strange to me.
--
Too many nested divs add unnecessary complexity to the templates.
Container, Row, Col, Col, Row, Col, Col, Col... Sound familiar? Yeah, it's not fun to have to deal with that when all you want is to find the deeply nested title you're looking for to capitalize a letter.
This is such a painful thing to see, especially since we now have all these new features in CSS3 to play with, like grid and flexbox. Tab Atkins must feel like a joke watching these frameworks pop up. These features are not that hard to use, and they can work wonders in simplifying your HTML structure.
There's no reason to consolidate a grid into one class either, because that's what the values in your CSS properties are supposed to be used for. Why overcomplicate that?
If having standardized spacing is your concern, you can just as easily use CSS custom properties to help out with that, or SCSS or LESS variables if that's your thing. Plus, those values could then be used across multiple properties instead of just the one dictated by the class.
There's simply no benefit these repetitive classes in HTML can give to you that flexbox/grid cannot.
--
Predefined styles are often more difficult to customize and override.
I cannot tell you how many times I got frustrated trying to customize stuff that was being thrown off due to some globally inherited Bootstrap style. Sometimes it's hard to overwrite these styles because they wrote such specific selectors that you can't work around it without the blasphemous !important. Now, granted, it's been several versions since I last used Bootstrap, but the point remains - when you use generic classes and you have to override them to make small tweaks, it can be a real pain.
Why sacrifice control over the CSS when you know the client is going to want you to make a hundred small tweaks? Save your future self hours of fighting against the CSS! Everyone can relate to how unpleasant that can be.
--
For maintainability reasons, you should always try to keep your layers swappable and modular. By coupling them together, you create a larger surface area for impact and risk management.
There are several factors with this one. Let's start with risk management. Assume you're using React, and someone asked you to make a button blue. Using Tailwind's approach, you might add bg-blue to your markup. Now let's say you make a typo and accidentally write a > before the end of your HTML tag. Now, what was supposed to be a style change has caused your HTML to render with all kinds of incoherent junk on the page. It wouldn't be the end of the world if the button wasn't blue, but messing up the page content was an unforeseen issue.
Obviously, this is pretty contrived, this example is not the worst problem ever, but the point I'm making is that by keeping style in CSS, you narrow the impact of your changes to just the CSS. If your company has a change management team, they'll be much more lenient about allowing a stylesheet hotfix at the last minute.
The next factor is concerning swappable parts. What if you want to drop Bootstrap later on? Maybe your team found out about Tailwind and wants to replace Bootstrap? Well, having all those classes in the markup is going to make that a really painstaking process. When you keep the styles separate, suddenly it's just a matter of swapping out the CSS file, instead of surgically removing or altering every last instance where you repeated the class names in each template.
--
Adding a dependency just creates an unnecessary layer of complexity to your project.
Let's just get down to brass tax here.
You can compose reusable components so that you never have to copy/paste CSS.
Writing repetitive class names in the HTML is not reducing repetition, it's just moving it from the CSS to the HTML.
As opposed to divs for rows and columns, flexbox and grid actually simplify the layout process.
More control means simpler work and faster delivery, with the added bonus of less frustration, and less of a learning curve.
Ultimately, a CSS framework does not serve to simplify your project or your workflow. It serves to complicate it. If you believe it simplifies your process, then perhaps your process is too complex to begin with. Having built several websites both with and without a framework, I can personally attest that my process without them is much smoother, less frustrating, faster, easier to make updates later, and easier to work with in a team setting.
Lastly, if you think a team is better off using generic classes to avoid inconsistency, I'll leave my sentiments to be expressed by the more eloquent Jeffery Zeldman...
I don’t believe the problem is the principle of semantic markup or the cascade in CSS. I believe the problem is a dozen people working on something without talking to each other.
TL;DR:
Defining your own CSS is easier to write and maintain. Frameworks offer you nothing that CSS doesn't already offer you.
Advertising by Adpathway




