9 Things that Tailwind CSS does to make your life easier

Sam Loyd
14 min readJun 12, 2020

Had I known that it was all the way back in 2017 that I was first made aware of Tailwind CSS (see the photo below), I probably would have spent the last few years in a more significant state of contentment when it came to styling both my clients’ projects and my own. Unfortunately, it didn’t register anything for me back then, and I never checked it out.

A photo I nabbed from a talk back in 2017.

If only I had known, well things would have been a lot different. I think most people cut their teeth on Bootstrap as a CSS framework (I have a deep dive about CSS frameworks and the concepts behind them in the works at the moment — stay tuned for that absolute banger). It does an excellent job of what it was designed to do, mainly increase the speed at which you can build interfaces and websites.

However, as you progress in your development career, the drawbacks start to pile up — significantly low levels of customisation, standardised components, staying within the bootstrap ecosystem, hard to modify for different frontend frameworks. But it is fast. You can get a project up and running very fast.

So this brings me to the first thing I love about TailwindCSS.

1. It is hands on

TailwindCSS is decoupled from the components you build with it. What do I mean by this? Well, instead of having classes like btn or navbar navbar-dark navbar-sticky you have an incredible collection of utility classes.

These utility classes give you the freedom to build your own components — yes, I know that you’ve been able to do that since the invention of HTML and CSS, but this is quick. Like ridiculously quick. You can create responsive, multi-browser components quicker than your designers can mock them up.

The reason you can do this is that the framework is incredibly intuitive. Once you grasp a couple of key concepts, you can quickly prototype anything, and it is only a few more hours with it before you can build production-ready components. While they have examples of components you can build in the documentation, take this card example.

(Sourced from TailwindCSS ).
<div class="max-w-sm rounded overflow-hidden shadow-lg">
<img class="w-full" src="/img/card-top.jpg" alt="Sunset in the mountains">
<div class="px-6 py-4">
<div class="font-bold text-xl mb-2">The Coldest Sunset</div>
<p class="text-gray-700 text-base">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus quia, nulla! Maiores et perferendis eaque, exercitationem praesentium nihil.
</p>
</div>
<div class="px-6 py-4">
<span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2">#photography</span>
<span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2">#travel</span>
<span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700">#winter</span>
</div>
</div>

A well-designed card created easily and quickly using simple classes. You can read this even if you haven’t ever seen Tailwind before and this brings me to my next point…

2. Everything is so deliciously well-named

Naming things is hard, most developers will tell you that. Naming conventions are more than just pub discussions they are the bread and butter of a well-structured application. They need to balance its deduce-ability (people being able to have a damn good guess at what it is or should be called based on convention) and intuitiveness (understanding what it does).

The best examples of deduce-ability are perhaps some of the most common and developer agreed-upon naming conventions in CSS. I’m talking of course about mx-auto. That little beauty is consistent across multiple different CSS frameworks, and thus, most developers intuitively know to reach for it.

Utility-based CSS frameworks are not new, but well-named ones are a dime a dozen. Tachyons, for example, emphasizes keeping your class lines as short as possible, so all of its classes are tiny. They end up like this — fl-ns. Now it may just be me, but that is not particularly easy to read. Whereas using Tailwind, the same classes are written as md:float-left - which frankly is so much easier to read.

There are several commonly adhered to conventions in Tailwind. But you can just understand it, even if you’ve never used it before. The learning curve is so shallow because the base concepts are elegant. As a result, custom, high-end user experiences can be built quickly and effectively without having to mess around modifying framework CSS files or overriding styles.

3. Responsiveness and Pseudo-classes

Like most modern frameworks, Tailwind is mobile-first. This means adding the class text-lg to an element or component will apply it to all screen sizes. If we wanted the text size to only increase on larger screens, for example, we would write something like this: text-md lg:text-lg and boom we have responsive CSS right off the bat.

To use a utility class and have it only take effect at a specific breakpoint, all you need to do is prefix the utility with the breakpoint name, followed by the : character: Every utility class can be modified by responsive prefixes (lg: etc). Every single one.

