DEV Community

Discussion on: What are your thoughts on Tailwind CSS?

koresar profile image
Vasyl Boroviak

Separation of concerns is not separation by technology.
Concern is a synonym to feature, or page, or component.
See Wikipedia.

Thread Thread
peerreynders profile image
peerreynders • Edited on

See Wikipedia.

"A concern can be as general as the details of database interaction or as specific as performing a primitive calculation, depending on the level of conversation between developers and the program being discussed. IBM uses the term concern space to describe the sectioning of conceptual information."

Separation of concerns is not separation by technology.

The separation was never about "technology" (or file types, though unfortunately it's an extremely common misconception even in the Vue documentation). The separation comes from the web browser's approach to fault tolerance and resilience. That approach ultimately lead to the practice of progressive enhancement.

Aside: The Resilient Web, Chapter 5: Layers

As Aaron Gustafson put it:

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

(And then SPAs (with native-envy; initially targeting desktop) made it all about JavaScript)

So in terms of concerns

  1. Structured Content (HTML)
  2. Visual Design (CSS)
  3. Interactivity (JavaScript)

The technologies are simply a consequence of the intentional layering of making content the top priority.

And if you look at OOCSS you'll find that even within CSS there are multiple concerns - for example: separate structure and skin - i.e. while a module/block should have autonomy over it's structure, the definition of it's skin should be separate (otherwise theming the module in accordance to the page/site as a whole becomes impossible).

Modern JS frameworks tend ignore this separation because they are all about JavaScript. To this day browsers still process HTML, CSS (layout), and JavaScript (behaviour) separately and a lot of the performance strategies focus on preventing JavaScript from blocking the other two layers from doing their job.

  • JSX isn't markup. It's a domain specific language with an XML-style syntax which only exists to generate JavaScript.
  • Similarly the template in Vue's single file component simply exists to generate JavaScript. At least the CSS styles can be extracted separately.

So none of these technologies conform to the "browser layers" because everything is just collapsed into the JavaScript layer. That is why SSR has to be used to get some of the performance benefits of "the layers" back.

Some argue that it's time to build the "application web" but the current layering acknowledges that the network is never going to be perfect. For the "document web" the tradeoffs are clear - content is king while visual design and interactivity are expendable. What are the tradeoffs that the "application web" can make when things start going south? Many "modern web apps" will only work under near perfect conditions, don't degrade gracefully, much less have their own vision of what progressive enhancement should look like.

Douglas Crockford wrote in 2008: "JavaScript is most despised because it isn’t some other language. If you are good in some other language and you have to program in an environment that only supports JavaScript, then you are forced to use JavaScript, and that is annoying."

These days "the web" is similarly despised. Seems many want to develop for the web as if they're developing a desktop application or at least some native application. That ignores that the web by its nature is distributed - there is no abstracting that away. And while native apps may use the network they are typically much less constrained by it. The browser's ServiceWorker API tries to make "the network an enhancement" by creating the opportunity to cache all essential parts of a web site for offline use but in general browser-based apps have to operate under more severe constraints than native apps.

Aside: Web vs. native redux (2015)

Another issue is that while the web strives to be a universal platform it isn't one platform - which is why people coming from (or wishing for) a more monolithic environment judge many web technologies (including CSS) as weird.

Aside: Front end and back end (2015)
Aside: Web development as a hack of hacks (2016)

CSS seems especially weird to software developers as visual design composes very differently from software. Object orientation values encapsulation, functional programming values functional purity and immutability. In contrast a "visual component" has to maintain its structural integrity while also having to adapt to its surroundings in order to become a "harmonious part of the visual whole" - so defining styles to compose visually is very different from composing software behaviours.

Aside: Don't QWOP Your Way Through CSS (2017)
Aside: You'd be better at css if you knew how it worked (2019)

Point being: The familiarity heuristic will lead you to favour tools aligned with your current mindset. However when the domain requires a significant shift in thinking (e.g. from software development to visual design, styling) then a shift in mindset is required to become truly effective (rather than subjectively productive; many paths to "apparent productivity" can compromise quality) - no tool can replace that necessary shift in mindset.

Perhaps that may explain statements like:
"I don’t think it’s a coincidence that back-end developers have written most functional CSS libraries."

Other more design-minded opinions seem to range from "it's useful for prototyping" (not for the final product) to "if it works for you, why fix it". But that's hardy a universal endorsement and doesn't explain the "best thing since sliced bread" popularity. That is explained by:

  • Editor support for choosing utility classes
  • Dispensing with (ignoring?) the need to identify appropriate abstractions and names (don't make me think)
  • Leading to a sense on increased productivity

I'm still skeptical that this approach is maintainable in the long run as style-centric classnames convey no intent behind (or provide any meaningful organization of) the selected property values - just because the values are right in front of you doesn't explain what particular aspect of the visual design/component they support.

Thread Thread
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!