Using TailwindCSS with CSS-in-JS

Andrew Del Prete
andrewdelprete
Published in
4 min readNov 27, 2017

Update 2— Dec 3, 2017

After a bit of exploration, it looks like babel-plugin-tailwind does indeed work with Vue! The problem stemmed from how Glamor injects styles. Emotion and CSX seem to work fine!

Obviously this isn’t the native <style></style> syntax that comes with Vue, but it’s an option if you want to pull in Tailwind in via CSS-in-JS.

Template:

JSX:

Update — Dec 1, 2017

In my post, I mentioned babel-plugin-tailwind doesn’t work with Emotion or Styled Components yet. However, it looks like Emotion has the ability to use JavaScript objects and works fine with the plugin.

I’ve added an example of how to do this in the repo using react-emotion.

https://github.com/andrewdelprete/babel-plugin-tailwind/blob/master/example/src/App.js#L20

At the beginning of 2017 I put together a small list of technologies I wanted to explore and grow in. Topics like Performance, PWAs, Vue, AdonisJS, Webpack, CSS-in-JS, and many more.

JavaScript is never ending.

One of the items on my list is to write a Babel plugin. I didn’t have any ideas during the year but it dawned on me when writing my previous post Using PurifyCSS to Remove Unused TailwindCSS Classes that I should try to convert TailwindCSS to something we can leverage with a CSS-in-JS library such as Glamorous.

If you haven’t heard some of the benefits of CSS-in-JS checkout this amazing writeup by Mark Dalgleish:

https://medium.com/seek-blog/a-unified-styling-language-d0c208de2660

Earlier this year…

In early 2017, I created an Atomic CSS-in-JS utility library based on BeardCSS called NeckbeardJS.

Neckbeard was my first experiment with trying to merge Atomic CSS and CSS-in-JS. I actually spent a lot of time on it upfront but it doesn’t get much of my attention anymore.

Using React it would look something like this:

The solution was pretty elegant IMO, but the approach I took with Neckbeard had the downside of a very heavy runtime. The library generates an object of around 10,000 selector properties on page load and has a function to reference each selector.

The problem is that this object has to be created by the browser and stored in memory to be referenced by the nb(classes) function. This defeats one of the many benefits of CSS-in-JS, which is to only load the styles used on the page.

Just like the performance hit of having 10,000 unused CSS selectors, I was running into the same issue with Neckbeard, but in JavaScript instead.

It needed to be better if it was going to be usable so instead of trying to rewrite Neckbeard, I decided to take a different approach with TailwindCSS instead.

Introducing babel-plugin-tailwind

After digging through the babel-handbook and reviewing lots of awesome examples, I finally felt like I could grasp Babel and ASTs enough to convert a string of TailwindCSS classes to a corresponding style object at build time. In other words, we wouldn’t make the browser generate all the necessary styles properties, but we would do it upfront at build time with a Babel plugin.

https://github.com/andrewdelprete/babel-plugin-tailwind

Example using Glamor 💅

Before build

After build

The babel plugin will look for the function tw(classes), reference the corresponding TailwindCSS style properties, and replace the whole expression with an object to serve to the user. In this example I use Glamor but you could easily use Glamorous or other CSS-in-JS library that utilizes the same object syntax for styles.

Example using Glamorous 💄

Before build

After build

Pretty nifty right? I felt like a JavaScript demigod once I figured out how ASTs work. This approach allows us to leverage the utility class power TailwindCSS provides but only serve the user what is being used on the page. It also allows us to use these great component based tools like Glamorous for making our code more explicit.

Here’s a slightly more complicated component I borrowed from the TailwindCSS docs and built using Glamorous and babel-plugin-tailwind.

Example: https://dist-zzostutvon.now.sh/
Code: https://github.com/andrewdelprete/babel-plugin-tailwind/blob/master/example/src/App.js

Installation

I provided some brief instructions on how to get started with the babel-plugin-tailwind over at https://github.com/andrewdelprete/babel-plugin-tailwind. There are a few undocumented features I still need to write about and will be sure to let everyone know once they’re fleshed out.

Contributions

There are a few technical hurdles to figure out:

The babel plugin converts a string of TailwindCSS classes to an object. However, libraries like *emotion (See update above) and styled components require a string of traditional CSS properties to work properly.

I also haven’t figured out how to make this work with Vue yet. Vue has it’s own way of doing CSS-in-JS which I’m not sure if it can be leveraged or not.

Server-side Rendering is another item on the list. I believe this shouldn’t be too difficult as long as the source is ran through babel first.

If you’d like to contribute to the project, please say hello and open a PR!

Closing thoughts

I believe the ability to inject Tailwind’s predefined classes in CSS-in-JS can be a huge win. Having consistent font sizes, grid widths, spacing, colors, and breakpoints can help make your codebase more uniformed. Plus, the added bonus of these classes being replaced with an object at build time makes things nice and snappy for the end-user.

Shameless Plug

I’ve recently recorded a few video series covering JavaScript Promises and Async / Await. Please feel free to check them out at www.fivemindev.com! There’s a 50% off sale until the end of November, 2017.

Also, If you’re interested in learning more about some easy performance wins, I’ve written a few articles on www.learnperf.com.

Thanks for reading!
-Andrew Del Prete

--

--