Explaining what a good abstraction layer is with Tailwind CSS

Explaining what a good abstraction layer is with Tailwind CSS

·

10 min read

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>

Example of responsive elements with Tailwind CSS

To have a similar result with CSS, you have to write the below CSS code. Which 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;
    }
}

Example of 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 hardware 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 style-sheet 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 style-sheet lines from 12,726 lines of CSS to 805 lines of CSS.

Image of the lines of code for Bulma CSS which was 12,726 lines of CSS. And with Tailwind CSS it actually uses only 805 lines of 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.

Image of the file size of the CSS style-sheet. With Bulma CSS it is 247.1KB because of unused CSS. With Tailwind CSS it is just 13KB

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 using clip-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. The image used is of Rurouni Kenshin character played Takeru Satoh in the movie Rurouni Kenshin

  • 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 arbitrary 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 up to 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.


This post was first published at unsungnovelty.org with the title "Explaining what a good abstraction layer is with Tailwind CSS".