To reiterate, the critical thing to note about Tailwind is that all classes apply themselves upwards. This means that responsive modifiers apply to that breakpoint and all larger breakpoints. Therefore, md:height-screen will be applied to the large and extra-large breakpoints unless classes are added separately for them.

An everyday use might be changing the size of an image depending on screen size.

// Width of 16 by default, 32 on medium screens, and 48 on large screens
<img class="w-16 md:w-32 lg:w-48" src="...">

What are the break point names?

ut here is another great thing, they can be whatever you want. For example, if you want to rename the prefixes to tablet tablet:text-lg, laptop:h-64, desktop:flex you can... and easily (see point 5).

4. All the colours at your finger tips

Sometimes when I am building things, nothing throws me off more than having to come up with a colour palette off the top of my head. Even when I am designing in Sketch or Photoshop, getting good combinations of colours is just not something I am good at. I know there are tools that you can use to create charming looking apps, but for me, I just find they slow down the process and never quite look right anyway.

Thankfully, for a large part, Tailwind has ushered in a solution with its default colour palette. The broad spectrum of colours (black, white, gray, red, orange, yellow, green, teal, blue, indigo, purple, pink) and their 9 different shades gives you 902 colours (black and white don’t have shades) to play without of the box. Add in the new ability in Tailwind 1.4 to add opacity modifiers to these colours, and you’re looking at 2706 different colour variations.

Now you might be thinking that is way too many colours and you’d never use anywhere near all of them. And you’re right — I don’t and won’t. But as we’ve already seen Tailwind makes prototyping so relaxed by virtue of its utility class syntax. If you wanted to make the background a shade of red, for example, you just add bg-red-400 as the class, and you're off the races. But maybe I didn't mean red and actually pink would be better bg-pink-400. Don't like the shade? Go up in increments of a hundred bg-pink-500 to go darker and down a hundred bg-pink-300 to go lighter.

Check out all the colours here .

Simple things like this make creating good looking apps quick and effective. It lets you test out ideas on the fly and most importantly, they are a beautifully designed palette of colours by someone that knows what they are doing — that means people like me get to enjoy their creative genius and wield in our own ways.

5. Customise everything

The power of tailwind.config.js should not be understated. Because Tailwind was designed from the ground up to be customisable, you can easily and quickly tweak values to fit your desired preferences. There are four main things you can do in the config file. Just as a note all files references below are examples of different tailwind.config.js. Firstly, you can just plain-old override things. Simple, don't like the .25rem spacing options? Override them.

module.exports = {
theme: {
spacing: {
'1': '8px',
'2': '12px',
'3': '16px',
'4': '24px', // these will replace the p-4 etc
'5': '32px',
'6': '48px',
}
}
}

Secondly, you can extend things. I often find myself extending the box-shadow effect to make them more dramatic for darker background, adding to spacing, or configuring the direct transition properties. You can extend any utility class like so.

