Tailwind is a better abstraction

Many approaches to CSS have been tried over the years and Tailwind CSS is now extremely popular choice. Tailwind takes a controversial approach, which is closer to inline-styling than traditional stylesheet abstractions. Does tailwind's popularity suggest that inline styles are actually a better abstraction than stylesheets?

Are stylesheets an unnecessary layer?

Cheng Lou has a talk on the topic of abstractions in software, explaining that much of the pain in software development is a result of the cost of of traversing levels of abstractions within a software stack. Lou uses the example of traditional CSS in the layers of modern web applications as a problematic layer of abstraction. He argues that, typically, in a modern web application, logic in the Javascript layer provides adequate control over styling and layout. Mixing the DSL of CSS (i.e., selectors) muddles logic and creates a cost of switching between levels of CSS and JS abstraction.

Tailwind

Tailwind is essentially a thin abstraction over CSS properties. Elements are styled using a set short-hand classes (utility classes) which map almost 1:1 to CSS properties. Resulting in an interface which very close to setting style attributes of elements, with a few more advantages which the class interface afford.

Tailwind

This approach avoids the much of the CSS DSL (mostly complex selectors), makes inventing a stylesheet of classes and selectors unnecessary – which frees developers from inventing and maintaining an abstraction between markup and the styles applied to it. The popularity of this approach suggests, and as Lou pointed out in 2016, the stylesheet abstraction may not be optimal for web applications.

Constraining the bad

The usage of CSS selectors and classes is so fraught, that many popular approaches to CSS are patterns to avoid classes and selectors usage.

BEM methodology can be thought of as approach to avoid using CSS selectors. BEM constrains CSS to a single kind of selector: single classes with 1 level of specifity and has strict and limited rules on using multiple class names. These contraints avoid many of the pitfalls of CSS i.e., specificity wars, styling conflicts, inconsistent selectors

Styled components and other CSS-in-JS approaches recongnise that complex selectors which mapping styles to elements is unnecessary when mapping can be delegated to inside the declaration of components.

Tailwind takes contraints further, by removing the responsibility of creating and maintaining a separate abstraction layer of classes and selectors entirely. Instead, relying on the abstraction level of CSS properties themselves and interfacing directly with CSS properties with concise utility classes.

Separations of concerns?

Styles and content are separate, so they should be separated is a common argument for using classes and selectors. CSS and HTML are different technologies, so they should be separated.

There is a parallel here to when JSX was first introduced with React. Purists argued HTML, CSS and JS should be separated and that the way JSX integrated mark up and logic is a bad idea. JSX proponents argued that the ergonomic benefits of mixing Javascript and HTML were worth pursuing and separation of JS/HTML was just separation of technogies, not concerns. Many years later, JSX is more popular than ever and there a many trends towards tigher coupling and consolidating more technology into the surface area of a single file (e.g., RSC, Vue)

The same argument can be made for CSS and HTML. CSS and HTML are different technologies, but they are arguably not different concerns. You can say that HTML should be content and CSS should be styles, but in practice, the two are so tightly coupled in a web application that constructs complex UI, that this separation is not ergonomic.

I think the lesson here is patterns which are more ergonomic on a micro level and fit the limits of working memory (1 file is easier to deal with than 2) will win over patterns that favour more abstract macro technical concerns (separate files are cleaner). In 10 years, will the state of CSS look more or less like Tailwind? I would bet on more. Browser standards have a history of adopting patterns developed

Separation of technologies

Constructicons form Devastator, the most powerful robot.

Learning to stop worrying and love inline styles

The prevailing wisdom is that using inline styles are bad practice. To keep a codebase maintainable, styles should be carefully abstracted, kept in a dedicated CSS files and applied to elements using classes and selectors. With this structure, you can in theory avoid duplicating styles and keep styles consistent. Need to change the shade of blue used across the interface? you can change it in one place and it will be applied everywhere.

But to abstract styles, you must create a taxonomy of the styles, and as anyone who has maintained a large CSS codebase knows, categorising and naming enitities which make up a UI is very difficult in practice. It is all-too-common for abstractions to break down as the requirements of the UI change and for the taxonomy becomes muddled and confused. Developers, confronted with huge CSS files, resort to using `!important` and creating silos of styles to avoid conflicts or to understand the entirely of the taxonomy to avoid unintended side-effects.

Technically, Tailwind styles are not inline styles in the traditional sense of setting the `style` attribute of an element. But the practical usage is close to inline styling. The Tailwind design overcomes some limitations of inline styles:

  • Class short hand is more concise than inline styles properties
  • Pseudo class support
  • Responsive breakpoint support
  • Values are configurable with underlying variables
  • Opinionated default values and a style reset
Tailwind

Advantages of inline styles

  • A piece of markup is with inline-styles is self-contained – understandable without reference to external depedencies
  • Changes can be made unintended side-effects.
  • The overhead of creating, maintaining and considering a taxonomy of styles across an application is avoided (or unified with application code)
  • Developers unfamiliar to a project are unburdened with needing to learn a set of proprietary classes and selectors

UI Copypasta Distribution

A interesting trend to note which is enabled by inlining styles is the ability to copy and paste snippets of UI across projects. The popular reusable component project shadcn-ui even forgoes using a package manager to distrubition code (i.e, npm) and instead relies on copying and pasting code. The rationale here is that required interface for custom UI is so varying and complex, there little point trying to trying to provide and maintain a stable interface and it's more practical for consumers to copy and paste the code and modify it as needed.

LLM Future?

There are also implications for LLM-generated code. It's possible the future ergonmics of code will be shaped by output of LLMs. LLMs (currently at least), limited by their context window, are mostly suitable for understanding and generating relatively small amounts of code (i.e., single files of code rather than entire codebases). LLMs have limited working memory, (just as humans do), therefore self-contained code with inline stlyes are more immenable to LLMs. It is no wonder why many GPT-geneated HTML code snippets include 'tailwind' in their prompt

Caveats

Of course it depends. Decisions are always tradeoffs. Tailwind's approach may not always make sense.

App vs Document

The web was designed to display documents. Web application development has been shoehorned into web standards which were not anticpating the needs of a rich dynamic UI. If website resembles a series of documents rather than a rich and dyanmic UI, then the abstraction of a stylesheet may be more appropriate. We can continue to beenlightened by CSS Zen Garden

Your web application might leverage selectors

The power of CSS selectors are necessary for some applications. Many products may require customisation at scale and leveraging the power of CSS stylesheets can be an great solution for this. e.g., a CMS which allows users to customise the look and feel of their site. In this case, the abstraction of a stylesheet extremely useful.