In this article I discuss
Why TailwindCSS is a turnkey design system that is easy to extend and how the combination of a base set of styles plus those tweaked for my app is why I adopted Tailwind.
How the use of component based design, web components, and front-end style guide mitigates the biggest complaint about Tailwind css which is that it makes markup harder to read and understand.
TailwindCSS emerged in the past few years as one of the most popular new developments in the web development sphere. When I set out to build a frontend system for WingTask I chose to use TailwindCSS for reasons that had little to do with utility classes, the most obvious feature of Tailwind, and instead chose it because it also provides a turnkey CSS design system that can be extended for a specific application with the end result being a complete customized design system. In this article I will discuss why I think this prebuilt design system concept is as much a reason to adopt Tailwind as utility classes are.
Less is more, Tailwind CSS provides a smaller set of CSS to pick from.
Hunting and pecking for the right amount of padding to apply to a div has become a tiresome chore of picking some arbitrary value like 1rem and then adjusting it. Supposing I use 1rem and then determine I need it be slightly bigger what does slightly bigger translate to in an rem increment?
1.1rem? 1.5rem? 2rem?
Before adopting TailwindCSS I knew I had to escape this type of decision making by creating my own design system. This meant that I would create my own spacing scales, typing scales, color palettes, and various other selections so that for any particular css decision I would choose from a predetermined list rather than arbitrary values. In this scenerio a class called “p-4” represents 1rem of padding, if I try that and it’s too small then I go to “p-5” which is 1.25rem of padding, and then onwards to “p-6” at 1.5rem if it still needs more.
Having an infinite world of possibilities seems like it would be empowering but instead the “The Paradox of Choice” kicks in and a feeling of overwhelm takes hold. Without constraints, design becomes agonizing because we have to apply too much thought just to do something seemingly simple as picking the right value for a margin. This quickly compounds into decision fatigue when the amount of decisions that a typical web page takes are agonized over and over again. The solution is simple because there is only one solution to consider: limit your CSS choices.
I base all my UI development on a design system that I’ve curated to meet the needs of the apps I build. I want my design system to act like how “Mis en place” functions for a chef in a kitchen, I want to have my tools and materials available to me in a convenient and known location so that I can reach for them without much thinking. Building a design system means having a fixed palette of colors to choose from, a type scale to use, a spacing scale that gives defined increments for padding, margin, width etc. Choosing from a fixed set of options that Tailwind provides out of the box instead of being paralyzed by an endless sea of decisions is for me the biggest benefit of both a design system and TailwindCSS.
Tailwind out of the box is a fully fledged design system that is ready to be extended to meet the particular needs of a project. For WingTask this meant choosing colors that would match the branding, picking fonts, and deciding if I was going to use rounded corners on buttons (I didn’t). I save time in not having to build out a spacing scale, or a type scale, or a color palette or any other of the most commmonly used CSS properties.
TailwindCSS makes markup difficult to read?
When I first saw TailwindCSS I thought it was madness to litter your markup with so many classes. I consider myself a practitioner of clean HTML and it seemed like switching to Tailwind meant vomitting CSS classes all over the document.
<div class="w-16 h-16 rounded text-white bg-black py-1 px-2 m-1 text-sm md:w-32 md:h-32 md:rounded-md md:text-base lg:w-48 lg:h-48 lg:rounded-lg lg:text-lg"> Yikes. </div>
I think if I was still taking a page based approach to UI building TailwindCSS would be a non-starter. The reason is that an entire page of TailwindCSS might make it difficult to see where certain parts of the page were delineated since utility classes don’t have any semantic meaning.
However, things change dramatically when a component based approach is used because now markup is only being worked on one component at a time starting from small atomic pieces and then combining them into larger blocks. Using a front-end style guide tool like Storybook further helps to keep components organized and separated so it’s clear what is what. Finally, encapsulating the components into HTML 5 web components makes it so that the clutter of utility classes is tucked away in an easy to use element.
In my markup the same div encapsulated as a custom element would look like this.
<tc-blacksquare>See this is okay.</tc-blacksquare>
Semantics is important, Tailwind forgoes It?
The argument against TailwindCSS from a semantics standpoint is that utility classes do nothing to communicate what a block of html actually is.
<ul class="divide-y divide-gray-200"> <li class="flex py-4"></li> <li class="flex py-4"></li> <li class="flex py-4"></li> </ul>
With the TailwindCSS classes it’s not likely you are intuitively going to pick up that this list is actually a menu with menu items. let’s see it again with semantic CSS classes:
<ul class="menu"> <li class="menu-item"></li> </ul>
The classes make it clear that this is a menu and so if TailwindCSS means forgoing semantic classes how are developers going to be able to identity discrete blocks of functionality buried in a soup of TailwindCSS classes? Here’s a hybrid approach.
<ul class="menu divide-y divide-gray-200"> <li class="menu-item flex py-4"></li> <li class="menu-item flex py-4"></li> <li class="menu-item flex py-4"></li> </ul>
I think this practical approach of combining TailwindCSS with a semantic class for identity is very much a workable solution and can fit in most cases, however I think web components makes the best possible solution.
The argument I’m making now goes beyond TailwindCSS, I think with the widespread support for web components there is less of a reason to use semantic classes because semantic elements trump semantic classes. Consider this common layout where semantic classes are being used.
<body> <div class="header"> </div> <div class="nav"> </div> <div class="main"> </div> <div class="aside"> </div> <div class="footer"> </div> </body>
This is markup that the fabled “Holy Grail” layout and even supporters of SemanticCSS classes wouldn’t advocate using this tag / class schema for doing layout and instead would go for an updated approach.
<body> <header></header> <nav></nav> <main></main> <aside></aside> <footer></footer> </body>
Semantic elements will always trump semantic classes because semantic elements describe its meaning to both the browser and the developer.
Building on this idea let’s replace TailwindCSS / Semantic CSS combination with a web component:
<wt-menu-item> <wt-menu-item></wt-menu-item> <wt-menu-item></wt-menu-item> <wt-menu-item></wt-menu-item> </wt-menu-item>
Here is a web component representation of the same menu. The markup element is “wt-menu-item” and not “menu-item” because web components must be namespaced, for this example I used an arbitrary “wt-“ for the namespacing.
For me the ability to use web components means being able to take advantage of the rapid development that TailwindCSS gives without having to give up on the clarity that Semantic CSS provides. The key to making TailwindCSS really shine is to use a component approach that is best enacted through web components.
TailwindCSS is rightfully becoming a crucial tool in the frontend workbox. By combining TailwindCSS with a frontend process that includes creating a design system, an isolated workspace with a frontend style guide, and encapsulated blocks of functionality in web components, we can take advantage of TailwindCSS in a way where it’s making development easier and faster and at the same time isn’t creating any bad side effects that need to be dealt with.
This is fourth in a series of articles describing how I build user interfaces for WingTask, others in this series are:
How I Build User Interfaces For WingTask
How I Sketch Interfaces on Pad and Paper
Storybook is an Essential Tool (previous)
Why Web Components Work For Me (next)