DEV Community

Discussion on: What are your thoughts on Tailwind CSS?

koresar profile image
Vasyl Boroviak

This is a deep write up.
Has to be an article.
Do you want to post it separately (as a post, not a comment) for further discussion?

Thread Thread
peerreynders profile image
peerreynders • Edited on

Not sure it's worthy of an article - right now it's more a collection of thoughts (and links to background information) that highlight some inherent characteristics of the web that a lot of people (and tools) just want to make "go away" (whether that's reasonable or not).

While I may have given a different perspective of what "separation of concerns" means in reference to web browsers that doesn't really put it into relation to Tailwind CSS. Adam Wathan actually does note in his article that "separation of concerns" isn't the core issue:

Instead, think about dependency direction.

He goes on to rationalize why it's OK for HTML to depend on CSS:

  • CSS that depends on HTML: HTML is restyleable, but your CSS is not reusable.
  • HTML that depends on CSS: CSS is reusable, but your HTML is not restyleable.

By emphasizing "reusable CSS" he justifies HTML that depends on CSS.

This can be judged as either "clever" or "very wrong".

"Clever" because "HTML that depends on CSS" gives permission to drop the loose coupling that approaches like BEM invest in and replace it with HTML to CSS tight coupling. While typically loose coupling is highly valued it does tend to create extra work - so if you invest in loose coupling where it doesn't show a return it's a pure waste. This tight coupling manifests itself in the style-based class naming that is used in the HTML. Ditching the indirection of a loosely coupled (meta) naming system is going to save work - "reusing CSS" serves as an additional incentive to embrace the HTML to CSS tight coupling.

"Very Wrong" because of the fundamental notion of "Visual Design is an enhancement" (see previous comment). HTML and CSS are often described as being co-dependent - largely because it is sometimes necessary to add elements and class names to markup to expose additional binding sites for styling (though divitis and classitis describes an overuse of that practice).

In my view the reliance of CSS on selectors tightly couples CSS to HTML structure. CSS declarations with selectors that don't bind to any HTML are essentially dead code - this is why Tailwind CSS has to use PurgeCSS to eliminate dead CSS.

  • CSS is tightly coupled to HTML due to CSS selectors binding to the HTML markup structure
  • HTML is tightly coupled to CSS due to style-based class names

Typically bidirectional tight coupling is only a good thing if both ends are part of one cohesive whole - but:

  1. Content is the foundation
  2. Markup is an enhancement
  3. Visual Design is an enhancement

i.e. while visual design builds on markup there is a distinct separation between both which is why people keep bringing up "separation of concerns".

Approaches like BEM accept the inherent CSS to HTML tight coupling and invest in HTML to CSS loose coupling (via naming that isn't style specific).
Giving Tailwind CSS the benefit of the doubt, it tries to weaken the inherent CSS to HTML coupling by supplying a library of predefined selectors (lessening the need to work in style sheets) in order to exploit the perceived benefits of HTML to CSS tight coupling.

At the very least it should be acknowledged that Tailwind's approach goes against the grain of CSS.

In this context HTML, CSS, and Dependency Direction makes an interesting observation:

we don’t have any long-term data to tell what difference HTML ← CSS (HTML depending on CSS) and CSS ← HTML (CSS depending on HTML) make.

The Tailwind CSS community reports that editing HTML is more productive than CSS.

But I've also observed that individuals who like CSS often don't see the need for Tailwind CSS (e.g. Two cheers for Tailwind, Why I Haven't Jumped on the Tailwind CSS Bandwagon). This gives rise to the hypothesis that perhaps Tailwind CSS adopters feel more productive editing HTML because they are more comfortable editing HTML (i.e. they don't feel comfortable with the way CSS works).

In my view the preference to edit HTML can be problematic. In a comment to her talk "In Defense of Utility-First CSS"" Sarah Dayan states:

If you need to change black titles to red titles, you need a new utility class for red text, and use it in place of the former. Depending on your project, this may either require a find/replace in your codebase, or a single change in a template component (instead of in a CSS abstraction).

??? To me this sounds akin to endorsing changing a JS constant name from const TITLE_BLACK = '#000000'; to const TITLE_RED = '#FF0000'; rather than using const TITLE_COLOR = '#000000';to begin with. This is where the indirection of names that are not based on style values pays off. Furthermore the change from black to red may affect titles in more than one template component but not all black titles in all template components. It doesn't always make sense to capture every styling abstraction above the level of a utility as a template component - like capturing a particular style of title (for the sake of consistency). Also not every useful aggregation of styles warrants a separate template component - aggregate style boundaries can exist within template component boundaries. There is also an overemphasis on reuse and elimination of duplication. Abstractions sometimes simply identify meaningful boundaries even if they they only occur in one single place (while duplication isn't always a sign that there is an (or just one) abstraction waiting to be uncovered). I wouldn't classify the same title style in multiple components as a case of reuse but as a demarcation of a common aspect across various template components.

Back to Adam Wathan's 2017 article - he really zeros in on "CSS reuse" citing About HTML semantics and front-end architecture (2012) as the inspiration for coming to the conclusion:

The more a component does, or the more specific a component is, the harder it is to reuse.

Again "reuse" (rather than demarcation of meaningful boundaries) is being cited as the all important objective. This is the justification for using single property/value utilities as the basic building blocks (pre-built "lego blocks").

In my view that isn't the primary reuse mechanism that the design of CSS had in mind.

In 2016 Harry Roberts published his ITCSS architecture (for one current implementation see ITCSS essentially lays out a strategy for wrangling CSS in a way that plays to the strengths of CSS. "Reuse" in the large is accomplished through global styles with a wide reach while narrow reach overrides are responsible for local styling; reuse in the small is largely left to mixins (see also mixin, BEMIT and CSS guide).

Aside: CSS Is Certainly Global And That’s Exactly The Point (2020)

There isn't anything that I can see in Tailwind CSS that encourages "reuse - the CSS way" (it's not actively discouraged either but exploiting it requires an understanding of how CSS works). Template components may repeatedly apply identical properties which really should be candidates for promotion to a "more global" reach via the cascade; leaving this type of duplication in place creates the opportunity for the "look and feel" of the components to "fall out of sync" when changes are only applied to some of them. Always maintaining a style guide could go a long way towards detecting these type of problems early.

In my judgement these type of problems result from the bottom-up approach to styling that is commonly found in the CSS-in-JS type approaches of component design styling. CSS itself is more of a top-down technology - set the overall global environment then override locally only when absolutely necessary. ITCSS coordinates between the top-down nature of CSS and the more local nature of components.

Recently Andy Bell introduced his CUBE CSS methodology which uses utilities but also incorporates some of the ITCSS insights. The first thing to note is that CUBE blends the top-down nature of CSS with the bottom-up approach of utilities - given that composition comes first, top-down gets the first pass. This mirrors "designing the system" before "implementing the components" in software development. The difference being that composition isn't just responsible for identifying the gross layout but also the global styles (i.e. establishing the overall "environment" of the page). And while a utility (generated by Gorko) can be just a rule with a single property/value, it isn't constrained to that. What is important is that it "does one job and does that job well" which can take multiple CSS properties
(aside: Anybody thinking "Single Responsibility Principle"? - that refers to: "Gather together those things that change for the same reason, and separate those things that change for different reasons." - "same reason to change" isn't the same as "single responsibility"; another case of bad naming).
Also "blocks" still have a role to play, whether or not they coincide with the boundaries of behavioural components doesn't matter.

So again I think that Tailwind CSS's definition of "reuse" deviates from the intent behind CSS.

Ran across this interesting tweet:

Confession: The apply feature in Tailwind basically only exists to trick people who are put off by long lists of classes into trying the framework.
You should almost never use it 😬
Reuse your utility-littered HTML instead.

... and further down:

I think buttons are still a good use case for custom classes in a lot of projects
I’m really more just bitter about all of the complexity and bugs apply leads to in Tailwind itself, wish we would have just offered the theme helper instead which is bullet proof.

My only conclusion here is "there is no getting away from knowing the fundamentals" - but that was an open secret even in the Bootstrap days; people just chose to ignore it.

Another observation:

Tailwind CSS users! Tell me what you like about Tailwind.

A point that keeps coming up:

easier to author by non css people

Now it's great to have such an empowering tool and Tailwind CSS operates at a lower level than Bootstrap - but from I can tell using Tailwind CSS doesn't contribute to understanding what makes CSS tick.

Frameworks have the potential to inhibit a deeper understanding of the things they abstract, which is the web platform.

radEventListener: a Tale of Client-side Framework Performance (2020)

This is a recurring theme with lots of popular front end tools - jQuery devalued JavaScript skills, React devalues Browser Web API skills, Bootstrap devalues CSS skills etc., etc., etc.

Aside: Tools don’t solve the web’s problems, they ARE the problem (2015)

From testimonials it's clear that Tailwind CSS:

  • is popular
  • has a number of short term benefits
  • is deemed useful over a wide range of use cases by those who have chosen to adopt it

As a result Tailwind CSS seems to be evangelized heavily but there seems to be a lack of clarity regarding applicability, deliberate tradeoffs, objective caveats, and potential long term (undesirable) consequences.

Thread Thread
koresar profile image
Vasyl Boroviak

Mate. Create a post from this. Call it "just raw thoughts" or something. But it should not be lost in comments section here.
Well written stuff!