module.exports = {
theme: {
extend: {
boxShadow: {
'3xl': '0 35px 60px -15px rgba(0, 0, 0, .4)'
},
spacing: {
'72': '18rem',
'80': '20rem',
'88': '22rem',
'96': '24rem',
'104': '26rem',
},
transitionProperty: {
'height': 'height',
'dimensions': 'margin, padding, height',
}
}
}

Thirdly, you can bring in plugins. Want to write a plugin that allows you to generate gradient animated backgrounds on the fly and use them in an as efficient manner as the rest of Tailwind? Now writing plugins is beyond the scope of this article, and in all honesty, I haven’t had to need to do it yet. But the documentation doesn’t make it look like rocket science. If you had written one (or got one from GitHub), the example below from their theme configuration documentation shows you how to implement it.

module.exports = {
theme: {
gradients: theme => ({
'blue-green': [theme('colors.blue.500'), theme('colors.green.500')],
'purple-blue': [theme('colors.purple.500'), theme('colors.blue.500')],
})
},
plugins: [
require('./plugins/gradients')
],
}

Lastly, variants. “What be’ith these variants you speak off?” I hear you say. In Tailwind, variants refer to pseudo-classes. That would be all the prefixes followed by a colon. For example md:w-1/2, hover:bg-gray-800, focus:shadow-xl etc. Now some of these are configured in the default theme , we know from point 3. Responsiveness and Pseudo-classes that all the utility classes are generated with their responsiveness class variants, and things like the background colour etc. all have the hover: functionality enabled.

The trick comes when you want to enable more advance variants, such as first-child, last-child, even, odd, disabled, group-focus, group-hover, focus-within, active and visited.

module.exports = {
variants: {
// make sure you provide the full list of variants as they overide
// rather than merge with the defaults.
// Variants are generated in order that they are added
// so make sure put those of greatest precendence at the end
backgroundColor: ['responsive', 'hover', 'focus', 'active'],
},
}
// Another option if you are playing around, experimenting
// and you have it setup to reduce file size at production time
// is just enabling all variants as documented below
module.exports = {
variants: ['responsive', 'group-hover', 'focus-within', 'first', 'last', 'odd', 'even', 'hover', 'focus', 'active', 'visited', 'disabled']
}

6. Use it in conjunction with existing frameworks

Let’s say you like the look of Tailwind, you’ve been convinced by some articles on the web, maybe even a little by this one. You decide that you want to use it. But you haven’t started any new projects and don’t plan to. Your work is set in their ways and have their own framework they’ve spent 15 years building and so is incredibly reliable and ALWAYS, I mean ALWAYS produces the intended result.

You could just install Tailwind and require the CSS after all the other CSS, thereby overriding the current incumbent… But this is probably going to cause significant breaking changes and might get you the sack if you manage to skate your way to accidentally deploying it.

Luckily, for all of us, due to the customizability of Tailwind, we can go in and prefix all the classes and CSS that it generates. This allows us to guarantee that there will be no class name overlap. Of course element and #ID CSS selectors will not be overridden unless you take the option above. But I think this is good enough to get the foot in the door and let your colleagues see how much easier life is developing with Tailwind. Who knows… maybe the process of refactoring UI’s with Tailwind will result in a better looking, more modern feeling UI. shrug emoji, you know.

// tailwind.config.js
module.exports = {
// add the prefix here, could be anything required to make it unique
prefix: 'what-a-great-article-on-tailwind-',
// If you toggle this on (false by default)
// you can also make all classes have the !important modifier
important: true,
// or more usefully you can set a selector this protects
// other libraries that you might import or legacy code
// all decendents of the selector will be marked !important
// important: ".tailwind-component",
}
// Would generate classes like this
// .what-a-great-article-on-tailwind-text-left {
// text-align: left !important;
// }

7. Development bundle huge, production bundle small

When you are developing, you want access to all the classes so you can experiment and play easily without having to worry about bringing in new classes or style sheets. But in the back of your head is always the worry that your site is getting bloated, every library you add, increases the load time for your users.

Before maybe you used smaller minimalistic frameworks like skeleton that just provide basic grid-style CSS and a few responsive classes. Then you’d write your own minimal CSS files, pipe them through a post-processor and call it a day. And that would work, but it is not the most pleasant developer experience is it?

Through PostCSS Tailwind was able to incorporate powerful extensions such as autoprefixer and cssnano . These would improve TailwindCSS across more types of browsers and reduce the production bundle size significantly. (These are the only ones I regularly use there is probably far more).

The real magic lies in the ability to shrink its production bundle down by over 90% through purging all the unused classes. Initially, this was done through things like PurgeCSS and using webpack, grunt or whatever post-processing tool you favour to complete the purge. But since version 1.4 Tailwind could do this itself, scanning all your the files of the paths you designate and removing all the classes that haven’t been used.

You can do this by adding the purge option to your tailwind.config.

// tailwind.config.js
module.exports = {
// include the paths to your files you want purge
purge: [
'./src/**/*.html',
'./src/**/*.vue',
'./src/**/*.jsx',
],
theme: {},
variants: {},
plugins: [],
}

Hot tip: Dynamic classes

Hot tip on this front: if you’re dynamically constructing classes — something like this in React perhaps.

// theme might be 'dark' or 'light' in this situation
const Header ({ theme, children, ...restProps }) => (
<h2 className={`text-${theme} text-lg` {...restProps}>{children}</h2>
)

Then Tailwind and PostCSS will both remove the text-dark or text-light classes respectively. It will not execute javascript during its scanning of the files or add in defaults if you're using them. You will need to do something like this instead.

// theme is again 'dark' or 'light' in this situation
const Header ({ theme, children, ...restProps }) => (
<h2 className={`${theme === "dark" ? "text-dark" : "text-light"} text-lg` {...restProps}>{children}</h2>
)

Just something that might make you scratch your head if your production site doesn’t look the same as your development.

8. You can create component classes, if you want

At some point, if your project gets large enough or perhaps the stack you’ve chosen isn’t particularly reusable component friendly you might want to create component classes. I can understand that this might also be seen as the middle ground for some people, allowing the experimentation and creativity of designing and building components before solidifying the prototypes into reusable CSS selectors.

When you do this you are building a component-based CSS framework on top of the flexibility of the Tailwind, and that gives you the best of both worlds. If that is what you need. Currently, my stack of choice involves using React. Hence, most of the time, my components are contained within their own files, and I re-use components by importing those files (React components are all classes and functions) into whatever layout I need.

Here is an excellent example from the Tailwind docs, creating a .btn component.

.btn {
@apply font-bold py-2 px-4 rounded; // all in on line
}
.btn-blue {
@apply bg-blue-500; // seperate lines
@apply text-white;
}
.btn-blue:hover {
@apply bg-blue-700;
transform: translateY(-1px); // can still apply normal css for fine tuning
}

9. Reference the configuration values

I actually only found out about this one when I was checking the documentation to write this article. Just goes to show how many little gems might always remain hidden in the literature. How often features that would make your life easier exist but are never discovered.

Anyway, this feature allows you to reference the values stored in your tailwind.config.js file. Why would you want to do that? Well if you wanted to keep things ultra configurable and governed by a single config document you could use these values to apply to inline styles.

If the class-based system can’t be used for a specific task and you need to use inline-styles, then you can access them, and retain consistency across all areas of your project. This might also be the thing that lets you remove other CSS files (bar Tailwind utilities, obviously) and take an element or #id selectors the method of choice for customisation.

Let’s look at an example use case.

import React, {useState, useEffect } from 'react';// Accessing the tailwind config values
import resolveConfig from 'tailwindcss/resolveConfig'
import tailwindConfig from './tailwind.config.js'
// Images
import smallImage from "../images/small.jpg"
import mediumImage from "../images/medium.jpg"
import largeImage from "../images/large.jpg"
import extraLargeImage from "../images/extra-large.jpg"
// This returns a modified tailwindConfig with any changes you've made
const fullConfig = resolveConfig(tailwindConfig)
// Destructing screen sizes
const { sm, md, lg, xl } = fullConfig.theme.screens
// Simple functional component that returns a different image
// based on the size of the screen
const ResponsiveImage = (props) => {
const [currentSrc, setCurrentSrc] = useState(smallImage); useEffect(()=>{
const screenWidth = window.innerWidth
if (screenWidth > md) return setCurrentSrc(mediumImage)
if (screenWidth > lg) return setCurrentSrc(largeImage)
if (screenWidth > xl) return setCurrentSrc(extraLargeImage)
},[])
return <img src={currentSrc} {...props} />;
}

So there you have it, the 9 reasons I love TailwindCSS. Quite the framework that embraces its customizability but doesn’t let it get in the way of incredible developer experience. I’ll definitely continue to be using it on nearly all my projects and recommending its use to clients and colleagues as well. Until of course, the next big thing comes along… of course, that is highly likely just to be TailwindCSS V2.0 considering how much @adamwathan is working.

This article was posted on my blog a month ago. If you want up to date articles like this. Feel free to check it out.

--

--

Sam Loyd

Entrepreneur. Developer. Designer. And occassionally interesting. Find out more at samloyd.io