Explaining what a good abstraction layer is with Tailwind CSS
Table of contents:
Abstraction layers are very subjective based on what, when and how you want things. This post is based on my experience with Tailwind CSS 3. It is also about my what, when and how.
In computing, an abstraction layer or abstraction level is a way of hiding the working details of a subsystem.
Abstraction layers are supposed to be a more accessible version of a subsystem. Frameworks and libraries can be called abstraction layers because it is hiding the way a subsystem works. I like to be careful when it comes to abstraction layers. Abstraction layers that I have a say in choosing that is. A Linux distribution like Ubuntu is an example of an abstraction layer. It makes Linux a complete operating system. It is an abstraction layer for Linux, which is just a kernel. This makes computing convenient, functional and in some cases secure too.
With web development, I found Tailwind CSS to be a good abstraction layer. A good abstraction layer which helps me work with CSS in a better way. This post is about some expectations I have when looking for an abstraction layer. And how Tailwind CSS falls into place when it comes to these expectations.
What’s so special about Tailwind CSS?
Tailwind CSS is a utility-first CSS framework. This means instead of some CSS classes which are prebuilt components, you are given utility classes. Utility classes are smaller sets of CSS classes which can be used to make components. This gives you more flexibility in designing elements. Utility classes like flex
for display:flex;
, p-2
for padding: 0.5rem;
are just examples of utility classes provided by Tailwind CSS. Tailwind CSS works like a design API with cherry picked utility classes and predefined options. It is also built with customisation in mind providing flexibility. These just some reasons why Tailwind CSS is so special.
The utility classes can be new to many. I have seen people complaint about Tailwind CSS, but without trying it out. I highly recommend you to feel it out first before judging it from paper. Any cons you may find will be outweighed by pros.
The good parts
An abstraction layer won’t be able to make everyone happy. There is always a price to pay. Having said that, here are the good parts expected from an abstraction layer. Good parts which are present in Tailwind CSS as well.
An abstraction layer should make the subsystem easier to understand
An abstraction layer is suppose to make it easier for us to learn and understand the subsystem. It shouldn’t hide or make it difficult for us to understand how the subsystem works.
With Tailwind CSS, it doesn’t just provide you a bunch of utility classes. You still need to know some CSS. The difference is that you don’t need to be a CSS expert. If you know enough CSS and little about flexbox and grid layout, you can take full advantage of Tailwind CSS. Because when you are using Tailwind CSS, you are still thinking in CSS. As you write with Tailwind CSS, you will learn CSS too. And I have been learning and understanding CSS more thanks to Tailwind CSS.
Making 5 elements get displayed in row on mobile and column on larger screens are easy with Tailwind CSS. All you have to do is write the below with Tailwind CSS utility classes. You don’t have to deal with media queries or anything complex which you will have to if you are writing CSS.
<!-- With Tailwind CSS -->
<div class="grid grid-flow-row gap-1 p-1 lg:grid-flow-col">
<div>Div element 1</div>
<div>Div element 2</div>
<div>Div element 3</div>
<div>Div element 4</div>
<div>Div element 5</div>
</div>
Responsive elements with Tailwind CSS
To have a similar result with CSS, you have to write the below CSS code. This is excluding the HTML code by the way. It also needs you to know what media queries are. Tailwind CSS makes it easy to make responsive elements for different breakpoints. You save a lot of time and be more productive instead of having a battle with CSS.
/* With CSS */
.divClass {
display: grid;
grid-auto-flow: column;
gap: 1rem;
padding: 1rem;
}
@media (max-width: 1024px) {
.divClass {
display: grid;
grid-auto-flow: row;
padding: 1rem;
gap: 1rem;
}
}
Responsive elements with CSS
Knowledge transfer after choosing an abstraction layer
Knowledge transfer should be ideal when you are using an abstraction layer. You shouldn’t have to relearn everything from the scratch. The more knowledge you can carry forward while using an abstraction layer, the better.
The utility classes in Tailwind CSS are very similar to CSS properties with small differences. It feels like aliases to CSS properties. And once you get the hang of it, you can guess most of them. You can also reuse the methods and solutions you know about CSS from experience and from places like StackOverflow. As mentioned earlier, with Tailwind CSS you are still thinking in CSS. You just have to reimplement it all with utility classes. This is made easier with VSCode Tailwind CSS intellisense extension. And just like the Tailwind CSS website advertises, it actually will help you build modern websites rapidly.
An abstraction layer should improve the developer experience
Main reasons we go for an abstraction layer is for convenience, ease or both. So Developer experience (DX) is important. With Tailwind CSS, instead of writing HTML and CSS in different files, you unify that process with utility classes in your HTML. That is just one less file to switch back and forth with. Tailwind CSS also takes care of the browser compatibility issues. And those hair pulling nitty gritty details which makes you hate CSS. This makes you productive, as expected from a good abstraction layer.
Abstraction layers & performance
Abstraction layers could tank performance. ElectronJS is a good example of this. Typical response to this is that modern hardwares can handle it. Or that we can add more memory, storage or processing power. This is not ideal for many reasons. One of them being user experience (UX). I forgot the difference between ElectronJS and native desktop apps. My note application was ElectronJS and I use VSCodium for coding. Then there is Slack, Discord and many more which uses ElectronJS. So when I used LiteXL text editor, it just blew my mind away. But it reminded me how apps are supposed to work. Our choice of abstraction layers can make all the difference. And, performance in abstraction layers matter!
The good thing about CSS is that it has evolved a lot. We can do a lot of things with just CSS nowadays. Tailwind CSS helps you make the most out of this. Tailwind CSS bundles only CSS that you actually use. This makes the size of your stylesheet smaller. This means you don’t have to load unused CSS in your application anymore.
I was using Bulma CSS for my website earlier. My migration to Tailwind CSS reduced my stylesheet lines from 12,726 lines of CSS to 805 lines of CSS.
Lines of CSS for my website with Bulma CSS & Tailwind CSS
Not just the lines of code. My stylesheet’s file size reduced as well. From 247.1KB
to 13.0KB
. This made my website fast. I like the fact that my website loads the pages faster.
File size comparison for my website with Bulma CSS & Tailwind CSS
Designed for more than 80%
An abstraction layer should not only be appealing to the general audience (80%). It should also be designed for the power users and tinkerers (the 20%). A lot of abstraction layers we use falls short for power users and tinkerers. But Tailwind CSS might prove you wrong.
Tailwind CSS is designed to handle it’s limitations and edge cases very well. It already let’s you customise the values of the utility classes. But it also has:
- Arbitrary property feature: Which helps you use a CSS property which is not yet supported by Tailwind CSS. Just put any CSS property inside a square bracket like
[css property to use]
instead of a utility class name and voila! You just used a CSS property inside Tailwind CSS. In the below example, I am usingclip-path
CSS property as an arbitrary value.
<img class="[clip-path:circle(50%)]" src="https://geekculture.co/wp-content/uploads/2019/01/takeru-satoh-ruroni-kenshin.jpg" alt="An image to try out clip-path feature of CSS"/>
Demonstration of arbitrary property feature of Tailwind CSS. This feature helps you use any CSS property even if they are not supported by Tailwind CSS.
- Arbitrary value feature: lets you use custom CSS values within the utility classes. You can replace the utility class value with a square bracket like
[20rem]
. You are now using an arbitary value that is not part of the utility classes. You can use any CSS units like rem, em, px, vh, vw or %.
<div class="w-[80%]">Checking the width!</div>
Not only that, you can customise Tailwind CSS extensively. Since it is out of scope for this post, let me redirect you towards Tailwind CSS documentation.
The bad parts
Not everything is good at Tailwind CSS. So here is the other side to abstraction layers/Tailwind CSS.
There is always a price to pay
I think all abstractions have disadvantage(s). In other words, there is always a price to pay. It is upto us to see if those disadvantages are fine with respect to the goals we have.
Other side of utility classes in Tailwind CSS: Readability and maintainability
Utility classes are great. But with utility classes, readability and maintainability takes a hit as you add complexity. Which ends with a long list of utility classes. In the below example, I am customising the html tags associated with a markdown file. Customising the prose
class which comes with the official typography plugin of Tailwind CSS. It already provides basic styling for markdown elements like blockquote
and code
tags. But if you want to customise it further, the below happens. These situations are inevitable as you deep dive into Tailwind CSS. Most common complaints I know about Tailwind CSS stems from this.
<article class="text-lg prose-headings:font-semibold py-1
prose-sm prose-gray prose-a:underline prose-a:decoration-2 hover:prose-a:text-blue-600
prose-ul:list-disc prose-code:bg-gray-300 prose-code:p-1 prose-code:rounded
prose-pre:overflow-x-auto prose-pre:text-lg prose-code:prose-pre:bg-[#272822] prose-pre:rounded prose-pre:px-4 prose-pre:py-1
prose-blockquote:border-l-4 prose-blockquote:italic prose-blockquote:border-gray-600 prose-blockquote:bg-gray-200
prose-blockquote:px-4 prose-blockquote:py-2 prose-blockquote:rounded prose-img:mx-auto
prose-hr:p-0 prose-hr:m-0 prose-headings:my-0 prose-headings:py-2
">
I split the utility classes into separate lines based on similarities of functionalities/properties as shown in the above example. If the list of utility classes are too long, maybe we should start writing comments just like for code? I don’t know. To most people, the utility classes are new. I expect a best practice to emerge sooner or later. If you know one already, do let me know.
Another solution is to keep your HTML markup clean. You can do this by mapping the utility classes to a CSS class name. And this can be added to Tailwind’s components layer by adding it like this in your main.css. You can then call the class name from HTML just like the other utility classes. But you shouldn’t go overboard with adding custom css like this. This should be used as a last resort as it is not a recommended method.
/* main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.styleMarkdown {
@apply text-lg prose-headings:font-semibold py-1
prose-sm prose-gray prose-a:underline prose-a:decoration-2 hover:prose-a:text-blue-600
prose-ul:list-disc prose-code:bg-gray-300 prose-code:p-1 prose-code:rounded
prose-pre:overflow-x-auto prose-pre:text-lg prose-code:prose-pre:bg-[#277322] prose-pre:rounded prose-pre:px-4 prose-pre:py-1
prose-blockquote:border-l-4 prose-blockquote:italic prose-blockquote:border-gray-600 prose-blockquote:bg-gray-200
prose-blockquote:px-4 prose-blockquote:py-2 prose-blockquote:rounded prose-img:mx-auto
prose-hr:p-0 prose-hr:m-0 prose-headings:my-0 prose-headings:py-2
}
}
In conclusion
Anybody who is going to use CSS regularly would appreaciate Tailwind CSS. I still think there is market of component based CSS frameworks. I am a fan of Bulma CSS’s aesthetics. I am also sure that there maybe places where Tailwind CSS can improve. This is why I used a “good” abstraction layer instead of a perfect abstraction layer. But it is pretty much there. It really have changed how I write CSS and how I learn CSS.
Resources / links
- What’s new in Tailwind CSS v3 from Tailwind Labs - https://youtu.be/mSC6GwizOag